From c740e7ffa1d3769e589157306f34a2f74c2ea9e6 Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Tue, 17 Oct 2023 12:48:29 -0500 Subject: [PATCH 01/65] first success --- .github/workflows/ci.yml | 116 ++- .github/workflows/clean.yml | 59 ++ .github/workflows/publish.yml | 27 - .gitignore | 9 +- .scalafmt.conf | 23 +- build.sbt | 104 +- .../dynamodb/DynamoFlavor.scala | 139 --- .../scala/co.blocke.scalajack/TestUtil.scala | 42 - .../co.blocke.scalajack/dynamodb/Basics.scala | 145 --- .../dynamodb/CreateTableRequest.scala | 41 - .../dynamodb/JsonDiff.scala | 58 -- .../dynamodb/JsonMatcher.scala | 21 - .../co.blocke.scalajack/dynamodb/Model.scala | 110 --- .../co.blocke.scalajack/dynamodb/Plain.scala | 72 -- .../co.blocke.scalajack/dynamodb/README.md | 10 - .../mongo/BsonBuilder.scala | 22 - .../mongo/BsonParser.scala | 216 ----- .../mongo/Converters.scala | 37 - .../mongo/MongoFlavor.scala | 79 -- .../mongo/MongoWriter.scala | 169 ---- .../typeadapter/ObjectIdTypeAdapter.scala | 28 - .../OffsetDateTimeTypeAdapter.scala | 35 - .../typeadapter/StringWrapTypeAdapter.scala | 69 -- .../ZonedDateTimeTypeAdapter.scala | 38 - .../scala/co.blocke.scalajack/JsonDiff.scala | 58 -- .../co.blocke.scalajack/JsonMatcher.scala | 21 - .../scala/co.blocke.scalajack/TestUtil.scala | 42 - .../co.blocke.scalajack/mongo/AnySpec.scala | 209 ---- .../mongo/ConverterSpec.scala | 134 --- .../co.blocke.scalajack/mongo/Custom.scala | 159 ---- .../mongo/LooseChange.scala | 376 -------- .../co.blocke.scalajack/mongo/MapKeys.scala | 96 -- .../co.blocke.scalajack/mongo/Model.scala | 272 ------ .../co.blocke.scalajack/mongo/MongoSpec.scala | 891 ------------------ .../mongo/TryAndCapture.scala | 57 -- .../co.blocke.scalajack/mongo/TupleSpec.scala | 81 -- project/build.properties | 2 +- project/metals.sbt | 6 + project/plugins.sbt | 1 + .../scala/co.blocke.scalajack/Codec.scala | 21 + .../co.blocke.scalajack/json/JsonWriter.scala | 76 ++ .../scala/co.blocke.scalajack/run/Play.scala | 34 + .../co.blocke.scalajack/run/Sample.scala | 3 + .../main/java/co/blocke/scalajack/Change.java | 0 .../java/co/blocke/scalajack/Collection.java | 0 .../main/java/co/blocke/scalajack/DBKey.java | 0 .../main/java/co/blocke/scalajack/Ignore.java | 0 .../java/co/blocke/scalajack/JavaStuff.java | 0 .../java/co/blocke/scalajack/Optional.java | 0 .../co.blocke.scalajack/Converters.scala | 0 .../scala/co.blocke.scalajack/Main.scalax | 0 .../co.blocke.scalajack/Performance.scalax | 0 .../scala/co.blocke.scalajack/SJCapture.scala | 0 .../scala/co.blocke.scalajack/ScalaJack.scala | 0 .../DelimitedEitherTypeAdapterFactory.scala | 0 .../delimited/DelimitedFlavor.scala | 0 .../DelimitedOptionTypeAdapterFactory.scala | 0 .../delimited/DelimitedParser.scala | 0 .../delimited/DelimitedWriter.scala | 0 .../co.blocke.scalajack/json/JsonFlavor.scala | 0 .../co.blocke.scalajack/json/JsonParser.scala | 0 .../co.blocke.scalajack/json/JsonWriter.scala | 0 .../json4s/JValueBuilder.scala | 0 .../json4s/Json4sFlavor.scala | 0 .../json4s/Json4sParser.scala | 0 .../json4s/Json4sWriter.scala | 0 .../json4s/StringWrapTypeAdapter.scala | 0 .../model/ClassFieldMember.scala | 0 .../model/HintValueModifier.scala | 0 .../model/JackFlavor.scala | 0 .../model/LazyTypeAdapter.scala | 0 .../co.blocke.scalajack/model/Parser.scala | 0 .../model/StringBuilder.scala | 0 .../model/TypeAdapaterCache.scala | 0 .../model/TypeAdapter.scala | 0 .../model/TypeAdapterFactory.scala | 0 .../model/ViewSplice.scala | 0 .../co.blocke.scalajack/model/Writer.scala | 0 .../typeadapter/AnyTypeAdapter.scala | 0 .../typeadapter/ArrayTypeAdatper.scala | 0 .../typeadapter/CollectionTypeAdapter.scala | 0 .../typeadapter/EitherTypeAdapter.scala | 0 .../typeadapter/EnumTypeAdapter.scala | 0 .../typeadapter/FallbackTypeAdapter.scala | 0 .../typeadapter/IntersectionTypeAdapter.scala | 0 .../typeadapter/JavaPrimitives.scala | 0 .../MaybeStringWrapTypeAadapter.scala | 0 .../typeadapter/OptionTypeAdapter.scala | 0 .../PermissiveJavaPrimitives.scala | 0 .../PermissiveScalaPrimitives.scala | 0 .../typeadapter/ScalaPrimitives.scala | 0 .../typeadapter/SealedTraitTypeAdapter.scala | 0 .../typeadapter/StringWrapTypeAdapter.scala | 0 .../typeadapter/TimePrimitives.scala | 0 .../typeadapter/TraitTypeAdapter.scala | 0 .../typeadapter/TryTypeAdapter.scala | 0 .../typeadapter/TupleTypeAdapter.scala | 0 .../typeadapter/UUIDTypeAdapter.scala | 0 .../typeadapter/UnionTypeAdapter.scala | 0 .../typeadapter/ValueClassTypeAdapter.scala | 0 .../classes/CaseClassTypeAdapter.scala | 0 .../classes/ClassTypeAdapterBase.scala | 0 .../classes/JavaClassTypeAdapter.scala | 0 .../classes/NonCaseClassTypeAdapter.scala | 0 .../classes/ScalaClassTypeAdapter.scala | 0 .../ScalaClassTypeAdapterFactory.scala | 0 .../collection/JavaMapLikeTypeAdapter.scala | 0 .../collection/JavaSeqLikeTypeAdapter.scala | 0 .../collection/JavaStackTypeAdapter.scala | 0 .../collection/MapLikeTypeAdapter.scala | 0 .../collection/SeqLikeTypeAdapter.scala | 0 .../util/BijectiveFunction.scala | 0 .../co.blocke.scalajack/util/FixFloat.scala | 0 .../yaml/YamlBuilder.scala | 0 .../co.blocke.scalajack/yaml/YamlFlavor.scala | 0 .../co.blocke.scalajack/yaml/YamlParser.scala | 0 .../co.blocke.scalajack/yaml/YamlWriter.scala | 0 .../java/co/blocke/scalajack/JavaArray.java | 0 .../java/co/blocke/scalajack/JavaCap.java | 0 .../java/co/blocke/scalajack/JavaEnum.java | 0 .../co/blocke/scalajack/JavaSimpleBase.java | 0 .../co/blocke/scalajack/JavaSimpleChild.java | 0 .../test/java/co/blocke/scalajack/Maybe.java | 0 .../test/java/co/blocke/scalajack/Maybe2.java | 0 .../java/co/blocke/scalajack/OnSetter.java | 0 .../java/co/blocke/scalajack/Temperature.java | 0 .../java/co/blocke/scalajack/Unsupported.java | 0 .../co.blocke.scalajack/ConverterSpec.scala | 0 .../scala/co.blocke.scalajack/JsonDiff.scala | 0 .../co.blocke.scalajack/JsonMatcher.scala | 0 .../scala/co.blocke.scalajack/TestUtil.scala | 0 .../delimited/DelimitedSpec.scala | 0 .../co.blocke.scalajack/delimited/Model.scala | 0 .../json/collections/AnyCollections.scala | 0 .../json/collections/Arrays.scala | 0 .../json/collections/Maps.scala | 0 .../json/collections/Model.scala | 0 .../json/collections/Options.scala | 0 .../json/collections/Seqs.scala | 0 .../json/collections/Tuples.scala | 0 .../json/custom/CustomAdapter.scala | 0 .../json/custom/CustomTypeHints.scala | 0 .../json/custom/Model.scala | 0 .../json/custom/ParseOrElse.scala | 0 .../json/mapkeys/ClassPrimKeys.scala | 0 .../json/mapkeys/JavaPrimKeys.scala | 0 .../json/mapkeys/ListCollKeys.scala | 0 .../json/mapkeys/MapCollKeys.scala | 0 .../json/mapkeys/Model.scala | 0 .../json/mapkeys/ScalaPrimKeys.scala | 0 .../json/mapkeys/TupleCollKeys.scala | 0 .../json/mapkeys/ValueClassKeys.scala | 0 .../json/parameters/ClassParams.scala | 0 .../json/parameters/Model.scala | 0 .../json/parameters/TraitParams.scala | 0 .../json/plainclass/Inheritance.scala | 0 .../json/plainclass/Misc.scala | 0 .../json/plainclass/Model.scala | 0 .../json/plainclass/TryAndCapture.scala | 0 .../json/plainclass/ValueClass.scala | 0 .../json/primitives/AnyPrim.scala | 0 .../json/primitives/Enums.scala | 0 .../json/primitives/JavaPrim.scala | 0 .../json/primitives/Model.scala | 0 .../primitives/PermissivePrimitives.scala | 0 .../json/primitives/ScalaPrim.scala | 0 .../json/primitives/TimePrim.scala | 0 .../json/primitives/ValueClassPrim.scala | 0 .../json/structures/Eithers.scala | 0 .../json/structures/Model.scala | 0 .../json/structures/SealedTraits.scala | 0 .../json/structures/TryAndCapture.scala | 0 .../json/structures/TypeMembers.scala | 0 .../structures/UnionsAndIntersections.scala | 0 .../co.blocke.scalajack/json4s/AnyColl.scala | 0 .../co.blocke.scalajack/json4s/Custom.scala | 0 .../json4s/Json4sSpec.scala | 0 .../co.blocke.scalajack/json4s/Model.scala | 0 .../co.blocke.scalajack/json4s/Parsing.scala | 0 .../json4s/ScalaPrim.scala | 0 .../yaml/collections/AnyColl.scala | 0 .../yaml/mapkeys/ClassPrimKeys.scala | 0 .../yaml/mapkeys/JavaPrimKeys.scala | 0 .../yaml/mapkeys/ListCollKeys.scala | 0 .../yaml/mapkeys/MapCollKeys.scala | 0 .../yaml/mapkeys/Model.scala | 0 .../yaml/mapkeys/ScalaPrimKeys.scala | 0 .../yaml/mapkeys/TupleCollKeys.scala | 0 .../yaml/mapkeys/ValueClassKeys.scala | 0 .../yaml/misc/ReadWriterSpec.scala | 0 .../yaml/misc/TypeMembers.scala | 0 .../yaml/misc/YamlFlavorSpec.scala | 0 .../yaml/parameters/ClassParams.scala | 0 .../yaml/parameters/Model.scala | 0 .../yaml/parameters/TraitParams.scala | 0 .../yaml/primitives.plain/Misc.scala | 0 .../yaml/primitives.plain/Model.scala | 0 .../yaml/primitives.plain/TryAndCapture.scala | 0 .../primitives.plain/ValueClassPrim.scala | 0 .../yaml/primitives/AnyPrim.scala | 0 .../yaml/primitives/JavaPrim.scala | 0 .../yaml/primitives/Model.scala | 0 .../yaml/primitives/ScalaPrim.scala | 0 .../yaml/primitives/TimePrim.scala | 0 .../yaml/primitives/ValueClassPrim.scala | 0 205 files changed, 376 insertions(+), 3832 deletions(-) create mode 100644 .github/workflows/clean.yml delete mode 100644 .github/workflows/publish.yml delete mode 100644 dynamodb/src/main/scala/co.block.scalajack/dynamodb/DynamoFlavor.scala delete mode 100644 dynamodb/src/test/scala/co.blocke.scalajack/TestUtil.scala delete mode 100644 dynamodb/src/test/scala/co.blocke.scalajack/dynamodb/Basics.scala delete mode 100644 dynamodb/src/test/scala/co.blocke.scalajack/dynamodb/CreateTableRequest.scala delete mode 100644 dynamodb/src/test/scala/co.blocke.scalajack/dynamodb/JsonDiff.scala delete mode 100644 dynamodb/src/test/scala/co.blocke.scalajack/dynamodb/JsonMatcher.scala delete mode 100644 dynamodb/src/test/scala/co.blocke.scalajack/dynamodb/Model.scala delete mode 100644 dynamodb/src/test/scala/co.blocke.scalajack/dynamodb/Plain.scala delete mode 100644 dynamodb/src/test/scala/co.blocke.scalajack/dynamodb/README.md delete mode 100644 mongo/src/main/scala/co.blocke.scalajack/mongo/BsonBuilder.scala delete mode 100644 mongo/src/main/scala/co.blocke.scalajack/mongo/BsonParser.scala delete mode 100644 mongo/src/main/scala/co.blocke.scalajack/mongo/Converters.scala delete mode 100644 mongo/src/main/scala/co.blocke.scalajack/mongo/MongoFlavor.scala delete mode 100644 mongo/src/main/scala/co.blocke.scalajack/mongo/MongoWriter.scala delete mode 100644 mongo/src/main/scala/co.blocke.scalajack/mongo/typeadapter/ObjectIdTypeAdapter.scala delete mode 100644 mongo/src/main/scala/co.blocke.scalajack/mongo/typeadapter/OffsetDateTimeTypeAdapter.scala delete mode 100644 mongo/src/main/scala/co.blocke.scalajack/mongo/typeadapter/StringWrapTypeAdapter.scala delete mode 100644 mongo/src/main/scala/co.blocke.scalajack/mongo/typeadapter/ZonedDateTimeTypeAdapter.scala delete mode 100644 mongo/src/test/scala/co.blocke.scalajack/JsonDiff.scala delete mode 100644 mongo/src/test/scala/co.blocke.scalajack/JsonMatcher.scala delete mode 100644 mongo/src/test/scala/co.blocke.scalajack/TestUtil.scala delete mode 100644 mongo/src/test/scala/co.blocke.scalajack/mongo/AnySpec.scala delete mode 100644 mongo/src/test/scala/co.blocke.scalajack/mongo/ConverterSpec.scala delete mode 100644 mongo/src/test/scala/co.blocke.scalajack/mongo/Custom.scala delete mode 100644 mongo/src/test/scala/co.blocke.scalajack/mongo/LooseChange.scala delete mode 100644 mongo/src/test/scala/co.blocke.scalajack/mongo/MapKeys.scala delete mode 100644 mongo/src/test/scala/co.blocke.scalajack/mongo/Model.scala delete mode 100644 mongo/src/test/scala/co.blocke.scalajack/mongo/MongoSpec.scala delete mode 100644 mongo/src/test/scala/co.blocke.scalajack/mongo/TryAndCapture.scala delete mode 100644 mongo/src/test/scala/co.blocke.scalajack/mongo/TupleSpec.scala create mode 100644 project/metals.sbt create mode 100644 src/main/scala/co.blocke.scalajack/Codec.scala create mode 100644 src/main/scala/co.blocke.scalajack/json/JsonWriter.scala create mode 100644 src/main/scala/co.blocke.scalajack/run/Play.scala create mode 100644 src/main/scala/co.blocke.scalajack/run/Sample.scala rename {core/src => src_old}/main/java/co/blocke/scalajack/Change.java (100%) rename {core/src => src_old}/main/java/co/blocke/scalajack/Collection.java (100%) rename {core/src => src_old}/main/java/co/blocke/scalajack/DBKey.java (100%) rename {core/src => src_old}/main/java/co/blocke/scalajack/Ignore.java (100%) rename {core/src => src_old}/main/java/co/blocke/scalajack/JavaStuff.java (100%) rename {core/src => src_old}/main/java/co/blocke/scalajack/Optional.java (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/Converters.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/Main.scalax (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/Performance.scalax (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/SJCapture.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/ScalaJack.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/delimited/DelimitedEitherTypeAdapterFactory.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/delimited/DelimitedFlavor.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/delimited/DelimitedOptionTypeAdapterFactory.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/delimited/DelimitedParser.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/delimited/DelimitedWriter.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/json/JsonFlavor.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/json/JsonParser.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/json/JsonWriter.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/json4s/JValueBuilder.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/json4s/Json4sFlavor.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/json4s/Json4sParser.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/json4s/Json4sWriter.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/json4s/StringWrapTypeAdapter.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/model/ClassFieldMember.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/model/HintValueModifier.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/model/JackFlavor.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/model/LazyTypeAdapter.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/model/Parser.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/model/StringBuilder.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/model/TypeAdapaterCache.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/model/TypeAdapter.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/model/TypeAdapterFactory.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/model/ViewSplice.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/model/Writer.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/typeadapter/AnyTypeAdapter.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/typeadapter/ArrayTypeAdatper.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/typeadapter/CollectionTypeAdapter.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/typeadapter/EitherTypeAdapter.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/typeadapter/EnumTypeAdapter.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/typeadapter/FallbackTypeAdapter.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/typeadapter/IntersectionTypeAdapter.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/typeadapter/JavaPrimitives.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/typeadapter/MaybeStringWrapTypeAadapter.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/typeadapter/OptionTypeAdapter.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/typeadapter/PermissiveJavaPrimitives.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/typeadapter/PermissiveScalaPrimitives.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/typeadapter/ScalaPrimitives.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/typeadapter/SealedTraitTypeAdapter.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/typeadapter/StringWrapTypeAdapter.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/typeadapter/TimePrimitives.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/typeadapter/TraitTypeAdapter.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/typeadapter/TryTypeAdapter.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/typeadapter/TupleTypeAdapter.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/typeadapter/UUIDTypeAdapter.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/typeadapter/UnionTypeAdapter.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/typeadapter/ValueClassTypeAdapter.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/typeadapter/classes/CaseClassTypeAdapter.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/typeadapter/classes/ClassTypeAdapterBase.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/typeadapter/classes/JavaClassTypeAdapter.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/typeadapter/classes/NonCaseClassTypeAdapter.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/typeadapter/classes/ScalaClassTypeAdapter.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/typeadapter/classes/ScalaClassTypeAdapterFactory.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/typeadapter/collection/JavaMapLikeTypeAdapter.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/typeadapter/collection/JavaSeqLikeTypeAdapter.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/typeadapter/collection/JavaStackTypeAdapter.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/typeadapter/collection/MapLikeTypeAdapter.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/typeadapter/collection/SeqLikeTypeAdapter.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/util/BijectiveFunction.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/util/FixFloat.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/yaml/YamlBuilder.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/yaml/YamlFlavor.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/yaml/YamlParser.scala (100%) rename {core/src => src_old}/main/scala/co.blocke.scalajack/yaml/YamlWriter.scala (100%) rename {core/src => src_old}/test/java/co/blocke/scalajack/JavaArray.java (100%) rename {core/src => src_old}/test/java/co/blocke/scalajack/JavaCap.java (100%) rename {core/src => src_old}/test/java/co/blocke/scalajack/JavaEnum.java (100%) rename {core/src => src_old}/test/java/co/blocke/scalajack/JavaSimpleBase.java (100%) rename {core/src => src_old}/test/java/co/blocke/scalajack/JavaSimpleChild.java (100%) rename {core/src => src_old}/test/java/co/blocke/scalajack/Maybe.java (100%) rename {core/src => src_old}/test/java/co/blocke/scalajack/Maybe2.java (100%) rename {core/src => src_old}/test/java/co/blocke/scalajack/OnSetter.java (100%) rename {core/src => src_old}/test/java/co/blocke/scalajack/Temperature.java (100%) rename {core/src => src_old}/test/java/co/blocke/scalajack/Unsupported.java (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/ConverterSpec.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/JsonDiff.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/JsonMatcher.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/TestUtil.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/delimited/DelimitedSpec.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/delimited/Model.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json/collections/AnyCollections.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json/collections/Arrays.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json/collections/Maps.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json/collections/Model.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json/collections/Options.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json/collections/Seqs.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json/collections/Tuples.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json/custom/CustomAdapter.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json/custom/CustomTypeHints.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json/custom/Model.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json/custom/ParseOrElse.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json/mapkeys/ClassPrimKeys.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json/mapkeys/JavaPrimKeys.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json/mapkeys/ListCollKeys.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json/mapkeys/MapCollKeys.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json/mapkeys/Model.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json/mapkeys/ScalaPrimKeys.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json/mapkeys/TupleCollKeys.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json/mapkeys/ValueClassKeys.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json/parameters/ClassParams.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json/parameters/Model.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json/parameters/TraitParams.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json/plainclass/Inheritance.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json/plainclass/Misc.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json/plainclass/Model.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json/plainclass/TryAndCapture.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json/plainclass/ValueClass.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json/primitives/AnyPrim.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json/primitives/Enums.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json/primitives/JavaPrim.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json/primitives/Model.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json/primitives/PermissivePrimitives.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json/primitives/ScalaPrim.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json/primitives/TimePrim.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json/primitives/ValueClassPrim.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json/structures/Eithers.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json/structures/Model.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json/structures/SealedTraits.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json/structures/TryAndCapture.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json/structures/TypeMembers.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json/structures/UnionsAndIntersections.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json4s/AnyColl.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json4s/Custom.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json4s/Json4sSpec.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json4s/Model.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json4s/Parsing.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/json4s/ScalaPrim.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/yaml/collections/AnyColl.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/yaml/mapkeys/ClassPrimKeys.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/yaml/mapkeys/JavaPrimKeys.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/yaml/mapkeys/ListCollKeys.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/yaml/mapkeys/MapCollKeys.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/yaml/mapkeys/Model.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/yaml/mapkeys/ScalaPrimKeys.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/yaml/mapkeys/TupleCollKeys.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/yaml/mapkeys/ValueClassKeys.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/yaml/misc/ReadWriterSpec.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/yaml/misc/TypeMembers.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/yaml/misc/YamlFlavorSpec.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/yaml/parameters/ClassParams.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/yaml/parameters/Model.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/yaml/parameters/TraitParams.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/yaml/primitives.plain/Misc.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/yaml/primitives.plain/Model.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/yaml/primitives.plain/TryAndCapture.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/yaml/primitives.plain/ValueClassPrim.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/yaml/primitives/AnyPrim.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/yaml/primitives/JavaPrim.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/yaml/primitives/Model.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/yaml/primitives/ScalaPrim.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/yaml/primitives/TimePrim.scala (100%) rename {core/src => src_old}/test/scala/co.blocke.scalajack/yaml/primitives/ValueClassPrim.scala (100%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 33b94f0f..f73ca6f3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,12 +1,31 @@ -name: Package Build -# This workflow is triggered on pushes to the repository. -on: [pull_request] +# This file was automatically generated by sbt-github-actions using the +# githubWorkflowGenerate task. You should add and commit this file to +# your git repository. It goes without saying that you shouldn't edit +# this file by hand! Instead, if you wish to make changes, you should +# change your sbt build configuration to revise the workflow description +# to meet your needs, then regenerate this file. + +name: Continuous Integration + +on: + pull_request: + branches: ['**'] + push: + branches: ['**'] + tags: [v*] + +env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} jobs: - build-and-test: - runs-on: ubuntu-latest + build: name: Build and Test - + strategy: + matrix: + os: [ubuntu-latest, windows-latest] + scala: [3.3.0] + java: [zulu@8] + runs-on: ${{ matrix.os }} steps: - name: Ignore line ending differences in git if: contains(runner.os, 'windows') @@ -18,11 +37,12 @@ jobs: with: fetch-depth: 0 - - name: Setup Java (zulu@13) + - name: Setup Java (zulu@8) + if: matrix.java == 'zulu@8' uses: actions/setup-java@v3 with: distribution: zulu - java-version: 13 + java-version: 8 - name: Cache sbt uses: actions/cache@v3 @@ -36,6 +56,84 @@ jobs: ~/Library/Caches/Coursier/v1 key: ${{ runner.os }}-sbt-cache-v2-${{ hashFiles('**/*.sbt') }}-${{ hashFiles('project/build.properties') }} + - name: Check that workflows are up to date + shell: bash + run: sbt githubWorkflowCheck + - name: Build project shell: bash - run: sbt test \ No newline at end of file + run: sbt '++${{ matrix.scala }}' test + + - name: Make target directories + if: github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) + shell: bash + run: mkdir -p target project/target + + - name: Compress target directories + if: github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) + shell: bash + run: tar cf targets.tar target project/target + + - name: Upload target directories + if: github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) + uses: actions/upload-artifact@v3 + with: + name: target-${{ matrix.os }}-${{ matrix.java }}-${{ matrix.scala }} + path: targets.tar + + publish: + name: Publish Artifacts + needs: [build] + if: github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) + strategy: + matrix: + os: [ubuntu-latest] + scala: [3.3.0] + java: [zulu@8] + runs-on: ${{ matrix.os }} + steps: + - name: Ignore line ending differences in git + if: contains(runner.os, 'windows') + run: git config --global core.autocrlf false + + - name: Checkout current branch (full) + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Setup Java (zulu@8) + if: matrix.java == 'zulu@8' + uses: actions/setup-java@v3 + with: + distribution: zulu + java-version: 8 + + - name: Cache sbt + uses: actions/cache@v3 + with: + path: | + ~/.sbt + ~/.ivy2/cache + ~/.coursier/cache/v1 + ~/.cache/coursier/v1 + ~/AppData/Local/Coursier/Cache/v1 + ~/Library/Caches/Coursier/v1 + key: ${{ runner.os }}-sbt-cache-v2-${{ hashFiles('**/*.sbt') }}-${{ hashFiles('project/build.properties') }} + + - name: Download target directories (3.3.0) + uses: actions/download-artifact@v3 + with: + name: target-${{ matrix.os }}-${{ matrix.java }}-3.3.0 + + - name: Inflate target directories (3.3.0) + run: | + tar xf targets.tar + rm targets.tar + + - env: + CI_SNAPSHOT_RELEASE: +publishSigned + PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }} + SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} + SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} + PGP_SECRET: ${{ secrets.PGP_SECRET }} + run: sbt '++${{ matrix.scala }}' ci-release diff --git a/.github/workflows/clean.yml b/.github/workflows/clean.yml new file mode 100644 index 00000000..547aaa43 --- /dev/null +++ b/.github/workflows/clean.yml @@ -0,0 +1,59 @@ +# This file was automatically generated by sbt-github-actions using the +# githubWorkflowGenerate task. You should add and commit this file to +# your git repository. It goes without saying that you shouldn't edit +# this file by hand! Instead, if you wish to make changes, you should +# change your sbt build configuration to revise the workflow description +# to meet your needs, then regenerate this file. + +name: Clean + +on: push + +jobs: + delete-artifacts: + name: Delete Artifacts + runs-on: ubuntu-latest + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - name: Delete artifacts + run: | + # Customize those three lines with your repository and credentials: + REPO=${GITHUB_API_URL}/repos/${{ github.repository }} + + # A shortcut to call GitHub API. + ghapi() { curl --silent --location --user _:$GITHUB_TOKEN "$@"; } + + # A temporary file which receives HTTP response headers. + TMPFILE=/tmp/tmp.$$ + + # An associative array, key: artifact name, value: number of artifacts of that name. + declare -A ARTCOUNT + + # Process all artifacts on this repository, loop on returned "pages". + URL=$REPO/actions/artifacts + while [[ -n "$URL" ]]; do + + # Get current page, get response headers in a temporary file. + JSON=$(ghapi --dump-header $TMPFILE "$URL") + + # Get URL of next page. Will be empty if we are at the last page. + URL=$(grep '^Link:' "$TMPFILE" | tr ',' '\n' | grep 'rel="next"' | head -1 | sed -e 's/.*.*//') + rm -f $TMPFILE + + # Number of artifacts on this page: + COUNT=$(( $(jq <<<$JSON -r '.artifacts | length') )) + + # Loop on all artifacts on this page. + for ((i=0; $i < $COUNT; i++)); do + + # Get name of artifact and count instances of this name. + name=$(jq <<<$JSON -r ".artifacts[$i].name?") + ARTCOUNT[$name]=$(( $(( ${ARTCOUNT[$name]} )) + 1)) + + id=$(jq <<<$JSON -r ".artifacts[$i].id?") + size=$(( $(jq <<<$JSON -r ".artifacts[$i].size_in_bytes?") )) + printf "Deleting '%s' #%d, %'d bytes\n" $name ${ARTCOUNT[$name]} $size + ghapi -X DELETE $REPO/actions/artifacts/$id + done + done diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml deleted file mode 100644 index 5b210239..00000000 --- a/.github/workflows/publish.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Release -on: - create: # Publish release on tag creation - -jobs: - publish: - runs-on: ubuntu-20.04 - steps: -# - run: git checkout "${GITHUB_REF:11}" - - uses: actions/checkout@v2.3.4 - if: github.event.ref_type == 'tag' - with: - fetch-depth: 0 - - uses: olafurpg/setup-scala@v10 - with: - java-version: openjdk@1.13 - if: github.event.ref_type == 'tag' - - uses: olafurpg/setup-gpg@v3 - if: github.event.ref_type == 'tag' - - run: sbt ci-release - if: github.event.ref_type == 'tag' - env: - CI_SNAPSHOT_RELEASE: +publishSigned - PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }} - PGP_SECRET: ${{ secrets.PGP_SECRET }} - SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} - SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} diff --git a/.gitignore b/.gitignore index 1df0bca6..521797b7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ out/ -.bsp/ +.vscode/ +.metals/ +.bloop/ .idea/ .idea_modules/ target/ @@ -7,6 +9,8 @@ reports/ lib_managed/ src_managed/ project/boot/ +project/project/ +project/project/target/ project/plugins/project java_pid*.hprof *.swp @@ -16,3 +20,6 @@ tags .history *~ wip/ +.dotty-ide* +_site/ +.bsp/ diff --git a/.scalafmt.conf b/.scalafmt.conf index 9d7a428f..f0e03d4f 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -1,3 +1,20 @@ -align = more // For pretty alignment. -maxColumn = 175 // For my wide 30" display. -assumeStandardLibraryStripMargin = true +version = 3.7.14 +project.git = true +maxColumn = 256 +runner.dialect = Scala3 + +align.preset = some + +rewrite.rules = [Imports, RedundantBraces, SortModifiers] +rewrite.imports.sort = scalastyle +rewrite.redundantBraces.stringInterpolation = true + +rewrite.scala3.convertToNewSyntax = true +rewrite.scala3.removeOptionalBraces = false + +docstrings.blankFirstLine = no +docstrings.style = SpaceAsterisk +docstrings.wrap = no + +newlines.sometimesBeforeColonInMethodReturnType = true +lineEndings=unix diff --git a/build.sbt b/build.sbt index 073c280c..74bcfb46 100644 --- a/build.sbt +++ b/build.sbt @@ -1,4 +1,5 @@ import org.typelevel.sbt.gha.JavaSpec.Distribution.Zulu +lazy val isCI = sys.env.get("CI").contains("true") inThisBuild(List( organization := "co.blocke", @@ -12,94 +13,69 @@ inThisBuild(List( url("http://www.blocke.co") ) ) + //coverageMinimumStmtTotal := 92, + //coverageFailOnMinimum := true )) name := "scalajack" -ThisBuild / githubWorkflowJavaVersions := Seq(JavaSpec(Zulu, "13")) ThisBuild / organization := "co.blocke" -val scala3 = "3.2.1" -val reflectionLibVersion = "1.1.11" +ThisBuild / scalaVersion := "3.3.0" -lazy val root = (project in file(".")) +lazy val root = project + .in(file(".")) .settings(settings) - .settings(publish / skip := true) .settings( - crossScalaVersions := Nil, - doc := null, // disable dottydoc for now - Compile / doc / sources := Seq() - ) - .aggregate(scalajack, scalajack_dynamo, scalajack_mongo) - -lazy val scalajack = (project in file("core")) - .settings(settings) - .settings( - name := "scalajack", + name := "serializer", + Compile / packageBin / mappings += { + (baseDirectory.value / "plugin.properties") -> "plugin.properties" + }, doc := null, // disable dottydoc for now Compile / doc / sources := Seq(), - libraryDependencies ++= commonDependencies, + //sources in (Compile, doc) := Seq(), Test / parallelExecution := false, - - // This messy stuff turns off reflection compiler plugin except for test case code - addCompilerPlugin("co.blocke" %% "scala-reflection" % reflectionLibVersion), - autoCompilerPlugins := false, - ivyConfigurations += Configurations.CompilerPlugin, - Test / scalacOptions ++= Classpaths.autoPlugins(update.value, Seq(), true) + scalafmtOnCompile := !isCI, + libraryDependencies ++= Seq( + "co.blocke" %% "scala-reflection" % "sj_fixes_58a385", + "org.scalameta" %% "munit" % "1.0.0-M9" % Test + ) ) -lazy val scalajack_dynamo = (project in file("dynamodb")) - .settings(settings) - .settings( - doc := null, // disable dottydoc for now - Compile / doc / sources := Seq(), - libraryDependencies ++= commonDependencies ++ Seq("com.amazonaws" % "aws-java-sdk-dynamodb" % "1.11.882" % Compile), - Test / parallelExecution := false, - - // This messy stuff turns off reflection compiler plugin except for test case code - addCompilerPlugin("co.blocke" %% "scala-reflection" % reflectionLibVersion), - autoCompilerPlugins := false, - ivyConfigurations += Configurations.CompilerPlugin, - Test / scalacOptions ++= Classpaths.autoPlugins(update.value, Seq(), true) - ).dependsOn(scalajack) - -lazy val scalajack_mongo = (project in file("mongo")) - .settings(settings) - .settings( - doc := null, // disable dottydoc for now - Compile / doc / sources := Seq(), - libraryDependencies ++= commonDependencies ++ Seq("org.mongodb" % "mongo-java-driver" % "3.12.7"), - Test / parallelExecution := false, - - // This messy stuff turns off reflection compiler plugin except for test case code - addCompilerPlugin("co.blocke" %% "scala-reflection" % reflectionLibVersion), - autoCompilerPlugins := false, - ivyConfigurations += Configurations.CompilerPlugin, - Test / scalacOptions ++= Classpaths.autoPlugins(update.value, Seq(), true) - ).dependsOn(scalajack) +ThisBuild / githubWorkflowJavaVersions := Seq(JavaSpec(Zulu, "8")) +ThisBuild / githubWorkflowOSes := Seq("ubuntu-latest") +ThisBuild / githubWorkflowPublishTargetBranches := Seq( + RefPredicate.Equals(Ref.Branch("main")), + RefPredicate.StartsWith(Ref.Tag("v")) +) -lazy val commonDependencies = Seq( - "co.blocke" %% "scala-reflection" % reflectionLibVersion, - "commons-codec" % "commons-codec" % "1.15", - "org.json4s" % "json4s-core_2.13" % "3.6.11", - "org.snakeyaml" % "snakeyaml-engine" % "2.3", - "org.json4s" % "json4s-native_2.13" % "3.6.11" % Test, - "org.scalameta" %% "munit" % "0.7.25" % Test +ThisBuild / githubWorkflowPublish := Seq( + WorkflowStep.Sbt( + List("ci-release"), + env = Map( + "PGP_PASSPHRASE" -> "${{ secrets.PGP_PASSPHRASE }}", + "PGP_SECRET" -> "${{ secrets.PGP_SECRET }}", + "SONATYPE_PASSWORD" -> "${{ secrets.SONATYPE_PASSWORD }}", + "SONATYPE_USERNAME" -> "${{ secrets.SONATYPE_USERNAME }}", + "CI_SNAPSHOT_RELEASE" -> "+publishSigned" + ) + ) ) //========================== // Settings //========================== +lazy val settings = Seq( + javacOptions ++= Seq("-source", "1.8", "-target", "1.8"), + scalacOptions ++= compilerOptions, + testFrameworks += new TestFramework("munit.Framework") +) + lazy val compilerOptions = Seq( "-unchecked", "-feature", "-language:implicitConversions", "-deprecation", + // "-explain", "-encoding", "utf8" ) -lazy val settings = Seq( - scalacOptions ++= compilerOptions, - scalaVersion := scala3, - testFrameworks += new TestFramework("munit.Framework") -) - diff --git a/dynamodb/src/main/scala/co.block.scalajack/dynamodb/DynamoFlavor.scala b/dynamodb/src/main/scala/co.block.scalajack/dynamodb/DynamoFlavor.scala deleted file mode 100644 index 206bc9b1..00000000 --- a/dynamodb/src/main/scala/co.block.scalajack/dynamodb/DynamoFlavor.scala +++ /dev/null @@ -1,139 +0,0 @@ -package co.blocke.scalajack -package dynamodb - -import model._ -import typeadapter._ -import co.blocke.scala_reflection.RType - -import scala.jdk.CollectionConverters._ -import com.amazonaws.services.dynamodbv2.document.Item -import com.amazonaws.services.dynamodbv2.model.{ - AttributeDefinition, - CreateTableRequest, - KeySchemaElement, - KeyType, - ProvisionedThroughput, - ScalarAttributeType -} - -case class DynamoFlavor( - override val defaultHint: String = "_hint", - override val permissivesOk: Boolean = false, - override val customAdapters: List[TypeAdapterFactory] = List.empty[TypeAdapterFactory], - override val hintMap: Map[String, String] = Map.empty[String, String], - override val hintValueModifiers: Map[String, HintValueModifier] = Map.empty[String, HintValueModifier], - override val typeValueModifier: HintValueModifier = DefaultHintModifier, - override val parseOrElseMap: Map[Class[_], RType] = Map.empty[Class[_], RType], - override val enumsAsInt: Boolean = false -) extends JackFlavor[Item] { - - // $COVERAGE-OFF$Not testing any of this stuff. Either unused for Dynamo or an exact copy of thoroughly-tested JSON flavor - override val stringifyMapKeys: Boolean = true - - def stringWrapTypeAdapterFactory[T]( - wrappedTypeAdapter: TypeAdapter[T], - emptyStringOk: Boolean = true - ): TypeAdapter[T] = ??? - - def maybeStringWrapTypeAdapterFactory[T]( - wrappedTypeAdapter: TypeAdapter[T], - emptyStringOk: Boolean = true - ): TypeAdapter[T] = ??? - - def allowPermissivePrimitives(): JackFlavor[Item] = - this.copy(permissivesOk = true) - def enumsAsInts(): JackFlavor[Item] = this.copy(enumsAsInt = true) - def parseOrElse(poe: (RType, RType)*): JackFlavor[Item] = - this.copy(parseOrElseMap = this.parseOrElseMap ++ poe.map{(p,oe) => p.infoClass->oe}) - def withAdapters(ta: TypeAdapterFactory*): JackFlavor[Item] = - this.copy(customAdapters = this.customAdapters ++ ta.toList) - def withDefaultHint(hint: String): JackFlavor[Item] = - this.copy(defaultHint = hint) - def withHints(h: (RType, String)*): JackFlavor[Item] = - this.copy(hintMap = this.hintMap ++ h.map{(rt,hint) => rt.name->hint}) - def withHintModifiers(hm: (RType, HintValueModifier)*): JackFlavor[Item] = - this.copy(hintValueModifiers = this.hintValueModifiers ++ hm.map{(rt,hintM) => rt.name->hintM}) - def withTypeValueModifier(tm: HintValueModifier): JackFlavor[Item] = - this.copy(typeValueModifier = tm) - - def parse(input: Item): Parser = ??? - // $COVERAGE-ON$ - - // Embedded JSON-flavored ScalaJack, as Item can read/write JSON, so this is actually the most straightforward - // path to serialization. - lazy val sj: JackFlavor[json.JSON] = { - val baseSj = ScalaJack() - .withAdapters(customAdapters: _*) - .withHints(hintMap.map{ case (k,v) => (RType.of(Class.forName(k)),v) }.toList: _*) - .withHintModifiers(hintValueModifiers.map{ case (k,v) => (RType.of(Class.forName(k)),v)}.toList: _*) - .withDefaultHint(defaultHint) - .parseOrElse(parseOrElseMap.map{ case(k,v) => (RType.of(k),v)}.toList: _*) - baseSj.withTypeValueModifier(typeValueModifier) - } - - private val jsonWriter = json.JsonWriter() - - def _read[T](input: Item, typeAdapter: TypeAdapter[T]): T = - // sj.read[T](input.toJSON.asInstanceOf[json.JSON]) - val parser = json.JsonParser(input.toJSON.asInstanceOf[json.JSON], sj) - typeAdapter.read(parser).asInstanceOf[T] - - def _render[T](t: T, typeAdapter: TypeAdapter[T]): Item = - val sb = StringBuilder[json.JSON]() - typeAdapter.write(t, jsonWriter, sb) - Item.fromJSON(sb.result().asInstanceOf[String]) - // Item.fromJSON(sj.render[T](t).asInstanceOf[String]) - /* - def write[WIRE]( - t: T, - writer: Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit - */ - - // This is Dynamo-Only. User will have to case ScalaJack to DynamoFlavor to call this. - // Yeah, this is a little large-ish for an inlined call, but it *MUST* be inlined for [T] - // to be known/visible, otherwise it will be (incorrectly) interpreted as Any! - inline def createTableRequest[T](provisionedThroughput: ProvisionedThroughput): CreateTableRequest = { - val (optionalTableName, keys, className) = taCache.typeAdapterOf[T] match { - case ta: classes.ClassTypeAdapterBase[_] => (ta.dbCollectionName, ta.dbKeys, ta.info.name) - } - val tableName = optionalTableName.getOrElse( - throw new java.lang.IllegalStateException( - s"Class ${className} must be annotated with @Collection to specify a table name." - ) - ) - - val cleanKeys = keys.map(_.asInstanceOf[ClassFieldMember[_, _]]) - - if (cleanKeys.isEmpty) - throw new java.lang.IllegalStateException( - s"Class ${className} must define at least a primary key with @DBKey." - ) - - val attrDetail = cleanKeys.zipWithIndex.collect { - case (key, idx) if idx == 0 => - ( - new AttributeDefinition(key.name, getAttrType(key)), - new KeySchemaElement(key.name, KeyType.HASH) - ) - case (key, idx) if idx == 1 => - ( - new AttributeDefinition(key.name, getAttrType(key)), - new KeySchemaElement(key.name, KeyType.RANGE) - ) - } - - new CreateTableRequest( - attrDetail.map(_._1).asJava, - tableName, - attrDetail.map(_._2).asJava, - provisionedThroughput - ) - } - - private def getAttrType(key: ClassFieldMember[_, _]) = - if (key.valueTypeAdapter.isStringish) - ScalarAttributeType.S - else - ScalarAttributeType.N -} \ No newline at end of file diff --git a/dynamodb/src/test/scala/co.blocke.scalajack/TestUtil.scala b/dynamodb/src/test/scala/co.blocke.scalajack/TestUtil.scala deleted file mode 100644 index c8365bdf..00000000 --- a/dynamodb/src/test/scala/co.blocke.scalajack/TestUtil.scala +++ /dev/null @@ -1,42 +0,0 @@ -package co.blocke.scalajack - -import munit.internal.console - -object TestUtil { - - inline def describe(message: String, color: String = Console.MAGENTA): Unit = println(s"$color$message${Console.RESET}") - inline def pending = describe(" << Test Pending (below) >>", Console.YELLOW) - - def hexStringToByteArray(s: String): Array[Byte] = { - val len = s.length - val data = new Array[Byte](len / 2) - var i = 0 - while ({ - i < len - }) { - data(i / 2) = ((Character.digit(s.charAt(i), 16) << 4) + Character.digit( - s.charAt(i + 1), - 16 - )).toByte - - i += 2 - } - data - } - - // Utility to generate test code quickly - def showException(label: String, fnStr: String, fn: () => Any) = - try { - fn() - } catch { - case x: IndexOutOfBoundsException => throw x - case t: Throwable => - if (!t.getMessage.contains("\n")) - throw t - val msg = "\"\"\"" + t.getMessage().replace("\n", "\n |") + "\"\"\"" - println( - label + " >> " + t.getClass.getName + "\n-----------------------\n" + - s"val msg = $msg.stripMargin\nthe[${t.getClass.getName}] thrownBy $fnStr should have message msg\n" - ) - } -} \ No newline at end of file diff --git a/dynamodb/src/test/scala/co.blocke.scalajack/dynamodb/Basics.scala b/dynamodb/src/test/scala/co.blocke.scalajack/dynamodb/Basics.scala deleted file mode 100644 index 0572de83..00000000 --- a/dynamodb/src/test/scala/co.blocke.scalajack/dynamodb/Basics.scala +++ /dev/null @@ -1,145 +0,0 @@ -package co.blocke.scalajack -package dynamodb - -import model._ -import co.blocke.scala_reflection.RType - -import TestUtil._ -import munit._ -import munit.internal.console - -import com.amazonaws.services.dynamodbv2.document.Item - -class Basics extends FunSuite: - - val sj = ScalaJack(DynamoFlavor()) - - test("Basic class with embedded class") { - describe( - "--------------------------------\n: DynamoDB Basic Value Tests :\n--------------------------------", Console.BLUE - ) - describe("Standard Serialization:") - val inst: Person = Person( - "Greg", - 50, - List("Woodworking", "Diet Coke"), - Misc(1.23, "boom"), - Some(true) - ) - val item = sj.render(inst) - assertEquals( - """{ Item: {name=Greg, age=50, likes=[Woodworking, Diet Coke], stuff={wow=1.23, bing=boom}, foo=true} }""", - item.toString - ) - assertEquals(inst, sj.read[Person](item)) - } - - test("Optional default = None") { - val inst: Person = Person( - "Greg", - 50, - List("Woodworking", "Diet Coke"), - Misc(1.23, "boom") - ) - val item = sj.render(inst) - assertEquals( - """{ Item: {name=Greg, age=50, likes=[Woodworking, Diet Coke], stuff={wow=1.23, bing=boom}} }""", - item.toString - ) - assertEquals(inst, sj.read[Person](item)) - } - - test("Trait support") { - val inst: Human = Person( - "Greg", - 50, - List("Woodworking", "Diet Coke"), - Misc(1.23, "boom"), - Some(false) - ) - val item = sj.render(inst) - assertEquals( - """{ Item: {_hint=co.blocke.scalajack.dynamodb.Person, name=Greg, age=50, likes=[Woodworking, Diet Coke], stuff={wow=1.23, bing=boom}, foo=false} }""", - item.toString - ) - assertEquals(inst, sj.read[Human](item)) - } - - test("Custom type adapter") { - describe("Extended Serialization:") - val sj = ScalaJack(DynamoFlavor()).withAdapters(PhoneAdapter) - val inst = PersonWithPhone("Bartholomew", "5555555555".asInstanceOf[Phone]) - val item = sj.render(inst) - assertEquals("""{ Item: {name=Bartholomew, phone=555-555-5555} }""", item.toString) - assertEquals(inst, sj.read[PersonWithPhone](item)) - } - - test("With Hints") { - val sj = - ScalaJack(DynamoFlavor()).withHints((RType.of[Address] -> "addr_kind")) - val inst: Address = USAddress("123 Main", "New York", "NY", "39822") - val item = sj.render(inst) - assertEquals( - """{ Item: {addr_kind=co.blocke.scalajack.dynamodb.USAddress, street=123 Main, city=New York, state=NY, postalCode=39822} }""", - item.toString - ) - assertEquals(inst, sj.read[Address](item)) - } - - test("With Hint Modifiers") { - val prependHintMod = ClassNameHintModifier( - (hint: String) => "co.blocke.scalajack.dynamodb." + hint, - (cname: String) => cname.split('.').last - ) - val sj = ScalaJack(DynamoFlavor()) - .withHintModifiers((RType.of[Address], prependHintMod)) - val inst: Address = USAddress("123 Main", "New York", "NY", "39822") - val item = sj.render(inst) - assertEquals( - """{ Item: {_hint=USAddress, street=123 Main, city=New York, state=NY, postalCode=39822} }""", - item.toString - ) - assertEquals(inst, sj.read[Address](item)) - } - - test("Externalized type modifier") { - val sj = ScalaJack(DynamoFlavor()).withTypeValueModifier( - ClassNameHintModifier( - (hint: String) => "co.blocke.scalajack.dynamodb." + hint, - (cname: String) => cname.split('.').last - ) - ) - val value = Envelope("DEF", FancyBody("BOO")) - val item = sj.render(value) - assertEquals( - "{ Item: {Giraffe=FancyBody, id=DEF, body={message=BOO}} }", - item.toString - ) - assert(value == sj.read[Envelope[Body]](item)) - } - - test("Default Hint") { - val sj = ScalaJack(DynamoFlavor()).withDefaultHint("kind") - val inst: Human = Person( - "Greg", - 50, - List("Woodworking", "Diet Coke"), - Misc(1.23, "boom"), - Some(false) - ) - val item = sj.render(inst) - assertEquals( - """{ Item: {kind=co.blocke.scalajack.dynamodb.Person, name=Greg, age=50, likes=[Woodworking, Diet Coke], stuff={wow=1.23, bing=boom}, foo=false} }""", - item.toString - ) - assertEquals(inst, sj.read[Human](item)) - } - - test("ParseOrElse") { - val sj = ScalaJack(DynamoFlavor()) - .parseOrElse((RType.of[Address] -> RType.of[DefaultAddress])) - val item = Item.fromJSON( - """{"_hint":"co.blocke.scalajack.custom.UnknownAddress","street":"123 Main","city":"New York","state":"NY","postalCode":"39822"}""" - ) - assert(DefaultAddress("39822") == sj.read[Address](item)) - } diff --git a/dynamodb/src/test/scala/co.blocke.scalajack/dynamodb/CreateTableRequest.scala b/dynamodb/src/test/scala/co.blocke.scalajack/dynamodb/CreateTableRequest.scala deleted file mode 100644 index 76bbb3e5..00000000 --- a/dynamodb/src/test/scala/co.blocke.scalajack/dynamodb/CreateTableRequest.scala +++ /dev/null @@ -1,41 +0,0 @@ -package co.blocke.scalajack -package dynamodb - -import TestUtil._ -import munit._ -import munit.internal.console -import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput - -class CreateTableRequest extends FunSuite: - - val sj = ScalaJack(DynamoFlavor()).asInstanceOf[DynamoFlavor] - - test("Single primary key") { - describe( - "-----------------------------------------\n: DynamoDB Create Table Request Tests :\n-----------------------------------------", Console.BLUE - ) - val req = sj.createTableRequest[PersonOneKey](new ProvisionedThroughput(12L, 5L)) - assertEquals( - """{AttributeDefinitions: [{AttributeName: name,AttributeType: S}],TableName: people2,KeySchema: [{AttributeName: name,KeyType: HASH}],ProvisionedThroughput: {ReadCapacityUnits: 12,WriteCapacityUnits: 5},}""", - req.toString) - } - - test("Primary key with sorting key") { - val req = - sj.createTableRequest[Person](new ProvisionedThroughput(12L, 5L)) - assertEquals( - """{AttributeDefinitions: [{AttributeName: age,AttributeType: N}, {AttributeName: name,AttributeType: S}],TableName: people,KeySchema: [{AttributeName: age,KeyType: HASH}, {AttributeName: name,KeyType: RANGE}],ProvisionedThroughput: {ReadCapacityUnits: 12,WriteCapacityUnits: 5},}""", - req.toString) - } - - test("Error - no key specified") { - interceptMessage[java.lang.IllegalStateException]("Class co.blocke.scalajack.dynamodb.ErrorNoKey must define at least a primary key with @DBKey."){ - sj.createTableRequest[ErrorNoKey](new ProvisionedThroughput(12L, 5L)) - } - } - - test("Error - no table specified") { - interceptMessage[java.lang.IllegalStateException]("Class co.blocke.scalajack.dynamodb.ErrorNoTable must be annotated with @Collection to specify a table name."){ - sj.createTableRequest[ErrorNoTable](new ProvisionedThroughput(12L, 5L)) - } - } diff --git a/dynamodb/src/test/scala/co.blocke.scalajack/dynamodb/JsonDiff.scala b/dynamodb/src/test/scala/co.blocke.scalajack/dynamodb/JsonDiff.scala deleted file mode 100644 index 7a49c311..00000000 --- a/dynamodb/src/test/scala/co.blocke.scalajack/dynamodb/JsonDiff.scala +++ /dev/null @@ -1,58 +0,0 @@ -package co.blocke.scalajack -package dynamodb - -import org.json4s.JsonAST.{ JNothing, JObject, JValue } - -object JsonDiff { - - def compare( - left: JValue, - right: JValue, - leftLabel: String = "left", - rightLabel: String = "right"): Seq[JsonDiff] = { - (left, right) match { - case (JObject(leftFields), JObject(rightFields)) => - val allFieldNames = - (leftFields.map(_._1) ++ rightFields.map(_._1)).distinct - allFieldNames.sorted flatMap { fieldName => - val leftFieldValue = leftFields - .collectFirst({ case (`fieldName`, fieldValue) => fieldValue }) - .getOrElse(JNothing) - val rightFieldValue = rightFields - .collectFirst({ case (`fieldName`, fieldValue) => fieldValue }) - .getOrElse(JNothing) - compare(leftFieldValue, rightFieldValue, leftLabel, rightLabel) - } - - // ---- Not used/needed at present, and I have questions about the correct behavior here. Exactly how do you - // "diff" two arrays (not necessarily homogeneous typed)? - // - // case (JArray(leftElements), JArray(rightElements)) => - // (0 until (leftElements.size max rightElements.size)) flatMap { elementIndex => - // val leftElement = leftElements.applyOrElse(elementIndex, (_: Int) => JNothing) - // val rightElement = rightElements.applyOrElse(elementIndex, (_: Int) => JNothing) - // compare(path \ elementIndex, leftElement, rightElement, leftLabel, rightLabel) - // } - - case _ => - if (left == right) { - Seq.empty - } else { - val outerLeft = left - val outerRight = right - Seq(new JsonDiff { - override val left: JValue = outerLeft - override val right: JValue = outerRight - override def toString: String = - s"JsonDiff($leftLabel: $left, $rightLabel: $right)" - }) - } - } - } - -} - -trait JsonDiff { - val left: JValue - val right: JValue -} diff --git a/dynamodb/src/test/scala/co.blocke.scalajack/dynamodb/JsonMatcher.scala b/dynamodb/src/test/scala/co.blocke.scalajack/dynamodb/JsonMatcher.scala deleted file mode 100644 index 1cdd381a..00000000 --- a/dynamodb/src/test/scala/co.blocke.scalajack/dynamodb/JsonMatcher.scala +++ /dev/null @@ -1,21 +0,0 @@ -package co.blocke.scalajack -package dynamodb - -import co.blocke.scalajack.json.JSON -import org.json4s.JsonAST.JValue -import org.json4s.native.JsonMethods._ -import org.json4s.string2JsonInput - -object JsonMatcher { - - def jsonMatches( expected: JSON, actual: JSON ): Boolean = - val diffs = JsonDiff.compare( - parseJValue(expected.asInstanceOf[String]), - parseJValue(actual.asInstanceOf[String]), - "expected", - "actual" - ) - diffs.isEmpty - - implicit def parseJValue(string: String): JValue = parse(string) -} diff --git a/dynamodb/src/test/scala/co.blocke.scalajack/dynamodb/Model.scala b/dynamodb/src/test/scala/co.blocke.scalajack/dynamodb/Model.scala deleted file mode 100644 index 36f725e6..00000000 --- a/dynamodb/src/test/scala/co.blocke.scalajack/dynamodb/Model.scala +++ /dev/null @@ -1,110 +0,0 @@ -package co.blocke.scalajack -package dynamodb - -import model._ -import scala.collection.mutable -import co.blocke.scala_reflection.RType -import co.blocke.scala_reflection.info.AliasInfo - -opaque type Phone >: Null = String - -// Override just Phone -object PhoneAdapter extends TypeAdapterFactory with TypeAdapter[Phone]: - def matches(concrete: RType): Boolean = - concrete match { - case a: AliasInfo if a.name == "Phone" => true - case _ => false - } - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[Phone] = this - val info = RType.of[Phone] - override def isStringish: Boolean = true - - def read(parser: Parser): Phone = - parser.expectString() match { - case null => null.asInstanceOf[Phone] - case s: String => s.replaceAll("-", "").asInstanceOf[Phone] - } - - def write[WIRE]( - t: Phone, - writer: Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit = t match { - case null => writer.writeNull(out) - case _ => - writer.writeString( - "%s-%s-%s".format(t.toString.substring(0, 3), t.toString.substring(3, 6), t.toString.substring(6)), - out - ) - } - -trait Human { val name: String; val age: Int } -case class Misc(wow: Double, bing: String) - -@Collection(name = "people") -case class Person( - @DBKey(index = 1) name:String, - @DBKey(index = 0) age:Int, - likes: List[String], - stuff: Misc, - foo: Option[Boolean] = None) - extends Human - -@Collection(name = "people2") -case class PersonOneKey( - @DBKey(index = 0) name:String, - age: Int, - likes: List[String], - stuff: Misc, - foo: Option[Boolean] = None) - extends Human - -@Collection(name = "bogus") -case class ErrorNoKey( - name: String, - age: Int, - likes: List[String], - stuff: Misc, - foo: Option[Boolean] = None) - extends Human - -case class ErrorNoTable( - @DBKey(index = 0) name:String, - age: Int, - likes: List[String], - stuff: Misc, - foo: Option[Boolean] = None) - extends Human - -case class PersonWithPhone(name: String, phone: Phone) -trait Address { val postalCode: String } -case class DefaultAddress(postalCode: String) extends Address -case class USAddress( - street: String, - city: String, - state: String, - postalCode: String) - extends Address - -@Collection(name = "people") -class PersonPlain1( - @DBKey(index = 1) val name:String, - @DBKey(index = 0) val age:Int, - val likes: List[String], - val stuff: Misc, - val foo: Option[Boolean] = None) - -@Collection(name = "people") -class PersonPlain2() { - @DBKey(index = 1) var name: String = "" - @DBKey(index = 0) var age: Int = 0 - var likes: List[String] = List.empty[String] - var stuff: Misc = _ - var foo: Option[Boolean] = None -} - -trait Body -case class FancyBody(message: String) extends Body - -case class Envelope[T <: Body](id: String, body: T) { - type Giraffe = T -} \ No newline at end of file diff --git a/dynamodb/src/test/scala/co.blocke.scalajack/dynamodb/Plain.scala b/dynamodb/src/test/scala/co.blocke.scalajack/dynamodb/Plain.scala deleted file mode 100644 index 5f177097..00000000 --- a/dynamodb/src/test/scala/co.blocke.scalajack/dynamodb/Plain.scala +++ /dev/null @@ -1,72 +0,0 @@ -package co.blocke.scalajack -package dynamodb - -import TestUtil._ -import munit._ -import munit.internal.console -import JsonMatcher._ - -import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput - -class Plain extends FunSuite: - - val sj = ScalaJack(DynamoFlavor()) - - test("All val constructor") { - describe( - "--------------------------------\n: DynamoDB Plain Class Tests :\n--------------------------------", Console.BLUE - ) - describe("Items:") - val inst = new PersonPlain1( - "Greg", - 50, - List("Woodworking", "Diet Coke"), - Misc(1.23, "boom"), - Some(true) - ) - val item = sj.render(inst) - assertEquals(true, jsonMatches( - """{"name":"Greg","age":50,"likes":["Woodworking","Diet Coke"],"stuff":{"wow":1.23,"bing":"boom"},"foo":true}""".asInstanceOf[json.JSON], - item.toJSON.asInstanceOf[json.JSON] - )) - assertEquals(true, { - val inst2 = sj.read[PersonPlain1](item) - inst.name == inst2.name && inst.age == inst2.age && inst.stuff.wow == inst2.stuff.wow - }) - } - - test("Zero-arg constructor with var members") { - val inst = new PersonPlain2() - inst.name = "Greg" - inst.age = 50 - inst.likes = List("Woodworking", "Diet Coke") - inst.stuff = Misc(1.23, "boom") - val item = sj.render(inst) - assertEquals(true, jsonMatches( - """{"name":"Greg","age":50,"likes":["Woodworking","Diet Coke"],"stuff":{"wow":1.23,"bing":"boom"}}""".asInstanceOf[json.JSON], - item.toJSON.asInstanceOf[json.JSON] - )) - assertEquals(true, { - val inst2 = sj.read[PersonPlain2](item) - inst.name == inst2.name && inst.age == inst2.age && inst.stuff.wow == inst2.stuff.wow - }) - } - - test("All val constructor") { - describe("Creation:") - val req = sj - .asInstanceOf[DynamoFlavor] - .createTableRequest[PersonPlain1](new ProvisionedThroughput(12L, 5L)) - assertEquals( - """{AttributeDefinitions: [{AttributeName: age,AttributeType: N}, {AttributeName: name,AttributeType: S}],TableName: people,KeySchema: [{AttributeName: age,KeyType: HASH}, {AttributeName: name,KeyType: RANGE}],ProvisionedThroughput: {ReadCapacityUnits: 12,WriteCapacityUnits: 5},}""", - req.toString) - } - - test("Zero-arg constructor with var members") { - val req = sj - .asInstanceOf[DynamoFlavor] - .createTableRequest[PersonPlain2](new ProvisionedThroughput(12L, 5L)) - assertEquals( - """{AttributeDefinitions: [{AttributeName: age,AttributeType: N}, {AttributeName: name,AttributeType: S}],TableName: people,KeySchema: [{AttributeName: age,KeyType: HASH}, {AttributeName: name,KeyType: RANGE}],ProvisionedThroughput: {ReadCapacityUnits: 12,WriteCapacityUnits: 5},}""", - req.toString) - } diff --git a/dynamodb/src/test/scala/co.blocke.scalajack/dynamodb/README.md b/dynamodb/src/test/scala/co.blocke.scalajack/dynamodb/README.md deleted file mode 100644 index 5992d855..00000000 --- a/dynamodb/src/test/scala/co.blocke.scalajack/dynamodb/README.md +++ /dev/null @@ -1,10 +0,0 @@ -This test suite is very minimal. - -Since the core of the DynamoDB implementation for ScalaJack involves merely using Item's own fromJSON method, -we just wrapped ScalaJack's native JSON capabilities, which have been extensively tested. There's no particular -need to re-test these here. - -There are limitations on what DynamoDB can handle, for example you can't serialize a naked List[Something] -like you can in the JSON flavor. - -If DynamoDB can handle it, ScalaJack's DynamoDB flavor should do it. \ No newline at end of file diff --git a/mongo/src/main/scala/co.blocke.scalajack/mongo/BsonBuilder.scala b/mongo/src/main/scala/co.blocke.scalajack/mongo/BsonBuilder.scala deleted file mode 100644 index 51ffa89a..00000000 --- a/mongo/src/main/scala/co.blocke.scalajack/mongo/BsonBuilder.scala +++ /dev/null @@ -1,22 +0,0 @@ -package co.blocke.scalajack -package mongo - -import org.bson.BsonValue - -import scala.collection.mutable - -case class BsonBuilder() extends mutable.Builder[BsonValue, BsonValue] { - private var internalValue: Option[BsonValue] = None - - def addOne(elem: BsonValue): this.type = { - internalValue = Some(elem) - this - } - - def clear(): Unit = internalValue = None - - def result(): BsonValue = - internalValue.getOrElse( - throw new ScalaJackError("No value set for internal mongo builder") - ) -} \ No newline at end of file diff --git a/mongo/src/main/scala/co.blocke.scalajack/mongo/BsonParser.scala b/mongo/src/main/scala/co.blocke.scalajack/mongo/BsonParser.scala deleted file mode 100644 index 23d6d50e..00000000 --- a/mongo/src/main/scala/co.blocke.scalajack/mongo/BsonParser.scala +++ /dev/null @@ -1,216 +0,0 @@ -package co.blocke.scalajack -package mongo - -import model._ -import org.bson._ -import org.bson.types.ObjectId -import co.blocke.scalajack.typeadapter.classes.ClassTypeAdapterBase -import co.blocke.scala_reflection.RType -import co.blocke.scala_reflection.info.TypeMemberInfo - -import scala.collection.mutable -import scala.jdk.CollectionConverters._ - -case class BsonParser(input: BsonValue, jackFlavor: JackFlavor[BsonValue]) extends Parser { - type WIRE = BsonValue - - def expectString(nullOK: Boolean = true): String = - if (input == null || input.isNull) - null - else if (input.isString) - input.asString.getValue - else - throw new ScalaJackError(s"Expected string here, not '$input'") - - def expectList[K, TO](KtypeAdapter: TypeAdapter[K], builder: mutable.Builder[K, TO]): TO = - if (input == null || input.isNull) - // $COVERAGE-OFF$Null caught by TypeAdapter but this left here as a safety - null.asInstanceOf[TO] - // $COVERAGE-ON$ - else if (input.isArray) { - input.asArray.getValues.asScala - .foreach(v => builder += KtypeAdapter.read(BsonParser(v, jackFlavor))) - builder.result() - } else - throw new ScalaJackError(s"Expected list here, not '$input'") - - def expectTuple( - tupleFieldTypeAdapters: List[TypeAdapter[_]] - ): List[Object] = - input match { - // $COVERAGE-OFF$Null caught by TypeAdapter but this left here as a safety - case inp if inp == null || inp.isNull => - null - // $COVERAGE-ON$ - case inp if inp.isArray => - tupleFieldTypeAdapters.zip(input.asArray.getValues.asScala).map { (fieldTypeAdapter, v) => - fieldTypeAdapter.read(BsonParser(v, jackFlavor)).asInstanceOf[Object] - } - case x => - throw new ScalaJackError(s"Expected tuple (list) here, not '$input'") - } - - def expectMap[K, V, TO](keyTypeAdapter: TypeAdapter[K], valueTypeAdapter: TypeAdapter[V], builder: mutable.Builder[(K, V), TO]): TO = - if (input == null || input.isNull) - // $COVERAGE-OFF$Null caught by TypeAdapter but this left here as a safety - null.asInstanceOf[TO] - // $COVERAGE-ON$ - else if (input.isDocument) { - input.asDocument.entrySet.asScala.foreach { entry => - val mapKey = keyTypeAdapter.read( - BsonParser(new BsonString(entry.getKey), jackFlavor) - ) - val mapValue = - valueTypeAdapter.read(BsonParser(entry.getValue, jackFlavor)) - val newElem: (K, V) = (mapKey, mapValue) - builder += newElem - } - builder.result - } else - throw new ScalaJackError(s"Expected document (map) here, not '$input'") - - def expectObject( - classBase: ClassTypeAdapterBase[_], - hintLabel: String - ): (mutable.BitSet, List[Object], java.util.HashMap[String, _]) = - if (input == null || input.isNull) - // $COVERAGE-OFF$Null caught by TypeAdapter but this left here as a safety - null - // $COVERAGE-ON$ - else if (input.isDocument) { - val args = classBase.argsTemplate.clone() - val fieldBits = mutable.BitSet() - val captured = - if (classBase.isSJCapture) new java.util.HashMap[String, BsonValue]() - else null - input.asDocument.entrySet.asScala.foreach { - case entry if entry.getKey == ID_FIELD && classBase.dbKeys.size == 1 => - val dbKey = classBase.dbKeys.head - fieldBits += dbKey.info.index - args(dbKey.info.index) = dbKey.valueTypeAdapter.read(BsonParser(entry.getValue, jackFlavor)).asInstanceOf[Object] - case entry if entry.getKey == ID_FIELD => // compound key - entry.getValue.asDocument.entrySet.asScala.foreach { dbKeyEntry => - classBase.fieldMembersByName - .get(dbKeyEntry.getKey) - .map { field => - fieldBits += field.info.index - args(field.info.index) = field.valueTypeAdapter.read( - BsonParser(dbKeyEntry.getValue, jackFlavor) - ).asInstanceOf[Object] - } - } - case entry => - classBase.fieldMembersByName.get(entry.getKey) match { - case Some(field) => - fieldBits += field.info.index - args(field.info.index) = field.valueTypeAdapter.read( - BsonParser(entry.getValue, jackFlavor) - ).asInstanceOf[Object] - case None => // found some input field not present in class - if (captured != null) - captured.put(entry.getKey, entry.getValue) - } - } - val missing = fieldBits.intersect(classBase.dbKeys.map(_.info.index).toSet) - if (classBase.dbKeys.size > 0 && missing.isEmpty) { - if (classBase.dbKeys.size == 1) - throw new ScalaJackError( - "Missing key (_id) field, or a component of a compound key field" - ) - else - throw new ScalaJackError( - "Missing key (_id) field, or a component of a compound key field: " + - classBase.dbKeys - .collect { case f if missing.contains(f.info.index) => f.name } - .mkString(",") - ) - } - (fieldBits, args.toList, captured) - } else - throw new ScalaJackError(s"Expected document (object) here, not '$input'") - - def expectBoolean(): Boolean = - if (input.isBoolean) - input.asBoolean.getValue - else - throw new ScalaJackError(s"Expected boolean here, not '$input'") - - def expectNumber(nullOK: Boolean = false): String = - input match { - case i if i.isNull && nullOK => null - case i if i.isNull => - throw new ScalaJackError(s"Expected number here, not '$input'") - case i if i.isDecimal128 => i.asDecimal128.getValue.toString - case i if i.isDouble => i.asDouble.getValue.toString - case i if i.isInt32 => i.asInt32.getValue.toString - case i if i.isInt64 => i.asInt64.getValue.toString - case i if i.isDateTime => i.asDateTime.getValue.toString - case _ => throw new ScalaJackError(s"Expected number here, not '$input'") - } - - def peekForNull: Boolean = input == null || input.isNull - - def scanForHint(hint: String, converterFn: HintBijective): Class[_] = - if (input.isDocument) { - val doc = input.asDocument - Option(doc.get(hint)) match { - case Some(hintValue) if hintValue.isString => - val hintType = try { - Class.forName(converterFn.apply(hintValue.asString.getValue)) - } catch { - case t: Throwable => - throw new ScalaJackError( - s"Couldn't marshal class for ${hintValue.asString.getValue}" - ) - } - hintType - case Some(hintValue) => - throw new ScalaJackError(s"Hint value $hint must be a string value") - case None => throw new ScalaJackError(s"Type hint '$hint' not found") - } - } else - throw new ScalaJackError(s"Expected document here, not '$input'") - - // For embedded type members. Convert the type member into runtime "actual" type, e.g. T --> Foo - def resolveTypeMembers( - typeMembersByName: Map[String, TypeMemberInfo], - converterFn: HintBijective - ): Map[String, TypeMemberInfo] = // Returns Map[Type Signature Type (e.g. 'T'), Type] - if (input.isDocument) { - val doc = input.asDocument - val collected = doc.keySet.asScala.collect { - case key if typeMembersByName.contains(key) => - ( - key, - TypeMemberInfo(key, typeMembersByName(key).typeSymbol, RType.of(Class.forName(converterFn.apply(doc.get(key).asString.getValue)))) - ) - } - collected.toMap - } else - throw new ScalaJackError(s"Expected document (object) here, not '$input'") - - def showError(msg: String): String = msg - def backspace(): Unit = {} - def mark(): Int = -1 - def revertToMark(mark: Int): Unit = {} - def nextIsString: Boolean = input.isString - def nextIsNumber: Boolean = input.isNumber - def nextIsObject: Boolean = input.isDocument - def nextIsArray: Boolean = input.isArray - def nextIsBoolean: Boolean = input.isBoolean - def subParser(input: BsonValue): Parser = this - def sourceAsString: String = - throw new ScalaJackError( - s"""BSON type ${input.getClass.getName} is not currently supported in ScalaJack.""" - ) - - //--- Mongo Specific --- - def expectObjectId(): ObjectId = - if (input == null || input.isNull) - null - else if (input.isObjectId) - input.asObjectId.getValue - else - throw new ScalaJackError(s"Expected ObjectId here, not '$input'") - -} \ No newline at end of file diff --git a/mongo/src/main/scala/co.blocke.scalajack/mongo/Converters.scala b/mongo/src/main/scala/co.blocke.scalajack/mongo/Converters.scala deleted file mode 100644 index 01092c7f..00000000 --- a/mongo/src/main/scala/co.blocke.scalajack/mongo/Converters.scala +++ /dev/null @@ -1,37 +0,0 @@ -package co.blocke.scalajack -package mongo - -import org.bson._ -import model.JackFlavor -import co.blocke.scalajack.Converters._ -import co.blocke.scalajack.json._ -import json4s._ -import yaml._ -import delimited._ -import org.json4s.JValue - -object Converters: - - extension (b: BsonValue) - inline def mapMongoTo[T, S](toFlavor: JackFlavor[S])(fn: T => T)(implicit sjB: JackFlavor[BsonValue]): S = toFlavor.render[T](fn(sjB.read[T](b))) - - // montoTo... flavors need T to be able to handle _id (DBKey) fields. - extension (b: BsonValue) - inline def mongoToJson[T](implicit sjJ: JackFlavor[JSON], sjB: JackFlavor[BsonValue]): JSON = sjJ.render( sjB.read[T](b) ) - inline def mongoToYaml[T](implicit sjY: JackFlavor[YAML], sjB: JackFlavor[BsonValue]): YAML = sjY.render( sjB.read[T](b) ) - inline def mongoToJson4s[T](implicit sjV: JackFlavor[JValue], sjB: JackFlavor[BsonValue]): JValue = sjV.render( sjB.read[T](b) ) - inline def fromMongo[T](implicit sjB: JackFlavor[BsonValue]): T = sjB.read[T](b) - inline def mapMongo[T](fn: T => T)(implicit sjB: JackFlavor[BsonValue]): BsonValue = sjB.render[T](fn(sjB.read[T](b))) - - // Tie in other converters... - extension (j: JSON) - inline def jsonToMongo[T](implicit sjB: JackFlavor[BsonValue], sjJ: JackFlavor[JSON]): BsonValue = sjB.render( sjJ.read[T](j) ) - - extension (y: YAML) - inline def yamlToMongo[T](implicit sjB: JackFlavor[BsonValue], sjY: JackFlavor[YAML]): BsonValue = sjB.render( sjY.read[T](y) ) - - extension (j: JValue) - inline def json4sToMongo[T](implicit sjB: JackFlavor[BsonValue], sjV: JackFlavor[JValue]): BsonValue = sjB.render( sjV.read[T](j) ) - - extension[T] (a: T) - inline def toMongo(implicit sjB: JackFlavor[BsonValue]): BsonValue = sjB.render(a) \ No newline at end of file diff --git a/mongo/src/main/scala/co.blocke.scalajack/mongo/MongoFlavor.scala b/mongo/src/main/scala/co.blocke.scalajack/mongo/MongoFlavor.scala deleted file mode 100644 index 2957d2b5..00000000 --- a/mongo/src/main/scala/co.blocke.scalajack/mongo/MongoFlavor.scala +++ /dev/null @@ -1,79 +0,0 @@ -package co.blocke.scalajack -package mongo - -import model._ -import typeadapter._ -import org.bson._ -import co.blocke.scala_reflection.RType - -import scala.collection.mutable - -final val ID_FIELD = "_id" - -case class MongoFlavor( - override val defaultHint: String = "_hint", - override val permissivesOk: Boolean = false, - override val customAdapters: List[TypeAdapterFactory] = List.empty[TypeAdapterFactory], - override val hintMap: Map[String, String] = Map.empty[String, String], - override val hintValueModifiers: Map[String, HintValueModifier] = Map.empty[String, HintValueModifier], - override val typeValueModifier: HintValueModifier = DefaultHintModifier, - override val parseOrElseMap: Map[Class[_], RType] = Map.empty[Class[_], RType], - override val enumsAsInt: Boolean = false -) extends JackFlavor[BsonValue] { - - override val stringifyMapKeys: Boolean = true - - def allowPermissivePrimitives(): JackFlavor[BsonValue] = - this.copy(permissivesOk = true) - def enumsAsInts(): JackFlavor[BsonValue] = this.copy(enumsAsInt = true) - def parseOrElse(poe: (RType, RType)*): JackFlavor[BsonValue] = - this.copy(parseOrElseMap = this.parseOrElseMap ++ poe.map{(p,oe) => p.infoClass->oe}) - def withAdapters(ta: TypeAdapterFactory*): JackFlavor[BsonValue] = - this.copy(customAdapters = this.customAdapters ++ ta.toList) - def withDefaultHint(hint: String): JackFlavor[BsonValue] = - this.copy(defaultHint = hint) - def withHints(h: (RType, String)*): JackFlavor[BsonValue] = - this.copy(hintMap = this.hintMap ++ h.map{(rt,hint) => rt.name->hint}) - def withHintModifiers(hm: (RType, HintValueModifier)*): JackFlavor[BsonValue] = - this.copy(hintValueModifiers = this.hintValueModifiers ++ hm.map{(rt,hintM) => rt.name->hintM}) - def withTypeValueModifier(tm: HintValueModifier): JackFlavor[BsonValue] = - this.copy(typeValueModifier = tm) - - def stringWrapTypeAdapterFactory[T]( - wrappedTypeAdapter: TypeAdapter[T], - emptyStringOk: Boolean = true - ): TypeAdapter[T] = - new StringWrapTypeAdapter(wrappedTypeAdapter) - - def maybeStringWrapTypeAdapterFactory[T]( - wrappedTypeAdapter: TypeAdapter[T], - emptyStringOk: Boolean = true - ): TypeAdapter[T] = - co.blocke.scalajack.typeadapter.MaybeStringWrapTypeAdapter(this, wrappedTypeAdapter, emptyStringOk) - - override def bakeCache(): TypeAdapterCache = { - val dads = super.bakeCache() - dads.copy( - factories = List( - ObjectIdTypeAdapter, - OffsetDateTimeTypeAdapter, - ZonedDateTimeTypeAdapter - ) ++ dads.factories.toList - ) - } - - private val writer = MongoWriter(anyTypeAdapter) - - def parse(input: BsonValue): Parser = BsonParser(input, this) - - def _read[T](input: BsonValue, typeAdapter: TypeAdapter[T]): T = - val parser = BsonParser(input, this) - typeAdapter.read(parser).asInstanceOf[T] - - def _render[T](t: T, typeAdapter: TypeAdapter[T]): BsonValue = - val sb = mongo.BsonBuilder() - typeAdapter.write(t, writer, sb) - sb.result() - - override def getBuilder: mutable.Builder[BsonValue, BsonValue] = mongo.BsonBuilder() -} \ No newline at end of file diff --git a/mongo/src/main/scala/co.blocke.scalajack/mongo/MongoWriter.scala b/mongo/src/main/scala/co.blocke.scalajack/mongo/MongoWriter.scala deleted file mode 100644 index 82a9266d..00000000 --- a/mongo/src/main/scala/co.blocke.scalajack/mongo/MongoWriter.scala +++ /dev/null @@ -1,169 +0,0 @@ -package co.blocke.scalajack -package mongo - -import model._ - -import scala.collection.Map -import scala.collection.mutable -import org.bson._ -import org.bson.types.Decimal128 - -case class MongoWriter(anyTypeAdapter: TypeAdapter[Any]) extends Writer[BsonValue] { - - def writeBigInt(t: BigInt, out: mutable.Builder[BsonValue, BsonValue]): Unit = - throw new ScalaJackError( - "BigInt is currently an unsupported datatype for MongoDB serialization" - ) - def writeBoolean(t: Boolean, out: mutable.Builder[BsonValue, BsonValue]): Unit = - out += new BsonBoolean(t) - def writeDecimal(t: BigDecimal, out: mutable.Builder[BsonValue, BsonValue]): Unit = - out += new BsonDecimal128(new Decimal128(t.bigDecimal)) - def writeDouble(t: Double, out: mutable.Builder[BsonValue, BsonValue]): Unit = - out += new BsonDouble(t) - def writeInt(t: Int, out: mutable.Builder[BsonValue, BsonValue]): Unit = - out += new BsonInt32(t) - def writeLong(t: Long, out: mutable.Builder[BsonValue, BsonValue]): Unit = - out += new BsonInt64(t) - def writeNull(out: mutable.Builder[BsonValue, BsonValue]): Unit = - out += new BsonNull() - def writeString(t: String, out: mutable.Builder[BsonValue, BsonValue]): Unit = - out += new BsonString(t) - def writeRaw(t: BsonValue, out: mutable.Builder[BsonValue, BsonValue]): Unit = - out += t - - def writeArray[Elem](t: Iterable[Elem], elemTypeAdapter: TypeAdapter[Elem], out: mutable.Builder[BsonValue, BsonValue]): Unit = - t match { - case null => out += new BsonNull() - case a => - val array = new BsonArray() - val builder = BsonBuilder() - val iter = a.iterator - while (iter.hasNext) { - elemTypeAdapter.write[BsonValue](iter.next, this, builder) - array.add(builder.result()) - builder.clear() - } - out += array - } - - def writeMap[Key, Value, To]( - t: Map[Key, Value], - keyTypeAdapter: TypeAdapter[Key], - valueTypeAdapter: TypeAdapter[Value], - out: mutable.Builder[BsonValue, BsonValue] - ): Unit = t match { - case null => out += new BsonNull() - case daMap => - val doc = new BsonDocument() - val keyBuilder = BsonBuilder() - val valueBuilder = BsonBuilder() - daMap.foreach { - case (key, value) => - if (key == null) - throw new ScalaJackError("Map keys cannot be null.") - keyTypeAdapter.write(key, this, keyBuilder) - valueTypeAdapter.write(value, this, valueBuilder) - doc.append( - keyBuilder.result().asString.getValue, - valueBuilder.result() - ) - keyBuilder.clear() - valueBuilder.clear() - } - out += doc - } - - def writeTuple[T]( - t: T, - writeFn: (Product) => List[(TypeAdapter[_], Any)], - out: mutable.Builder[BsonValue, BsonValue] - ): Unit = { - - var arr = new BsonArray() - val outBuf = BsonBuilder() - writeFn(t.asInstanceOf[Product]).foreach { (fieldTA, fieldValue) => - outBuf.clear() - fieldTA.castAndWrite(fieldValue, this, outBuf) - arr.add(outBuf.result()) - } - out += arr - } - - @inline private def writeFields(fields: List[(String, Object, TypeAdapter[_])], doc: BsonDocument): Unit = { - for ((label, value, valueTypeAdapter) <- fields) - if (value != None) { - val builder = BsonBuilder() - valueTypeAdapter.castAndWrite(value, this, builder) - doc.append(label, builder.result()) - } - } - - def writeObject[T]( - t: T, - orderedFieldNames: List[String], - fieldMembersByName: Map[String, ClassFieldMember[_,_]], - out: mutable.Builder[BsonValue, BsonValue], - extras: List[(String, ExtraFieldValue[_])] = List.empty[(String, ExtraFieldValue[_])] - ): Unit = - if (t == null) - out += new BsonNull() - else { - val doc = new BsonDocument() - writeFields( - extras.map( - e => - ( - e._1, - e._2.value.asInstanceOf[Object], - e._2.valueTypeAdapter.asInstanceOf[TypeAdapter[Any]] - ) - ), - doc - ) - - // Logic to handle _id (@DBKey) - val (dbkeys, theRest) = - fieldMembersByName.partition(_._2.dbKeyIndex.isDefined) - if (dbkeys.isEmpty) // no @DBKey fields - writeFields( - fieldMembersByName - .map(f => (f._1, f._2.info.valueOf(t), f._2.valueTypeAdapter)) - .toList, - doc - ) - else { - if (dbkeys.size == 1) { - val toWrite = List( - ( - ID_FIELD, - fieldMembersByName(dbkeys.head._1).info.valueOf(t), - fieldMembersByName(dbkeys.head._1).valueTypeAdapter - ) - ) - writeFields(toWrite, doc) - } else { - val idDoc = new BsonDocument() - val toWrite = dbkeys - .map(e => (e._1, e._2.info.valueOf(t), e._2.valueTypeAdapter)) - .toList - writeFields(toWrite, idDoc) - doc.append(ID_FIELD, idDoc) - } - val toWrite = theRest - .map(e => (e._1, e._2.info.valueOf(t), e._2.valueTypeAdapter)) - .toList - writeFields(toWrite, doc) - } - - t match { - case sjc: SJCapture => - import scala.jdk.CollectionConverters._ - sjc.captured.asScala.foreach { - case (label, capturedValue) => - doc.append(label, capturedValue.asInstanceOf[BsonValue]) - } - case _ => - } - out += doc - } -} \ No newline at end of file diff --git a/mongo/src/main/scala/co.blocke.scalajack/mongo/typeadapter/ObjectIdTypeAdapter.scala b/mongo/src/main/scala/co.blocke.scalajack/mongo/typeadapter/ObjectIdTypeAdapter.scala deleted file mode 100644 index 64d6b108..00000000 --- a/mongo/src/main/scala/co.blocke.scalajack/mongo/typeadapter/ObjectIdTypeAdapter.scala +++ /dev/null @@ -1,28 +0,0 @@ -package co.blocke.scalajack -package mongo -package typeadapter - -import model._ - -import scala.collection.mutable -import org.bson.types.ObjectId -import org.bson._ -import co.blocke.scala_reflection.RType - -object ObjectIdTypeAdapter extends TypeAdapterFactory with TypeAdapter[ObjectId]: - - def matches(concrete: RType): Boolean = concrete == info - - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[_] = this - - val info = RType.of[ObjectId] - - def read(parser: Parser): ObjectId = - parser.asInstanceOf[BsonParser].expectObjectId() - - def write[WIRE](t: ObjectId, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - t match { - case null => out += new BsonNull().asInstanceOf[WIRE] - case _ => - out += (new BsonObjectId(t)).asInstanceOf[WIRE] - } \ No newline at end of file diff --git a/mongo/src/main/scala/co.blocke.scalajack/mongo/typeadapter/OffsetDateTimeTypeAdapter.scala b/mongo/src/main/scala/co.blocke.scalajack/mongo/typeadapter/OffsetDateTimeTypeAdapter.scala deleted file mode 100644 index 465bcd96..00000000 --- a/mongo/src/main/scala/co.blocke.scalajack/mongo/typeadapter/OffsetDateTimeTypeAdapter.scala +++ /dev/null @@ -1,35 +0,0 @@ -package co.blocke.scalajack -package mongo -package typeadapter - -import model._ - -import scala.collection.mutable -import org.bson._ -import java.time._ -import co.blocke.scala_reflection.RType - -object OffsetDateTimeTypeAdapter extends TypeAdapterFactory with TypeAdapter[OffsetDateTime]: - - def matches(concrete: RType): Boolean = concrete == info - - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[_] = this - - val info = RType.of[OffsetDateTime] - - def read(parser: Parser): OffsetDateTime = - parser.expectNumber(true) match { - case null => null - case dateTimeLong => - OffsetDateTime.ofInstant( - Instant.ofEpochMilli(dateTimeLong.toLong), - ZoneOffset.UTC - ) - } - - def write[WIRE](t: OffsetDateTime, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - t match { - case null => out += new BsonNull().asInstanceOf[WIRE] - case _ => - out += new BsonDateTime(t.toInstant.toEpochMilli).asInstanceOf[WIRE] - } diff --git a/mongo/src/main/scala/co.blocke.scalajack/mongo/typeadapter/StringWrapTypeAdapter.scala b/mongo/src/main/scala/co.blocke.scalajack/mongo/typeadapter/StringWrapTypeAdapter.scala deleted file mode 100644 index 801ffe66..00000000 --- a/mongo/src/main/scala/co.blocke.scalajack/mongo/typeadapter/StringWrapTypeAdapter.scala +++ /dev/null @@ -1,69 +0,0 @@ -package co.blocke.scalajack -package mongo -package typeadapter - -import model._ -import org.bson._ -import co.blocke.scalajack.typeadapter.AnyTypeAdapter -import co.blocke.scala_reflection.RType -import co.blocke.scala_reflection.impl.PrimitiveType -import co.blocke.scalajack.typeadapter._ - -import scala.collection.mutable - -// A TypeAdapter for a type T, which is wrapped in a String, a.k.a. "stringified". -// This is used for JSON Map keys, which must be strings. -case class StringWrapTypeAdapter[T](wrappedTypeAdapter: TypeAdapter[T]) extends TypeAdapter[T]: - - override def isStringish: Boolean = true - val info: RType = wrappedTypeAdapter.info - - def read(parser: Parser): T = - val wrappedValueString = parser.expectString() - wrappedTypeAdapter match { - case value: ScalarTypeAdapter[_] => - value.info match { - case PrimitiveType.Scala_Byte => wrappedValueString.toByte.asInstanceOf[T] - case PrimitiveType.Scala_Int => wrappedValueString.toInt.asInstanceOf[T] - case PrimitiveType.Scala_Long => wrappedValueString.toLong.asInstanceOf[T] - case PrimitiveType.Scala_Double => wrappedValueString.toDouble.asInstanceOf[T] - case PrimitiveType.Scala_Float => wrappedValueString.toFloat.asInstanceOf[T] - case PrimitiveType.Scala_Short => wrappedValueString.toShort.asInstanceOf[T] - case PrimitiveType.Scala_Boolean => wrappedValueString.toBoolean.asInstanceOf[T] - case PrimitiveType.Java_Number => wrappedValueString.toDouble.asInstanceOf[T] - case r: RType if r.name == "scala.math.BigInt" => BigInt(wrappedValueString).asInstanceOf[T] - case r: RType if r.name == "scala.math.BigDecimal" => BigDecimal(wrappedValueString).asInstanceOf[T] - // $COVERAGE-OFF$Currently all scalars in ScalaJack are supported. Here just in case... - case _ => - throw new ScalaJackError( - "Only Scala scalar values are supported as BSON Map keys" - ) - // $COVERAGE-ON$ - } - case value: AnyTypeAdapter => - value.read(parser).asInstanceOf[T] - case _ => - throw new ScalaJackError( - "Only scalar values are supported as BSON Map keys" - ) - } - - def write[WIRE]( - t: T, - writer: Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit = - val keyValBuilder = BsonBuilder().asInstanceOf[mutable.Builder[Any, WIRE]] - wrappedTypeAdapter.write(t, writer, keyValBuilder) - val result = keyValBuilder.result() match { - case r: BsonBoolean => r.asBoolean().getValue.toString - case r: BsonInt32 => r.asInt32().getValue.toString - case r: BsonInt64 => r.asInt64().getValue.toString - case r: BsonDecimal128 => r.asDecimal128().getValue.toString - case r: BsonDouble => r.asDouble().getValue.toString - case r: BsonNumber => r.asDecimal128().getValue.toString - case r => - throw new ScalaJackError( - "BSON type " + r.getClass.getName + " is not supported as a Map key" - ) - } - writer.writeString(result, out) diff --git a/mongo/src/main/scala/co.blocke.scalajack/mongo/typeadapter/ZonedDateTimeTypeAdapter.scala b/mongo/src/main/scala/co.blocke.scalajack/mongo/typeadapter/ZonedDateTimeTypeAdapter.scala deleted file mode 100644 index c8cfad5d..00000000 --- a/mongo/src/main/scala/co.blocke.scalajack/mongo/typeadapter/ZonedDateTimeTypeAdapter.scala +++ /dev/null @@ -1,38 +0,0 @@ -package co.blocke.scalajack -package mongo -package typeadapter - -import scala.collection.mutable -import org.bson._ -import java.time._ -import co.blocke.scala_reflection.RType - -import model._ - -object ZonedDateTimeTypeAdapter extends TypeAdapterFactory with TypeAdapter[ZonedDateTime]: - - def matches(concrete: RType): Boolean = concrete == info - - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[_] = this - - val info = RType.of[ZonedDateTime] - - def read(parser: Parser): ZonedDateTime = - parser.expectNumber(true) match { - case null => null - case dateTimeLong => - ZonedDateTime.ofInstant( - Instant.ofEpochMilli(dateTimeLong.toLong), - ZoneId.of("UTC") - ) - } - - def write[WIRE](t: ZonedDateTime, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - t match { - case null => - out += new BsonNull().asInstanceOf[WIRE] - case _ => - out += new BsonDateTime( - t.withZoneSameInstant(ZoneId.of("UTC")).toInstant.toEpochMilli - ).asInstanceOf[WIRE] - } diff --git a/mongo/src/test/scala/co.blocke.scalajack/JsonDiff.scala b/mongo/src/test/scala/co.blocke.scalajack/JsonDiff.scala deleted file mode 100644 index 6ab1886a..00000000 --- a/mongo/src/test/scala/co.blocke.scalajack/JsonDiff.scala +++ /dev/null @@ -1,58 +0,0 @@ -package co.blocke.scalajack -package mongo - -import org.json4s.JsonAST.{ JNothing, JObject, JValue } - -object JsonDiff { - - def compare( - left: JValue, - right: JValue, - leftLabel: String = "left", - rightLabel: String = "right"): Seq[JsonDiff] = { - (left, right) match { - case (JObject(leftFields), JObject(rightFields)) => - val allFieldNames = - (leftFields.map(_._1) ++ rightFields.map(_._1)).distinct - allFieldNames.sorted flatMap { fieldName => - val leftFieldValue = leftFields - .collectFirst({ case (`fieldName`, fieldValue) => fieldValue }) - .getOrElse(JNothing) - val rightFieldValue = rightFields - .collectFirst({ case (`fieldName`, fieldValue) => fieldValue }) - .getOrElse(JNothing) - compare(leftFieldValue, rightFieldValue, leftLabel, rightLabel) - } - - // ---- Not used/needed at present, and I have questions about the correct behavior here. Exactly how do you - // "diff" two arrays (not necessarily homogeneous typed)? - // - // case (JArray(leftElements), JArray(rightElements)) => - // (0 until (leftElements.size max rightElements.size)) flatMap { elementIndex => - // val leftElement = leftElements.applyOrElse(elementIndex, (_: Int) => JNothing) - // val rightElement = rightElements.applyOrElse(elementIndex, (_: Int) => JNothing) - // compare(path \ elementIndex, leftElement, rightElement, leftLabel, rightLabel) - // } - - case _ => - if (left == right) { - Seq.empty - } else { - val outerLeft = left - val outerRight = right - Seq(new JsonDiff { - override val left: JValue = outerLeft - override val right: JValue = outerRight - override def toString: String = - s"JsonDiff($leftLabel: $left, $rightLabel: $right)" - }) - } - } - } - -} - -trait JsonDiff { - val left: JValue - val right: JValue -} diff --git a/mongo/src/test/scala/co.blocke.scalajack/JsonMatcher.scala b/mongo/src/test/scala/co.blocke.scalajack/JsonMatcher.scala deleted file mode 100644 index 4a483d64..00000000 --- a/mongo/src/test/scala/co.blocke.scalajack/JsonMatcher.scala +++ /dev/null @@ -1,21 +0,0 @@ -package co.blocke.scalajack -package mongo - -import co.blocke.scalajack.json.JSON -import org.json4s.JsonAST.JValue -import org.json4s.native.JsonMethods._ -import org.json4s.string2JsonInput - -object JsonMatcher { - - def jsonMatches( expected: JSON, actual: JSON ): Boolean = - val diffs = JsonDiff.compare( - parseJValue(expected.asInstanceOf[String]), - parseJValue(actual.asInstanceOf[String]), - "expected", - "actual" - ) - diffs.isEmpty - - implicit def parseJValue(string: String): JValue = parse(string) -} diff --git a/mongo/src/test/scala/co.blocke.scalajack/TestUtil.scala b/mongo/src/test/scala/co.blocke.scalajack/TestUtil.scala deleted file mode 100644 index c8365bdf..00000000 --- a/mongo/src/test/scala/co.blocke.scalajack/TestUtil.scala +++ /dev/null @@ -1,42 +0,0 @@ -package co.blocke.scalajack - -import munit.internal.console - -object TestUtil { - - inline def describe(message: String, color: String = Console.MAGENTA): Unit = println(s"$color$message${Console.RESET}") - inline def pending = describe(" << Test Pending (below) >>", Console.YELLOW) - - def hexStringToByteArray(s: String): Array[Byte] = { - val len = s.length - val data = new Array[Byte](len / 2) - var i = 0 - while ({ - i < len - }) { - data(i / 2) = ((Character.digit(s.charAt(i), 16) << 4) + Character.digit( - s.charAt(i + 1), - 16 - )).toByte - - i += 2 - } - data - } - - // Utility to generate test code quickly - def showException(label: String, fnStr: String, fn: () => Any) = - try { - fn() - } catch { - case x: IndexOutOfBoundsException => throw x - case t: Throwable => - if (!t.getMessage.contains("\n")) - throw t - val msg = "\"\"\"" + t.getMessage().replace("\n", "\n |") + "\"\"\"" - println( - label + " >> " + t.getClass.getName + "\n-----------------------\n" + - s"val msg = $msg.stripMargin\nthe[${t.getClass.getName}] thrownBy $fnStr should have message msg\n" - ) - } -} \ No newline at end of file diff --git a/mongo/src/test/scala/co.blocke.scalajack/mongo/AnySpec.scala b/mongo/src/test/scala/co.blocke.scalajack/mongo/AnySpec.scala deleted file mode 100644 index 6a422652..00000000 --- a/mongo/src/test/scala/co.blocke.scalajack/mongo/AnySpec.scala +++ /dev/null @@ -1,209 +0,0 @@ -package co.blocke.scalajack -package mongo - -import TestUtil._ -import munit._ -import munit.internal.console -import org.bson._ -import scala.jdk.CollectionConverters._ - -case class Something(name: String, stuff: Map[String, Any]) - -class AnySpec extends FunSuite: - - val sjM = ScalaJack(MongoFlavor()) - - object MongoMaster { - val a = new BsonDocument( - List( - new BsonElement("name", new BsonString("Fred")), - new BsonElement( - "stuff", - new BsonDocument( - List( - new BsonElement("a", new BsonInt32(1)), - new BsonElement("b", new BsonBoolean(true)) - ).asJava - ) - ) - ).asJava - ) - - val b = new BsonDocument( - List( - new BsonElement("name", new BsonString("Fred")), - new BsonElement( - "stuff", - new BsonDocument( - List( - new BsonElement("a", new BsonInt32(1)), - new BsonElement( - "b", - new BsonArray( - List(new BsonInt32(4), new BsonInt32(5), new BsonInt32(6)).asJava - ) - ) - ).asJava - ) - ) - ).asJava - ) - - val c = new BsonDocument( - List( - new BsonElement("name", new BsonString("Fred")), - new BsonElement( - "stuff", - new BsonDocument( - List( - new BsonElement("a", new BsonInt32(1)), - new BsonElement( - "b", - new BsonArray( - List( - new BsonDocument( - List( - new BsonElement("x", new BsonString("Fido")), - new BsonElement("y", new BsonBoolean(false)) - ).asJava - ), - new BsonDocument( - List( - new BsonElement("x", new BsonString("Cat")), - new BsonElement("y", new BsonBoolean(true)) - ).asJava - ) - ).asJava - ) - ) - ).asJava - ) - ) - ).asJava - ) - - val e = new BsonDocument( - List( - new BsonElement("name", new BsonString("Fred")), - new BsonElement( - "stuff", - new BsonDocument( - List( - new BsonElement("a", new BsonInt32(1)), - new BsonElement( - "b", - new BsonArray( - List( - new BsonString("foo"), - new BsonNull(), - new BsonString("bar") - ).asJava - ) - ) - ).asJava - ) - ) - ).asJava - ) - - val f = new BsonDocument( - List( - new BsonElement("name", new BsonString("Fred")), - new BsonElement( - "stuff", - new BsonDocument( - List( - new BsonElement("a", new BsonInt32(1)), - new BsonElement("b", new BsonDouble(1.23)) - ).asJava - ) - ) - ).asJava - ) - - val g = new BsonDocument( - List( - new BsonElement("name", new BsonString("Fred")), - new BsonElement( - "stuff", - new BsonDocument( - List( - new BsonElement("a", new BsonInt32(1)), - new BsonElement("b", new BsonInt64(25L)) - ).asJava - ) - ) - ).asJava - ) - } - - object ScalaMaster { - val a = Something("Fred", Map("a" -> 1, "b" -> true)) - val b = Something("Fred", Map("a" -> 1, "b" -> List(4, 5, 6))) - val c = Something( - "Fred", - Map( - "a" -> 1, - "b" -> List( - Map("x" -> "Fido", "y" -> false), - Map("x" -> "Cat", "y" -> true) - ) - ) - ) - val e = Something("Fred", Map("a" -> 1, "b" -> List("foo", null, "bar"))) - val f = Something("Fred", Map("a" -> 1, "b" -> 1.23)) - val g = Something("Fred", Map("a" -> 1, "b" -> 25L)) - } - - test("Any 1") { - describe( - "-------------------------\n: Any Tests (MongoDB) :\n-------------------------", Console.BLUE - ) - describe("Render Tests") - assertEquals(sjM.render(ScalaMaster.a), MongoMaster.a) - } - - test("Any 2") { - assertEquals(sjM.render(ScalaMaster.b), MongoMaster.b) - } - - test("Any 3") { - assertEquals(sjM.render(ScalaMaster.c), MongoMaster.c) - } - - test("Any 4") { - assertEquals(sjM.render(ScalaMaster.e), MongoMaster.e) - } - - test("Any 5") { - assertEquals(sjM.render(ScalaMaster.f), MongoMaster.f) - } - - test("Any 6") { - assertEquals(sjM.render(ScalaMaster.g), MongoMaster.g) - } - - test("Any 1") { - describe("Read Tests") - assertEquals(sjM.read[Something](MongoMaster.a), ScalaMaster.a) - } - - test("Any 2") { - assertEquals(sjM.read[Something](MongoMaster.b), ScalaMaster.b) - } - - test("Any 3") { - assertEquals(sjM.read[Something](MongoMaster.c), ScalaMaster.c) - } - - test("Any 4") { - assertEquals(sjM.read[Something](MongoMaster.e), ScalaMaster.e) - } - - test("Any 5") { - assertEquals(sjM.read[Something](MongoMaster.f), ScalaMaster.f) - } - - test("Any 6") { - assertEquals(sjM.read[Something](MongoMaster.g), ScalaMaster.g) - } diff --git a/mongo/src/test/scala/co.blocke.scalajack/mongo/ConverterSpec.scala b/mongo/src/test/scala/co.blocke.scalajack/mongo/ConverterSpec.scala deleted file mode 100644 index a74d5897..00000000 --- a/mongo/src/test/scala/co.blocke.scalajack/mongo/ConverterSpec.scala +++ /dev/null @@ -1,134 +0,0 @@ -package co.blocke.scalajack -package mongo - -import co.blocke.scalajack.json.JsonFlavor -import co.blocke.scalajack.model.JackFlavor -import TestUtil._ -import munit._ -import munit.internal.console - -import yaml._ -import json._ -import json4s._ -import delimited._ -import Converters._ -import mongo.Converters._ -import org.json4s._ -import org.bson._ -import scala.jdk.CollectionConverters._ - -trait HumanM -case class PersonM(@DBKey name: String, age: Int) extends HumanM -case class TypeyM[T](thing: T) { - type foom = T -} - -class ConverterSpec extends FunSuite: - - implicit val sj: JsonFlavor = ScalaJack() - implicit val sjY: JackFlavor[YAML] = ScalaJack(YamlFlavor()) - implicit val sjV: JackFlavor[JValue] = ScalaJack(Json4sFlavor()) - implicit val sjD: JackFlavor[DELIMITED] = ScalaJack(DelimitedFlavor()) - implicit val sjB: JackFlavor[BsonValue] = ScalaJack(MongoFlavor()) - - val simple: PersonM = PersonM("Fred", 34) - val complex: TypeyM[HumanM] = TypeyM[HumanM](PersonM("Fred", 34)) - val simpleJson = """{"name":"Fred","age":34}""".asInstanceOf[JSON] - val complexJson = """{"foom":"co.blocke.scalajack.mongo.PersonM","thing":{"name":"Fred","age":34}}""".asInstanceOf[JSON] - val simpleJson4s = JObject(List(("name", JString("Fred")), ("age", JInt(34)))) - val complexJson4s = JObject( - List( - ("foom", JString("co.blocke.scalajack.mongo.PersonM")), - ("thing", JObject(List(("name", JString("Fred")), ("age", JInt(34))))) - )) - val simpleYaml = """name: Fred - |age: 34 - |""".stripMargin.asInstanceOf[YAML] - val complexYaml = """foom: co.blocke.scalajack.mongo.PersonM - |thing: - | name: Fred - | age: 34 - |""".stripMargin.asInstanceOf[YAML] - val simpleDelimited = "Fred,34".asInstanceOf[DELIMITED] - val simpleMongo = new BsonDocument( - List( - new BsonElement("_id", new BsonString("Fred")), - new BsonElement("age", new BsonInt32(34)) - ).asJava - ) - val complexMongo = new BsonDocument( - List( - new BsonElement("foom", new BsonString("co.blocke.scalajack.mongo.PersonM")), - new BsonElement("thing", - new BsonDocument( - List( - new BsonElement("_id", new BsonString("Fred")), - new BsonElement("age", new BsonInt32(34)) - ).asJava - )) - ).asJava - ) - - test("mapMongo") { - describe( - "-------------------------------------\n: Converter Mapping Tests (Mongo) :\n-------------------------------------", Console.BLUE - ) - assertEquals(simpleMongo.mapMongo[PersonM](_.copy(age = 45)), new BsonDocument( - List( - new BsonElement("_id", new BsonString("Fred")), - new BsonElement("age", new BsonInt32(45)) - ).asJava - )) - } - - test("mapMongoTo") { - assertEquals(simpleMongo.mapMongoTo[PersonM, YAML](sjY)(_.copy(age = 45)), """name: Fred - |age: 45 - |""".stripMargin.asInstanceOf[YAML]) - } - - test("toMongo") { - describe( - "-----------------------------------------\n: Convenience \"to/from\" Tests (Mongo) :\n-----------------------------------------", Console.BLUE - ) - assertEquals(toMongo[PersonM](simple), simpleMongo) - assertEquals(toMongo[TypeyM[HumanM]](complex), complexMongo) - } - - test("fromMongo") { - assertEquals(simpleMongo.fromMongo[PersonM], simple) - assertEquals(complexMongo.fromMongo[TypeyM[HumanM]], complex) - } - - test("mongoToJson") { - describe( - "------------------------------\n: Converters Tests (Mongo) :\n------------------------------", Console.BLUE - ) - assertEquals(simpleMongo.mongoToJson[PersonM], simpleJson) - assertEquals(complexMongo.mongoToJson[TypeyM[HumanM]], complexJson) - } - - test("mongoToJson4s") { - assertEquals(simpleMongo.mongoToJson4s[PersonM], simpleJson4s) - assertEquals(complexMongo.mongoToJson4s[TypeyM[HumanM]], complexJson4s) - } - - test("mongoToYaml") { - assertEquals(simpleMongo.mongoToYaml[PersonM], simpleYaml) - assertEquals(complexMongo.mongoToYaml[TypeyM[HumanM]], complexYaml) - } - - test("jsonToMongo") { - assertEquals(simpleJson.jsonToMongo[PersonM], simpleMongo) - assertEquals(complexJson.jsonToMongo[TypeyM[HumanM]], complexMongo) - } - - test("json4sToMongo") { - assertEquals(simpleJson4s.json4sToMongo[PersonM], simpleMongo) - assertEquals(complexJson4s.json4sToMongo[TypeyM[HumanM]], complexMongo) - } - - test("yamlToMongo") { - assertEquals(simpleJson4s.json4sToMongo[PersonM], simpleMongo) - assertEquals(complexJson4s.json4sToMongo[TypeyM[HumanM]], complexMongo) - } \ No newline at end of file diff --git a/mongo/src/test/scala/co.blocke.scalajack/mongo/Custom.scala b/mongo/src/test/scala/co.blocke.scalajack/mongo/Custom.scala deleted file mode 100644 index 21411e83..00000000 --- a/mongo/src/test/scala/co.blocke.scalajack/mongo/Custom.scala +++ /dev/null @@ -1,159 +0,0 @@ -package co.blocke.scalajack -package mongo - -import TestUtil._ -import munit._ -import munit.internal.console -import org.bson._ -import scala.jdk.CollectionConverters._ -import co.blocke.scala_reflection.RType - -class Custom extends FunSuite: - - test("Supports withAdapters") { - describe( - "----------------------------\n: Custom Tests (MongoDB) :\n----------------------------", Console.BLUE - ) - val sj = ScalaJack(MongoFlavor()).withAdapters(PhoneAdapter) - val dbo = new BsonDocument( - List( - new BsonElement("_id", new BsonString("Fred")), - new BsonElement("phone", new BsonString("123-456-7890")) - ).asJava - ) - assertEquals(dbo.toJson, """{"_id": "Fred", "phone": "123-456-7890"}""") - val read = sj.read[Person](dbo) - assertEquals(read, Person("Fred", "1234567890".asInstanceOf[Phone])) - assertEquals(sj.render(read), dbo) - } - - test("Supports withHints") { - val sj = ScalaJack(MongoFlavor()).withHints( - RType.of[Address] -> "addr_kind", - RType.of[Demographic] -> "demo" - ) - val dbo = new BsonDocument( - List( - new BsonElement( - "demo", - new BsonString("co.blocke.scalajack.mongo.USDemographic") - ), - new BsonElement("_id", new BsonString("34")), - new BsonElement( - "address", - new BsonDocument( - List( - new BsonElement( - "addr_kind", - new BsonString("co.blocke.scalajack.mongo.USAddress") - ), - new BsonElement("street", new BsonString("123 Main")), - new BsonElement("city", new BsonString("New York")), - new BsonElement("state", new BsonString("NY")), - new BsonElement("postalCode", new BsonString("39822")) - ).asJava - ) - ) - ).asJava - ) - assertEquals(dbo.toJson, - """{"demo": "co.blocke.scalajack.mongo.USDemographic", "_id": "34", "address": {"addr_kind": "co.blocke.scalajack.mongo.USAddress", "street": "123 Main", "city": "New York", "state": "NY", "postalCode": "39822"}}""" - ) - val read = sj.read[Demographic](dbo) - assertEquals(read, USDemographic("34", USAddress("123 Main", "New York", "NY", "39822"))) - assertEquals(sj.render(read), dbo) - } - - // withHintModifiers tested in another case - - test("Supports withDefaultHint") { - val sj = ScalaJack(MongoFlavor()).withDefaultHint("kind") - val dbo = new BsonDocument( - List( - new BsonElement( - "kind", - new BsonString("co.blocke.scalajack.mongo.USDemographic") - ), - new BsonElement("_id", new BsonString("34")), - new BsonElement( - "address", - new BsonDocument( - List( - new BsonElement( - "kind", - new BsonString("co.blocke.scalajack.mongo.USAddress") - ), - new BsonElement("street", new BsonString("123 Main")), - new BsonElement("city", new BsonString("New York")), - new BsonElement("state", new BsonString("NY")), - new BsonElement("postalCode", new BsonString("39822")) - ).asJava - ) - ) - ).asJava - ) - assertEquals(dbo.toJson, - """{"kind": "co.blocke.scalajack.mongo.USDemographic", "_id": "34", "address": {"kind": "co.blocke.scalajack.mongo.USAddress", "street": "123 Main", "city": "New York", "state": "NY", "postalCode": "39822"}}""" - ) - val read = sj.read[Demographic](dbo) - assertEquals(read, USDemographic("34", USAddress("123 Main", "New York", "NY", "39822")) ) - assertEquals(sj.render(read), dbo) - } - - test( - "Provide a default object if the object specified in the type hint is unknown (parseOrElse)" - ) { - val sj = ScalaJack(MongoFlavor()) - .parseOrElse((RType.of[Address] -> RType.of[DefaultAddress])) - val dbo = new BsonDocument( - List( - new BsonElement( - "_hint", - new BsonString("co.blocke.scalajack.mongo.USDemographic") - ), - new BsonElement("_id", new BsonString("34")), - new BsonElement( - "address", - new BsonDocument( - List( - new BsonElement( - "_hint", - new BsonString("co.blocke.scalajack.mongo.UnknownAddress") - ), - new BsonElement("street", new BsonString("123 Main")), - new BsonElement("city", new BsonString("New York")), - new BsonElement("state", new BsonString("NY")), - new BsonElement("postalCode", new BsonString("39822")) - ).asJava - ) - ) - ).asJava - ) - val dboDefault = new BsonDocument( - List( - new BsonElement( - "_hint", - new BsonString("co.blocke.scalajack.mongo.USDemographic") - ), - new BsonElement("_id", new BsonString("34")), - new BsonElement( - "address", - new BsonDocument( - List( - new BsonElement( - "_hint", - new BsonString("co.blocke.scalajack.mongo.DefaultAddress") - ), - new BsonElement("postalCode", new BsonString("39822")) - ).asJava - ) - ) - ).asJava - ) - assertEquals(dbo.toJson, - """{"_hint": "co.blocke.scalajack.mongo.USDemographic", "_id": "34", "address": {"_hint": "co.blocke.scalajack.mongo.UnknownAddress", "street": "123 Main", "city": "New York", "state": "NY", "postalCode": "39822"}}""" - ) - val read = sj.read[Demographic](dbo) - assertEquals(read, USDemographic("34", DefaultAddress("39822"))) - assertEquals(sj.render(read), dboDefault) - } diff --git a/mongo/src/test/scala/co.blocke.scalajack/mongo/LooseChange.scala b/mongo/src/test/scala/co.blocke.scalajack/mongo/LooseChange.scala deleted file mode 100644 index 9d4b8493..00000000 --- a/mongo/src/test/scala/co.blocke.scalajack/mongo/LooseChange.scala +++ /dev/null @@ -1,376 +0,0 @@ -package co.blocke.scalajack -package mongo - -import co.blocke.scalajack.model.JackFlavor -import TestUtil._ -import munit._ -import munit.internal.console -import org.bson._ -import JsonMatcher._ -import java.time.{ OffsetDateTime, ZonedDateTime } - -import scala.jdk.CollectionConverters._ - -case class MyNumbers(d: BigDecimal, n: Number) -case class NumberBoom(x: BigInt) - -class LooseChange extends FunSuite: - val sjM: JackFlavor[BsonValue] = ScalaJack(MongoFlavor()) - - object MongoMaster { - val a = new BsonDocument( - List( - new BsonElement("name", new BsonString("Fred")), - new BsonElement( - "stuff", - new BsonDocument( - List( - new BsonElement("a", new BsonInt32(1)), - new BsonElement("b", new BsonBoolean(true)) - ).asJava - ) - ) - ).asJava - ) - - val b = new BsonDocument( - List( - new BsonElement("name", new BsonString("Fred")), - new BsonElement( - "stuff", - new BsonDocument( - List( - new BsonElement("a", new BsonInt32(1)), - new BsonElement( - "b", - new BsonArray( - List(new BsonInt32(4), new BsonInt32(5), new BsonInt32(6)).asJava - ) - ) - ).asJava - ) - ) - ).asJava - ) - - val c = new BsonDocument( - List( - new BsonElement("name", new BsonString("Fred")), - new BsonElement( - "stuff", - new BsonDocument( - List( - new BsonElement("a", new BsonInt32(1)), - new BsonElement( - "b", - new BsonArray( - List( - new BsonDocument( - List( - new BsonElement("x", new BsonString("Fido")), - new BsonElement("y", new BsonBoolean(false)) - ).asJava - ), - new BsonDocument( - List( - new BsonElement("x", new BsonString("Cat")), - new BsonElement("y", new BsonBoolean(true)) - ).asJava - ) - ).asJava - ) - ) - ).asJava - ) - ) - ).asJava - ) - - val e = new BsonDocument( - List( - new BsonElement("name", new BsonString("Fred")), - new BsonElement( - "stuff", - new BsonDocument( - List( - new BsonElement("a", new BsonInt32(1)), - new BsonElement( - "b", - new BsonArray( - List( - new BsonString("foo"), - new BsonNull(), - new BsonString("bar") - ).asJava - ) - ) - ).asJava - ) - ) - ).asJava - ) - - val f = new BsonDocument( - List( - new BsonElement("name", new BsonString("Fred")), - new BsonElement( - "stuff", - new BsonDocument( - List( - new BsonElement("a", new BsonInt32(1)), - new BsonElement("b", new BsonDouble(1.23)) - ).asJava - ) - ) - ).asJava - ) - - val g = new BsonDocument( - List( - new BsonElement("name", new BsonString("Fred")), - new BsonElement( - "stuff", - new BsonDocument( - List( - new BsonElement("a", new BsonInt32(1)), - new BsonElement("b", new BsonInt64(25L)) - ).asJava - ) - ) - ).asJava - ) - } - - object ScalaMaster { - val a: Something = Something("Fred", Map("a" -> 1, "b" -> true)) - val b: Something = Something("Fred", Map("a" -> 1, "b" -> List(4, 5, 6))) - val c: Something = Something( - "Fred", - Map( - "a" -> 1, - "b" -> List( - Map("x" -> "Fido", "y" -> false), - Map("x" -> "Cat", "y" -> true) - ) - ) - ) - val e: Something = - Something("Fred", Map("a" -> 1, "b" -> List("foo", null, "bar"))) - val f: Something = Something("Fred", Map("a" -> 1, "b" -> 1.23)) - val g: Something = Something("Fred", Map("a" -> 1, "b" -> 25L)) - } - - test("Handles null value") { - describe( - "----------------------------\n: Loose Change (MongoDB) :\n----------------------------" - ) - val dbo = new BsonDocument( - List( - new BsonElement("name", new BsonString("Fred")), - new BsonElement( - "stuff", - new BsonDocument( - List( - new BsonElement("a", new BsonInt32(1)), - new BsonElement("b", new BsonInt32(15)) - ).asJava - ) - ) - ).asJava - ) - assertEquals(sjM.read[Something](dbo), - Something("Fred", Map("a" -> 1, "b" -> 15)) - ) - } - - test("Should blow up for unsupported BSON type") { - val dbo = new BsonDocument( - List( - new BsonElement("name", new BsonString("Fred")), - new BsonElement( - "stuff", - new BsonDocument( - List(new BsonElement("a", new BsonJavaScript("code here"))).asJava - ) - ) - ).asJava - ) - interceptMessage[ScalaJackError]("""BSON type org.bson.BsonJavaScript is not currently supported in ScalaJack."""){ - sjM.read[Something](dbo) - } - } - - test("Field name remapping must work") { - val mfp = MapFactor("wonder", 25L, 3, "hungry") - val dbo = sjM.render(mfp) - assertEquals(dbo.asDocument.toJson, - """{"foo_bar": "wonder", "a_b": {"$numberLong": "25"}, "count": 3, "big_mac": "hungry"}""" - ) - assertEquals(sjM.read[MapFactor](dbo), mfp) - } - - test("Field name remapping on dbkey must work") { - // val mfp = MapFactorId2("wonder", 25L, 1, 3) - val mfp = MapFactorId("wonder", 25L, 3, "hungry") - val dbo = sjM.render(mfp) - assertEquals(dbo.asDocument.toJson, - """{"_id": "wonder", "a_b": {"$numberLong": "25"}, "count": 3, "big_mac": "hungry"}""" - ) - assertEquals(sjM.read[MapFactorId](dbo), mfp) - } - - test("Field name remapping on dbkey with multi-part keys must work") { - val mfp = MapFactorId2("wonder", 25L, 1, 3, "hungry") - val dbo = sjM.render(mfp) - assert(jsonMatches( - dbo.asDocument.toJson.asInstanceOf[co.blocke.scalajack.json.JSON], - """{"_id": {"foo_bar": "wonder", "a_b": {"$numberLong": "25"}, "hey": 1}, "count": 3, "big_mac": "hungry"}""".asInstanceOf[co.blocke.scalajack.json.JSON] - )) - assertEquals(sjM.read[MapFactorId2](dbo), mfp) - } - - test("Number, Decimal, and BigInt handling") { - val my = MyNumbers(BigDecimal(123.45), 15) - val d = sjM.render(my) - assertEquals(d.asDocument.toJson, - """{"d": {"$numberDecimal": "123.45"}, "n": 15}""" - ) - assertEquals(sjM.read[MyNumbers](d), my) - - val boom = NumberBoom(BigInt(5)) - interceptMessage[ScalaJackError]("BigInt is currently an unsupported datatype for MongoDB serialization"){ - sjM.render(boom) - } - } - - test("Nulls") { - val prim = PrimitiveLists(null, null, null, null, null) - assertEquals(sjM.read[PrimitiveLists](sjM.render(prim)), prim) - - val os = OneSub2("foo", flipflop = false, null) - interceptMessage[ScalaJackError]("Class co.blocke.scalajack.mongo.PrimitiveLists missing required fields: bools, chars, doubles, ints, longs"){ - sjM.read[PrimitiveLists](sjM.render(os)) - } - - val os2 = - OneSub2("foo", flipflop = false, Map(null.asInstanceOf[String] -> 5)) - interceptMessage[ScalaJackError]("Map keys cannot be null."){ - sjM.render(os2) - } - - val out = null - assertEquals(sjM.render[OneSub2](out).isNull, true) - - val mapDoc = new BsonDocument( - List( - new BsonElement("i", new BsonInt32(5)), - new BsonElement("items", new BsonNull()) - ).asJava - ) - assertEquals(sjM.read[BagMap[Int]](mapDoc), BagMap(5, null)) - - assertEquals(sjM.read[OneSub2](null), null) - - val times = Times(null, null) - val d = sjM.render(times) - assertEquals(d.asDocument().toJson, """{"offset": null, "zoned": null}""") - assertEquals(sjM.read[Times](d), times) - val d2 = new BsonDocument( - List( - new BsonElement("offset", new BsonNull()), - new BsonElement("zoned", new BsonNull()) - ).asJava - ) - assertEquals(sjM.read[Times](d2), times) - } - - test("Good time values") { - val t = Times( - OffsetDateTime.parse("2007-12-03T10:15:30+01:00"), - ZonedDateTime.parse("2007-12-03T10:15:30Z[UTC]") - ) - val d = sjM.render(t) - val t2 = sjM.read[Times](d) - // Extra gymnastics because the read/rendered OffsetDateTime, while the same actual instant, isn't the same value so comparison fails - assert(t2.offset.atZoneSameInstant(java.time.ZoneId.of("Europe/Paris")) == t.offset.atZoneSameInstant(java.time.ZoneId.of("Europe/Paris"))) - assertEquals(t2.zoned == t.zoned, true) - } - - test("Bad expected values") { - val d = new BsonDocument( - List( - new BsonElement("name", new BsonString("Fred")), - new BsonElement("big", new BsonDouble(123.45)) - ).asJava - ) - interceptMessage[ScalaJackError]("Cannot parse an Long from value"){ - sjM.read[OneSub1](d) - } - } - - test("Non-hint hint-labeled field") { - val d = new BsonDocument( - List( - new BsonElement("num", new BsonInt32(3)), - new BsonElement( - "s", - new BsonDocument( - List( - new BsonElement("_hint", new BsonInt32(45)), - new BsonElement("size", new BsonInt32(34)) - ).asJava - ) - ) - ).asJava - ) - interceptMessage[ScalaJackError]("Hint value _hint must be a string value"){ - sjM.read[StrangeWrapper](d) - } - } - - test("Can't find trait hint") { - val d = new BsonDocument( - List( - new BsonElement("num", new BsonInt32(3)), - new BsonElement( - "s", - new BsonDocument( - List(new BsonElement("size", new BsonInt32(34))).asJava - ) - ) - ).asJava - ) - interceptMessage[ScalaJackError]("Type hint '_hint' not found"){ - sjM.read[StrangeWrapper](d) - } - } - - test("Enums as ints work") { - val sj = ScalaJack(MongoFlavor()).enumsAsInts() - val n = Numy(5, Num.B) - val d = sj.render(n) - assertEquals(d.asDocument.toJson, """{"age": 5, "num": 1}""") - assertEquals(sj.read[Numy](d), n) - } - - test("Failure due to empty BsonBuilder") { - interceptMessage[ScalaJackError]("No value set for internal mongo builder"){ - BsonBuilder().result() - } - } - - test("Parse only") { - val d = new BsonDocument( - List( - new BsonElement("num", new BsonInt32(3)), - new BsonElement( - "s", - new BsonDocument( - List(new BsonElement("size", new BsonInt32(34))).asJava - ) - ) - ).asJava - ) - val p = sjM.parse(d) - assertEquals(p.isInstanceOf[BsonParser], true) - } \ No newline at end of file diff --git a/mongo/src/test/scala/co.blocke.scalajack/mongo/MapKeys.scala b/mongo/src/test/scala/co.blocke.scalajack/mongo/MapKeys.scala deleted file mode 100644 index 827bd4b6..00000000 --- a/mongo/src/test/scala/co.blocke.scalajack/mongo/MapKeys.scala +++ /dev/null @@ -1,96 +0,0 @@ -package co.blocke.scalajack -package mongo - -import TestUtil._ -import munit._ -import munit.internal.console -import java.util.UUID -import org.bson.BsonDocument - -case class IntKey(m: Map[Int, Int]) -case class LongKey(m: Map[Long, Int]) -case class DoubleKey(m: Map[Double, Int]) -case class FloatKey(m: Map[Float, Int]) -case class ShortKey(m: Map[Short, Int]) -case class BigDecimalKey(m: Map[BigDecimal, Int]) -case class ByteKey(m: Map[Byte, Int]) -case class BooleanKey(m: Map[Boolean, Int]) -case class CharKey(m: Map[Char, Int]) -case class UUIDKey(m: Map[UUID, Int]) -case class NumberKey(m: Map[java.lang.Number, Int]) - -case class WithMap(m: Map[Map[Int, Int], Int]) - -class MapKeys extends FunSuite: - - val sj = ScalaJack(MongoFlavor()) - - test("Renders wrapped non-String scalar Map keys") { - describe( - "------------------------\n: Map Keys (MongoDB) :\n------------------------", Console.BLUE - ) - val short: Short = 5 - val bte: Byte = 5 - val a = IntKey(Map(5 -> 2)) - val b = LongKey(Map(5L -> 2)) - val c = DoubleKey(Map(5.3 -> 2)) - val d = FloatKey(Map(5F -> 2)) - val e = ShortKey(Map(short -> 2)) - val f = BigDecimalKey(Map(BigDecimal(5.1) -> 2)) - val g = ByteKey(Map(bte -> 2)) - val h = BooleanKey(Map(true -> 2)) - val i = CharKey(Map('c' -> 2)) - val j = NumberKey(Map(5.asInstanceOf[Number] -> 2)) - - val docs = List( - sj.render(a), - sj.render(b), - sj.render(c), - sj.render(d), - sj.render(e), - sj.render(f), - sj.render(g), - sj.render(h), - sj.render(i), - sj.render(j), - ) - assertEquals(docs.map(_.asDocument.toJson), - List( - """{"m": {"5": 2}}""", - """{"m": {"5": 2}}""", - """{"m": {"5.3": 2}}""", - """{"m": {"5.0": 2}}""", - """{"m": {"5": 2}}""", - """{"m": {"5.1": 2}}""", - """{"m": {"5": 2}}""", - """{"m": {"true": 2}}""", - """{"m": {"c": 2}}""", - """{"m": {"5": 2}}""" - ) - ) - - assertEquals(sj.read[IntKey](docs(0)), a) - assertEquals(sj.read[LongKey](docs(1)), b) - assertEquals(sj.read[DoubleKey](docs(2)), c) - assertEquals(sj.read[FloatKey](docs(3)), d) - assertEquals(sj.read[ShortKey](docs(4)), e) - assertEquals(sj.read[BigDecimalKey](docs(5)), f) - assertEquals(sj.read[ByteKey](docs(6)), g) - assertEquals(sj.read[BooleanKey](docs(7)), h) - assertEquals(sj.read[CharKey](docs(8)), i) - assertEquals(sj.read[NumberKey](docs(9)), j) - } - - test("Traps attempt to read non-scalar Map key") { - val d = BsonDocument.parse("""{"m":{"bogus":5}}""") - interceptMessage[ScalaJackError]("Only scalar values are supported as BSON Map keys"){ - sj.read[WithMap](d) - } - } - - test("Traps attempt to write non-scalar Map key") { - val m = WithMap(Map(Map(1 -> 2) -> 3)) - interceptMessage[ScalaJackError]("BSON type org.bson.BsonDocument is not supported as a Map key"){ - sj.render(m) - } - } diff --git a/mongo/src/test/scala/co.blocke.scalajack/mongo/Model.scala b/mongo/src/test/scala/co.blocke.scalajack/mongo/Model.scala deleted file mode 100644 index 522a9248..00000000 --- a/mongo/src/test/scala/co.blocke.scalajack/mongo/Model.scala +++ /dev/null @@ -1,272 +0,0 @@ -package co.blocke.scalajack -package mongo - -import java.time._ - -import model._ -import org.bson.types.ObjectId - -import scala.collection.mutable -import scala.util.Try -import co.blocke.scala_reflection.RType -import co.blocke.scala_reflection.info.AliasInfo - -object Num extends Enumeration { - val A, B, C = Value -} - -case class Bar[A, B](a: A, b: B) -case class Zoo[U](name: String, z: U) //stuff:Bar[U,String]) -case class Hey(age: Int) - -case class Wrap[T, U](name: String, data: T, stuff: U) -case class Carry[V](s: String, w: Wrap[V, String]) -case class CarryList[V](li: List[String], w: Wrap[V, String]) -case class CarryOpt[V](li: List[String], w: Wrap[V, String]) -case class BagList[Y](s: String, many: List[Y]) -case class BagMap[Y](i: Int, items: Map[String, Y]) -case class BagOpt[Y](i: Int, maybe: Option[Y]) -case class Truck[Z](s: Z, t: Two) - -case class AllOpt( - one: Option[String], - two: Option[String], - three: Option[String]) - -case class PrimitiveLists( - ints: List[Int], - longs: List[Long], - bools: List[Boolean], - chars: List[Char], - doubles: List[Double]) - -case class One( - name: String, - stuff: List[String], - more: List[Two], - nest: Two, - maybe: Option[String], - mymap: Map[String, Int], - flipflop: Boolean, - big: Long, - num: Num.Value, - age: Int) { - val foo: String = "yikes!" -} - -case class OneSub1(name: String, big: Long, maybe: Option[String]) - -case class OneSub2(name: String, flipflop: Boolean, mymap: Map[String, Int]) - -case class Two(foo: String, bar: Boolean) - -case class Three(name: String, two: Num.Value, pp: Pop) - -case class Four(stuff: List[String], things: Map[String, Int]) - -case class Five(@DBKey name: String, two: Two) - -case class Six(@DBKey name: String, @DBKey num: Int, two: Two) - -case class Seven(@DBKey _id: ObjectId, two: Two) - -case class Numy(age: Int, num: Num.Value) - -case class UuidThing( - name: String, - uuid: java.util.UUID, - many: List[java.util.UUID], - maybe: Option[java.util.UUID]) - -case class JodaThing( - name: String, - dt: OffsetDateTime, - many: List[OffsetDateTime], - maybe: Option[OffsetDateTime]) - -trait Pop { - def go(): Unit -} -trait Tart[T] { - val yum: T -} -trait Soup[A] { - val sweet: A -} - -case class Wow1(a: String, b: Int) extends Pop { - def go(): Unit = println("--1--") -} -case class Wow2(x: String, y: Int) extends Pop { - def go(): Unit = println("--2--") -} -case class Cruton[U](i: Int, sweet: U) extends Soup[U] -case class Toast[D](g: Int, yum: D) extends Tart[D] -case class Bun[R](g: Int, yum: R) extends Tart[R] -case class Breakfast[K](y: Boolean, bread: Tart[K]) - -case class Animal(name: String, legs: Int) - -// Value class support w/custom rendering -class Wrapper(val underlying: Int) extends AnyVal -case class ValSupport(name: String, wrap: Wrapper, more: Boolean) - -case class ListValSupport(name: String, wrap: List[Wrapper], more: Boolean) -case class OptValSupport(name: String, wrap: Option[Wrapper]) -case class MapValSupport(name: String, wrap: Map[String, Wrapper]) - -// Test Lists -case class ListList(name: String, stuff: List[List[Animal]]) -case class ListListList(name: String, stuff: List[List[List[Animal]]]) -case class ListOpt(name: String, stuff: List[Option[Animal]]) -case class ListMap(name: String, stuff: List[Map[String, Animal]]) - -// Test nested Options+Variants w/other collections -case class OpOp(name: String, opts: Option[Option[Animal]]) -case class OpList(name: String, opList: Option[List[Animal]]) -case class OpListList(name: String, opListList: Option[List[List[Animal]]]) -case class OpMap(name: String, opMap: Option[Map[String, Animal]]) - -// Test nested Maps+Variants w/other collections -case class MapList(name: String, mapList: Map[String, List[Animal]]) -case class MapListList(name: String, mapList: Map[String, List[List[Animal]]]) -case class MapOpt(name: String, mapOpt: Map[String, Option[Animal]]) -case class MapMap(name: String, mapmap: Map[String, Map[String, Animal]]) - -case class Foo(name: String, stuff: List[String]) - -trait PetAnimal { - val name: String -} -case class Dog(name: String) extends PetAnimal -case class Cat(name: String) extends PetAnimal -trait Pet { - val kind: PetAnimal - val food: String -} -case class NicePet(kind: PetAnimal, food: String) extends Pet -case class GrumpyPet(kind: PetAnimal, food: String) extends Pet -case class WithDefaults( - name: String, - age: Int = 50, - num: Option[Int], - hasStuff: Option[Boolean] = Some(true), - pet: Pet = NicePet(Dog("Fido"), "bones")) - -object CustomVC { - - // val typeAdapter = StringTypeAdapter andThen - - // def stringToDateTime - // - // def read:PartialFunction[(KindMarker,_), Any] = { - // case (jk:JsonKind,js:String) => DateTimeFormat.forPattern("MMMM, yyyy").parseDateTime(js) - // case (mk:MongoKind,bdt:BsonDateTime) => new DateTime(bdt.getValue) - // } - // def render:PartialFunction[(KindMarker,_), Any] = { - // case (jk:JsonKind,dt:DateTime) => '"'+DateTimeFormat.forPattern("MMMM, yyyy").print(dt)+'"' - // case (mk:MongoKind,dt:DateTime) => BsonDateTime(dt.toDate) - // } -} - -class CustomVC(val underlying: YearMonth) extends AnyVal { - override def toString = s"CustomVC($underlying)" -} -case class SomethingSpecial(what: String, when: CustomVC) - -case class SampleZonedDateTime(o1: ZonedDateTime, o2: ZonedDateTime) - -trait Address { val postalCode: String } -case class USAddress( - street: String, - city: String, - state: String, - postalCode: String) - extends Address -case class CanadaAddress( - street: String, - city: String, - province: String, - postalCode: String) - extends Address -case class DefaultAddress(postalCode: String) extends Address -trait Demographic { val address: Address } -case class USDemographic(@DBKey age: String, address: Address) - extends Demographic - -opaque type Phone >: Null = String - -// Override just Phone -object PhoneAdapter extends TypeAdapterFactory with TypeAdapter[Phone]: - def matches(concrete: RType): Boolean = - concrete match { - case a: AliasInfo if a.name == "Phone" => true - case _ => false - } - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[Phone] = this - val info = RType.of[Phone] - override def isStringish: Boolean = true - - def read(parser: Parser): Phone = - parser.expectString() match { - case null => null.asInstanceOf[Phone] - case s: String => s.replaceAll("-", "").asInstanceOf[Phone] - } - - def write[WIRE]( - t: Phone, - writer: Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit = t match { - case null => writer.writeNull(out) - case _ => - writer.writeString( - "%s-%s-%s".format(t.toString.substring(0, 3), t.toString.substring(3, 6), t.toString.substring(6)), - out - ) - } - -case class Person(@DBKey name: String, phone: Phone) - -case class Loose(a: Char, b: Float, c: Short, d: Byte) - -case class MapFactor( - @Change(name = "foo_bar") fooBar:String, - @Change(name = "a_b") thingy: Long, - count: Int, - @Change(name = "big_mac") bigMac:String) -case class MapFactorId( - @DBKey @Change(name = "foo_bar") fooBar:String, - @Change(name = "a_b") thingy: Long, - count: Int, - @Change(name = "big_mac") bigMac: String) -case class MapFactorId2( - @DBKey @Change(name = "foo_bar") fooBar:String, - @DBKey @Change(name = "a_b") thingy: Long, - @DBKey hey: Int, - count: Int, - @Change(name = "big_mac") bigMac: String) - -case class PersonCapture( - id: ObjectId, - name: String, - age: Int, - stuff: Map[Int, Int]) - extends SJCapture -case class Tuple(t: (String, Int)) - -trait Strange -case class StrangeWrapper(num: Int, s: Strange) -case class StrangeHint(_hint: Int, size: Int) extends Strange - -trait Body -case class FancyBody(message: String) extends Body -case class Envelope[T <: Body](id: String, body: T) { - type Giraffe = T -} - -case class Times(offset: OffsetDateTime, zoned: ZonedDateTime) - -case class Embed(stuff: List[String], num: Int) -case class Boom(name: String, other: Try[Embed]) - -case class Flexible(name: String, dunno: Any) \ No newline at end of file diff --git a/mongo/src/test/scala/co.blocke.scalajack/mongo/MongoSpec.scala b/mongo/src/test/scala/co.blocke.scalajack/mongo/MongoSpec.scala deleted file mode 100644 index d741b5e2..00000000 --- a/mongo/src/test/scala/co.blocke.scalajack/mongo/MongoSpec.scala +++ /dev/null @@ -1,891 +0,0 @@ -package co.blocke.scalajack -package mongo - -import model._ - -import java.time._ -import java.util.UUID - -import org.bson._ -import org.bson.types.ObjectId -import TestUtil._ -import munit._ -import munit.internal.console -import scala.jdk.CollectionConverters._ -import co.blocke.scala_reflection.RType - -class WrappedOffsetDateTime(val offsetDateTime: OffsetDateTime) extends AnyVal - -class MongoSpec extends FunSuite: - - val TRUE = true - val FALSE = false - - val data = One( - "Greg", - List("a", "b"), - List(Two("x", FALSE), Two("y", TRUE)), - Two("Nest!", TRUE), - Some("wow"), - Map("hey" -> 17, "you" -> 21), - TRUE, - 99123986123L, - Num.C, - 46 - ) - - def mongoScalaJack: JackFlavor[BsonValue] = ScalaJack(MongoFlavor()) - - test("Naked Map support") { - describe( - "---------------------------\n: Mongo Tests (MongoDB) :\n---------------------------", Console.BLUE - ) - describe("Prinitives") - val li = Map("a" -> 1, "b" -> 2, "c" -> 3) - val dbo: BsonValue = mongoScalaJack.render(li) - assertEquals(dbo.asDocument.toJson, """{"a": 1, "b": 2, "c": 3}""") - assertEquals(mongoScalaJack.read[Map[String, Int]](dbo), li) - } - - test("UUID support") { - val thing = UuidThing( - "Foo", - UUID.fromString("1e6c2b31-4dfe-4bf6-a0a0-882caaff0e9c"), - List( - UUID.fromString("1e6c2b31-4dfe-4bf6-a0a0-882caaff0e9c"), - UUID.fromString("1e6c2b31-4dfe-4bf6-a0a0-882caaff0e9c") - ), - Some(UUID.fromString("1e6c2b31-4dfe-4bf6-a0a0-882caaff0e9c")) - ) - val dbo = mongoScalaJack.render(thing) - assertEquals(dbo.asDocument.toJson, - """{"name": "Foo", "uuid": "1e6c2b31-4dfe-4bf6-a0a0-882caaff0e9c", "many": ["1e6c2b31-4dfe-4bf6-a0a0-882caaff0e9c", "1e6c2b31-4dfe-4bf6-a0a0-882caaff0e9c"], "maybe": "1e6c2b31-4dfe-4bf6-a0a0-882caaff0e9c"}""" - ) - val b = mongoScalaJack.read[UuidThing](dbo) - assertEquals(b, thing) - } - - test("Misc number primitives support") { - val inst = Loose('A', 1.23F, 15.toShort, 3.toByte) - val dbo = mongoScalaJack.render(inst) - assertEquals(dbo.asDocument.toJson, - """{"a": "A", "b": 1.23, "c": 15, "d": 3}""" - ) - assertEquals(mongoScalaJack.read[Loose](dbo), inst) - } - - test("OffsetDateTime support") { - val t = LocalDate - .parse("1986-07-01") - .atTime(OffsetTime.of(LocalTime.MIDNIGHT, ZoneOffset.UTC)) - val thing = JodaThing("Foo", t, List(t, t), Some(t)) - val dbo = mongoScalaJack.render(thing) - assertEquals(dbo.asDocument.toJson, - """{"name": "Foo", "dt": {"$date": 520560000000}, "many": [{"$date": 520560000000}, {"$date": 520560000000}], "maybe": {"$date": 520560000000}}""" - ) - val b = mongoScalaJack.read[JodaThing](dbo) - assertEquals(b, thing) - } - - test("ZonedDateTime must work") { - val inst = SampleZonedDateTime( - ZonedDateTime.parse("2007-12-03T10:15:30Z[UTC]"), - ZonedDateTime.parse("2007-12-03T10:15:30Z[UTC]") - ) - val dbo = mongoScalaJack.render(inst) - assertEquals(dbo.asDocument.toJson, - """{"o1": {"$date": 1196676930000}, "o2": {"$date": 1196676930000}}""" - ) - val b = mongoScalaJack.read[SampleZonedDateTime](dbo) - assertEquals(b, inst) - } - - test("Permissives work") { - val bd = new BsonDocument() - bd.append("name", new BsonString("Fido")) - bd.append("legs", new BsonString("3")) - val wPerm = mongoScalaJack.allowPermissivePrimitives() - assertEquals(wPerm.read[Animal](bd), Animal("Fido", 3)) - } - - test( - "Case class having List parameter - Foo[A](x:A) where A -> List of simple type" - ) { - describe("Basic Collection Support") - val w = Carry("Trey", Wrap("Hobbies", List(true, true, false), "all")) - val db = mongoScalaJack.render(w) - assertEquals(db.asDocument.toJson, - """{"s": "Trey", "w": {"name": "Hobbies", "data": [true, true, false], "stuff": "all"}}""" - ) - assertEquals(mongoScalaJack.read[Carry[List[Boolean]]](db), w) - } - - test( - "Case class having Map parameter - Foo[A](x:A) where A -> Map of simple type" - ) { - val w = Carry("Troy", Wrap("Articles", Map("OK" -> 59), "all")) - val db = mongoScalaJack.render(w) - assertEquals(db.asDocument.toJson, - """{"s": "Troy", "w": {"name": "Articles", "data": {"OK": 59}, "stuff": "all"}}""" - ) - assertEquals(mongoScalaJack.read[Carry[Map[String, Int]]](db), w) - } - - test( - "Case class having Option parameter - Foo[A](x:A) where A -> Option of simple type" - ) { - val w = Carry( - "Terri", - Wrap("Hobbies", Some(17).asInstanceOf[Option[Int]], "all") - ) - val x = Carry[Option[Int]]("Terry", Wrap("Hobbies", None, "all")) - val db = mongoScalaJack.render(w) - val db2 = mongoScalaJack.render(x) - assertEquals(db.asDocument.toJson, - """{"s": "Terri", "w": {"name": "Hobbies", "data": 17, "stuff": "all"}}""" - ) - assertEquals(db2.asDocument.toJson, - """{"s": "Terry", "w": {"name": "Hobbies", "stuff": "all"}}""" - ) - assertEquals(mongoScalaJack.read[Carry[Option[Int]]](db), w) - assertEquals(mongoScalaJack.read[Carry[Option[Int]]](db2), x) - } - - test( - "Case class having List of parameterized value - Foo[A](x:List[A]) - where A is a simple type" - ) { - val w = BagList("list", List(1, 2, 3)) - val db = mongoScalaJack.render(w) - assertEquals(db.asDocument.toJson, - """{"s": "list", "many": [1, 2, 3]}""" - ) - assertEquals(mongoScalaJack.read[BagList[Int]](db), w) - } - - test( - "Case class having Map of parameterized value - Foo[A,B](x:Map[A,B]) - where A,B are simple types" - ) { - val w = BagMap(5, Map("one" -> true, "two" -> false)) - val db = mongoScalaJack.render(w) - assertEquals(db.asDocument.toJson, - """{"i": 5, "items": {"one": true, "two": false}}""" - ) - assertEquals(mongoScalaJack.read[BagMap[Boolean]](db), w) - } - - test( - "Case class having Option of parameterized value - Foo[A](x:Option[A]) - where A is a simple type" - ) { - val w = BagOpt(1, Some("ok")) - val x = BagOpt[String](1, None) - val db = mongoScalaJack.render(w) - val db2 = mongoScalaJack.render(x) - assertEquals(db.asDocument.toJson, """{"i": 1, "maybe": "ok"}""") - assertEquals(db2.asDocument.toJson, """{"i": 1}""") - assertEquals(mongoScalaJack.read[BagOpt[String]](db), w) - assertEquals(mongoScalaJack.read[BagOpt[String]](db2), x) - } - - test( - "Case class having List parameter - Foo[A](x:A) where A -> List of Bar[Int]" - ) { - describe( - "Advanced Collection Support - collections of parameterized case class" - ) - val w = Carry( - "Trey", - Wrap("Hobbies", List(Zoo("one", 1), Zoo("two", 2)), "all") - ) - val db = mongoScalaJack.render(w) - assertEquals(db.asDocument.toJson, - """{"s": "Trey", "w": {"name": "Hobbies", "data": [{"name": "one", "z": 1}, {"name": "two", "z": 2}], "stuff": "all"}}""" - ) - assertEquals(mongoScalaJack.read[Carry[List[Zoo[Int]]]](db), w) - } - - test( - "Case class having Map parameter - Foo[A](x:A) where A -> Map of Bar[Int,String]" - ) { - val w = - Carry("Troy", Wrap("Articles", Map("OK" -> Zoo("q", false)), "all")) - val db = mongoScalaJack.render(w) - assertEquals(db.asDocument.toJson, - """{"s": "Troy", "w": {"name": "Articles", "data": {"OK": {"name": "q", "z": false}}, "stuff": "all"}}""" - ) - assertEquals(mongoScalaJack.read[Carry[Map[String, Zoo[Boolean]]]](db), - w - ) - } - - test( - "Case class having Option parameter - Foo[A](x:A) where A -> Option of Bar[Int]" - ) { - val w = Carry( - "Terri", - Wrap( - "Hobbies", - Some(Zoo("a", "b")).asInstanceOf[Option[Zoo[String]]], - "all" - ) - ) - val x = Carry[Option[Int]]("Terry", Wrap("Hobbies", None, "all")) - val db = mongoScalaJack.render(w) - val db2 = mongoScalaJack.render(x) - assertEquals(db.asDocument.toJson, - """{"s": "Terri", "w": {"name": "Hobbies", "data": {"name": "a", "z": "b"}, "stuff": "all"}}""" - ) - assertEquals(db2.asDocument.toJson, - """{"s": "Terry", "w": {"name": "Hobbies", "stuff": "all"}}""" - ) - assertEquals(mongoScalaJack.read[Carry[Option[Zoo[String]]]](db), w) - assert(mongoScalaJack.read[Carry[Option[Zoo[String]]]](db2) == x) - } - - test( - "Case class having List parameter - Foo[A](x:A) where A -> List of value class" - ) { - val w = Carry( - "Trey", - Wrap("Hobbies", List(new Wrapper(99), new Wrapper(100)), "all") - ) - val db = mongoScalaJack.render(w) - assertEquals(db.asDocument.toJson, - """{"s": "Trey", "w": {"name": "Hobbies", "data": [99, 100], "stuff": "all"}}""" - ) - assertEquals(mongoScalaJack.read[Carry[List[Wrapper]]](db), w) - } - - test( - "Case class having Map parameter - Foo[A](x:A) where A -> Map of Bar[String,value class]" - ) { - val w = - Carry("Troy", Wrap("Articles", Map("OK" -> new Wrapper(2)), "all")) - val db = mongoScalaJack.render(w) - assertEquals(db.asDocument.toJson, - """{"s": "Troy", "w": {"name": "Articles", "data": {"OK": 2}, "stuff": "all"}}""" - ) - assertEquals(mongoScalaJack.read[Carry[Map[String, Wrapper]]](db), w) - } - - test( - "Case class having Option parameter - Foo[A](x:A) where A -> Option of value class" - ) { - val w = Carry( - "Terri", - Wrap( - "Hobbies", - Some(new Wrapper(-2)).asInstanceOf[Option[Wrapper]], - "all" - ) - ) - val x = Carry[Option[Wrapper]]("Terry", Wrap("Hobbies", None, "all")) - val db = mongoScalaJack.render(w) - val db2 = mongoScalaJack.render(x) - assertEquals(db.asDocument.toJson, - """{"s": "Terri", "w": {"name": "Hobbies", "data": -2, "stuff": "all"}}""" - ) - assertEquals(db2.asDocument.toJson, - """{"s": "Terry", "w": {"name": "Hobbies", "stuff": "all"}}""" - ) - assertEquals(mongoScalaJack.read[Carry[Option[Wrapper]]](db), w) - assertEquals(mongoScalaJack.read[Carry[Option[Wrapper]]](db2), x) - } - - test( - "Case class having List of parameterized value - Foo[A](x:List[A]) - where A -> Bar[Int]" - ) { - val w = BagList("list", List(Zoo("a", 1), Zoo("b", 2))) - val db = mongoScalaJack.render(w) - assertEquals(db.asDocument.toJson, - """{"s": "list", "many": [{"name": "a", "z": 1}, {"name": "b", "z": 2}]}""" - ) - assertEquals(mongoScalaJack.read[BagList[Zoo[Int]]](db), w) - } - - test( - "Case class having Map of parameterized value - Foo[A,B](x:Map[A,B]) - where A,B -> String,Bar[Int]" - ) { - val w = BagMap(5, Map("one" -> Zoo("a", 1), "two" -> Zoo("b", 2))) - val db = mongoScalaJack.render(w) - assertEquals(db.asDocument.toJson, - """{"i": 5, "items": {"one": {"name": "a", "z": 1}, "two": {"name": "b", "z": 2}}}""" - ) - assertEquals(mongoScalaJack.read[BagMap[Zoo[Int]]](db), w) - } - - test( - "Case class having Option of parameterized value - Foo[A](x:Option[A]) - where A -> Bar[Int]" - ) { - val w = Carry( - "Terri", - Wrap( - "Hobbies", - Some(Truck(false, Two("aaa", true))) - .asInstanceOf[Option[Truck[Boolean]]], - "all" - ) - ) - val x = - Carry[Option[Truck[Boolean]]]("Terry", Wrap("Hobbies", None, "all")) - val db = mongoScalaJack.render(w) - val db2 = mongoScalaJack.render(x) - assertEquals(db.asDocument.toJson, - """{"s": "Terri", "w": {"name": "Hobbies", "data": {"s": false, "t": {"foo": "aaa", "bar": true}}, "stuff": "all"}}""" - ) - assertEquals(db2.asDocument.toJson, - """{"s": "Terry", "w": {"name": "Hobbies", "stuff": "all"}}""" - ) - assertEquals(mongoScalaJack.read[Carry[Option[Truck[Boolean]]]](db), w) - assertEquals(mongoScalaJack.read[Carry[Option[Truck[Boolean]]]](db2), x) - } - - test( - "Case class having List of parameterized value - Foo[A](x:List[A]) - where A -> value class" - ) { - val w = BagList( - "list", - List(Zoo("a", new Wrapper(1)), Zoo("b", new Wrapper(2))) - ) - val db = mongoScalaJack.render(w) - assertEquals(db.asDocument.toJson, - """{"s": "list", "many": [{"name": "a", "z": 1}, {"name": "b", "z": 2}]}""" - ) - assertEquals(mongoScalaJack.read[BagList[Zoo[Wrapper]]](db), w) - } - - test( - "Case class having Map of parameterized value - Foo[A,B](x:Map[A,B]) - where A,B -> String,value class" - ) { - val w = BagMap( - 5, - Map( - "one" -> Zoo("a", new Wrapper(1)), - "two" -> Zoo("b", new Wrapper(2)) - ) - ) - val db = mongoScalaJack.render(w) - assertEquals(db.asDocument.toJson, - """{"i": 5, "items": {"one": {"name": "a", "z": 1}, "two": {"name": "b", "z": 2}}}""" - ) - assertEquals(mongoScalaJack.read[BagMap[Zoo[Wrapper]]](db), w) - } - - test( - "Case class having Option of parameterized value - Foo[A](x:Option[A]) - where A -> value class" - ) { - val w = Carry( - "Terri", - Wrap( - "Hobbies", - Some(Zoo("a", new Wrapper(12))).asInstanceOf[Option[Zoo[Wrapper]]], - "all" - ) - ) - val x = - Carry[Option[Truck[Boolean]]]("Terry", Wrap("Hobbies", None, "all")) - val db = mongoScalaJack.render(w) - val db2 = mongoScalaJack.render(x) - assertEquals(db.asDocument.toJson, - """{"s": "Terri", "w": {"name": "Hobbies", "data": {"name": "a", "z": 12}, "stuff": "all"}}""" - ) - assertEquals(db2.asDocument.toJson, - """{"s": "Terry", "w": {"name": "Hobbies", "stuff": "all"}}""" - ) - assertEquals(mongoScalaJack.read[Carry[Option[Zoo[Wrapper]]]](db), w) - assert(mongoScalaJack.read[Carry[Option[Zoo[Wrapper]]]](db2) == x) - } - - test("Parameter is a simple trait") { - describe("Basic trait support") - val w = Carry[Pop]("Surprise", Wrap("Yellow", Wow2("three", 3), "Done")) - val db = mongoScalaJack.render(w) - assertEquals(db.asDocument.toJson, - """{"s": "Surprise", "w": {"name": "Yellow", "data": {"_hint": "co.blocke.scalajack.mongo.Wow2", "x": "three", "y": 3}, "stuff": "Done"}}""" - ) - assertEquals(mongoScalaJack.read[Carry[Pop]](db), w) - } - - test("Parameter is a simple trait with hint function value mappings") { - val w = Carry[Pop]("Surprise", Wrap("Yellow", Wow2("three", 4), "Done")) - val scalaJack = mongoScalaJack.withHintModifiers( - RType.of[Pop] -> ClassNameHintModifier( - hint => s"co.blocke.scalajack.mongo.$hint", - fullName => fullName.split('.').last - ) - ) - val db = scalaJack.render(w) - assertEquals(db.asDocument.toJson, - """{"s": "Surprise", "w": {"name": "Yellow", "data": {"_hint": "Wow2", "x": "three", "y": 4}, "stuff": "Done"}}""" - ) - assertEquals(scalaJack.read[Carry[Pop]](db), w) - } - - test("Hint modifier fails") { - val w = Carry[Pop]("Surprise", Wrap("Yellow", Wow2("three", 4), "Done")) - val scalaJack = mongoScalaJack.withHintModifiers( - RType.of[Pop] -> ClassNameHintModifier( - hint => throw new Exception("Boom"), // intentional hint mod failure - fullName => fullName.split('.').last - ) - ) - val db = scalaJack.render(w) - interceptMessage[ScalaJackError]("Couldn't marshal class for Wow2"){ - scalaJack.read[Carry[Pop]](db) - } - } - - test("Type modifier works") { - val scalaJack = ScalaJack(MongoFlavor()).withTypeValueModifier( - ClassNameHintModifier( - (hint: String) => "co.blocke.scalajack.mongo." + hint, - (cname: String) => cname.split('.').last - ) - ) - val value: Envelope[Body] = Envelope("DEF", FancyBody("BOO")) - val d = scalaJack.render[Envelope[Body]](value) - assertEquals(d.asDocument.toJson, - """{"Giraffe": "FancyBody", "id": "DEF", "body": {"message": "BOO"}}""" - ) - assertEquals(scalaJack.read[Envelope[Body]](d), value) - } - - test("Parameter is List of trait") { - val w = Carry[List[Pop]]( - "Surprise", - Wrap("Yellow", List(Wow1("four", 4), Wow2("three", 3)), "Done") - ) - val db = mongoScalaJack.render(w) - assertEquals(db.asDocument.toJson, - """{"s": "Surprise", "w": {"name": "Yellow", "data": [{"_hint": "co.blocke.scalajack.mongo.Wow1", "a": "four", "b": 4}, {"_hint": "co.blocke.scalajack.mongo.Wow2", "x": "three", "y": 3}], "stuff": "Done"}}""" - ) - assertEquals(mongoScalaJack.read[Carry[List[Pop]]](db), w) - } - - test("Parameter is Map of String->trait") { - val w = Carry[Map[String, Pop]]( - "Surprise", - Wrap( - "Yellow", - Map("a" -> Wow1("four", 4), "b" -> Wow2("three", 3)), - "Done" - ) - ) - val db = mongoScalaJack.render(w) - assertEquals(db.asDocument.toJson, - """{"s": "Surprise", "w": {"name": "Yellow", "data": {"a": {"_hint": "co.blocke.scalajack.mongo.Wow1", "a": "four", "b": 4}, "b": {"_hint": "co.blocke.scalajack.mongo.Wow2", "x": "three", "y": 3}}, "stuff": "Done"}}""" - ) - assertEquals(mongoScalaJack.read[Carry[Map[String, Pop]]](db), w) - } - - test("Parameter is an Option of trait") { - val w = Carry[Option[Pop]]( - "Terri", - Wrap("Hobbies", Some(Wow1("ok", -99)), "all") - ) - val x = Carry[Option[Pop]]("Terry", Wrap("Hobbies", None, "all")) - val db = mongoScalaJack.render(w) - val db2 = mongoScalaJack.render(x) - assertEquals(db.asDocument.toJson, - """{"s": "Terri", "w": {"name": "Hobbies", "data": {"_hint": "co.blocke.scalajack.mongo.Wow1", "a": "ok", "b": -99}, "stuff": "all"}}""" - ) - assertEquals(db2.asDocument.toJson, - """{"s": "Terry", "w": {"name": "Hobbies", "stuff": "all"}}""" - ) - assertEquals(mongoScalaJack.read[Carry[Option[Pop]]](db), w) - assertEquals(mongoScalaJack.read[Carry[Option[Pop]]](db2), x) - } - - test("List of parameter, where parameter is a trait") { - val w = BagList[Pop]("list", List(Wow1("A", 1), Wow1("B", 2))) - val db = mongoScalaJack.render(w) - assertEquals(db.asDocument.toJson, - """{"s": "list", "many": [{"_hint": "co.blocke.scalajack.mongo.Wow1", "a": "A", "b": 1}, {"_hint": "co.blocke.scalajack.mongo.Wow1", "a": "B", "b": 2}]}""" - ) - assertEquals(mongoScalaJack.read[BagList[Pop]](db), w) - } - - test("Map of String->parameter, where parameter is a trait") { - val w = - BagMap[Pop](5, Map("one" -> Wow2("q", 7), "two" -> Wow1("r", 3))) - val db = mongoScalaJack.render(w) - assertEquals(db.asDocument.toJson, - """{"i": 5, "items": {"one": {"_hint": "co.blocke.scalajack.mongo.Wow2", "x": "q", "y": 7}, "two": {"_hint": "co.blocke.scalajack.mongo.Wow1", "a": "r", "b": 3}}}""" - ) - assertEquals(mongoScalaJack.read[BagMap[Pop]](db), w) - } - - test("Option of parameter, where parameter is a trait") { - val w = Carry[Option[Pop]]( - "Terri", - Wrap("Hobbies", Some(Wow2("finite", 1000)), "all") - ) - val x = Carry[Option[Pop]]("Terry", Wrap("Hobbies", None, "all")) - val db = mongoScalaJack.render(w) - val db2 = mongoScalaJack.render(x) - assertEquals(db.asDocument.toJson, - """{"s": "Terri", "w": {"name": "Hobbies", "data": {"_hint": "co.blocke.scalajack.mongo.Wow2", "x": "finite", "y": 1000}, "stuff": "all"}}""" - ) - assertEquals(db2.asDocument.toJson, - """{"s": "Terry", "w": {"name": "Hobbies", "stuff": "all"}}""" - ) - assertEquals(mongoScalaJack.read[Carry[Option[Pop]]](db), w) - assertEquals(mongoScalaJack.read[Carry[Option[Pop]]](db2), x) - } - - test("Case class having an embedded parameterized trait") { - describe( - "Advanced trait support -- parameters are traits, themselves having parameters" - ) - val w = Breakfast(true, Toast(7, "Burnt")) - val db = mongoScalaJack.render(w) - assertEquals(db.asDocument.toJson, - """{"y": true, "bread": {"_hint": "co.blocke.scalajack.mongo.Toast", "g": 7, "yum": "Burnt"}}""" - ) - assertEquals(mongoScalaJack.read[Breakfast[String]](db), w) - } - - test( - "Case class having an embedded parameterized trait, with the trait's parameter another case class" - ) { - val w = Breakfast(true, Toast(7, Two("two", true))) - val db = mongoScalaJack.render(w) - assertEquals(db.asDocument.toJson, - """{"y": true, "bread": {"_hint": "co.blocke.scalajack.mongo.Toast", "g": 7, "yum": {"foo": "two", "bar": true}}}""" - ) - assertEquals(mongoScalaJack.read[Breakfast[Two]](db), w) - } - - test( - "Case class having an embedded parameterized trait, with the trait's parameter a value class" - ) { - val w = Breakfast(true, Toast(7, new Wrapper(-100))) - val db = mongoScalaJack.render(w) - assertEquals(db.asDocument.toJson, - """{"y": true, "bread": {"_hint": "co.blocke.scalajack.mongo.Toast", "g": 7, "yum": -100}}""" - ) - assertEquals(mongoScalaJack.read[Breakfast[Wrapper]](db), w) - } - - test("Parameter is a parameterized trait") { // I can't believe this one worked! - val w = Carry[Tart[Soup[String]]]( - "Bill", - Wrap("Betty", Bun(3, Cruton(8, "eight")), "ok") - ) - val db = mongoScalaJack.render(w) - assertEquals(db.asDocument.toJson, - """{"s": "Bill", "w": {"name": "Betty", "data": {"_hint": "co.blocke.scalajack.mongo.Bun", "g": 3, "yum": {"_hint": "co.blocke.scalajack.mongo.Cruton", "i": 8, "sweet": "eight"}}, "stuff": "ok"}}""" - ) - assertEquals(mongoScalaJack.read[Carry[Tart[Soup[String]]]](db), w) - } - - test("Parameter is List of parameterized trait") { - val w = Carry[List[Tart[Boolean]]]( - "Trey", - Wrap("Hobbies", List(Bun(1, false), Toast(2, true)), "all") - ) - val db = mongoScalaJack.render(w) - assertEquals(db.asDocument.toJson, - """{"s": "Trey", "w": {"name": "Hobbies", "data": [{"_hint": "co.blocke.scalajack.mongo.Bun", "g": 1, "yum": false}, {"_hint": "co.blocke.scalajack.mongo.Toast", "g": 2, "yum": true}], "stuff": "all"}}""" - ) - assertEquals(mongoScalaJack.read[Carry[List[Tart[Boolean]]]](db), w) - } - - test("Parameter is Map of String->parameterized trait") { - val w = Carry[Map[String, Tart[String]]]( - "Troy", - Wrap("Articles", Map("OK" -> Bun(27, "Hot")), "all") - ) - val db = mongoScalaJack.render(w) - assertEquals(db.asDocument.toJson, - """{"s": "Troy", "w": {"name": "Articles", "data": {"OK": {"_hint": "co.blocke.scalajack.mongo.Bun", "g": 27, "yum": "Hot"}}, "stuff": "all"}}""" - ) - assertEquals(mongoScalaJack.read[Carry[Map[String, Tart[String]]]](db), - w - ) - } - - test("Parameter is an Option of parameterized trait") { - val w = Carry[Option[Tart[Int]]]( - "Terri", - Wrap("Hobbies", Some(Toast(11, 12)), "all") - ) - val x = Carry[Option[Tart[Int]]]("Terry", Wrap("Hobbies", None, "all")) - val db = mongoScalaJack.render(w) - val db2 = mongoScalaJack.render(x) - assertEquals(db.asDocument.toJson, - """{"s": "Terri", "w": {"name": "Hobbies", "data": {"_hint": "co.blocke.scalajack.mongo.Toast", "g": 11, "yum": 12}, "stuff": "all"}}""" - ) - assertEquals(db2.asDocument.toJson, - """{"s": "Terry", "w": {"name": "Hobbies", "stuff": "all"}}""" - ) - assertEquals(mongoScalaJack.read[Carry[Option[Tart[Int]]]](db), w) - assertEquals(mongoScalaJack.read[Carry[Option[Tart[Int]]]](db2), x) - } - - test("List of parameter, where parameter is a parameterized trait") { - val w = - BagList[Tart[Boolean]]("list", List(Toast(1, true), Bun(2, false))) - val db = mongoScalaJack.render(w) - assertEquals(db.asDocument.toJson, - """{"s": "list", "many": [{"_hint": "co.blocke.scalajack.mongo.Toast", "g": 1, "yum": true}, {"_hint": "co.blocke.scalajack.mongo.Bun", "g": 2, "yum": false}]}""" - ) - assertEquals(mongoScalaJack.read[BagList[Tart[Boolean]]](db), w) - } - - test("Map of String->parameter, where parameter is a parameterized trait") { - val w = BagMap[Tart[Boolean]]( - 5, - Map("one" -> Bun(1, true), "two" -> Toast(2, false)) - ) - val db = mongoScalaJack.render(w) - assertEquals(db.asDocument.toJson, - """{"i": 5, "items": {"one": {"_hint": "co.blocke.scalajack.mongo.Bun", "g": 1, "yum": true}, "two": {"_hint": "co.blocke.scalajack.mongo.Toast", "g": 2, "yum": false}}}""" - ) - assertEquals(mongoScalaJack.read[BagMap[Tart[Boolean]]](db), w) - } - - test("Option of parameter, where parameter is a parameterized trait") { - val w = BagOpt[Tart[String]](1, Some(Bun(6, "ok"))) - val x = BagOpt[Tart[String]](1, None) - val db = mongoScalaJack.render(w) - val db2 = mongoScalaJack.render(x) - assertEquals(db.asDocument.toJson, - """{"i": 1, "maybe": {"_hint": "co.blocke.scalajack.mongo.Bun", "g": 6, "yum": "ok"}}""" - ) - assertEquals(db2.asDocument.toJson, """{"i": 1}""") - assertEquals(mongoScalaJack.read[BagOpt[Tart[String]]](db), w) - assertEquals(mongoScalaJack.read[BagOpt[Tart[String]]](db2), x) - } - - test("DBKey Annotation (_id field generation) - single key") { - describe("Annotations (e.g. DBKey)") - val five = Five("Fred", Two("blah", true)) - val dbo = mongoScalaJack.render(five) - assertEquals(dbo.asDocument.toJson, - """{"_id": "Fred", "two": {"foo": "blah", "bar": true}}""" - ) - assertEquals(mongoScalaJack.read[Five](dbo), five) - } - - test( - "DBKey Annotation (_id field generation) - single key -- Missing Non-Key Field" - ) { - val dbo = new BsonDocument( - List( - new BsonElement("_id", new BsonString("Fred")), - new BsonElement( - "two", - new BsonDocument( - List(new BsonElement("bar", new BsonBoolean(true))).asJava - ) - ) - ).asJava - ) - assertEquals(dbo.toJson, """{"_id": "Fred", "two": {"bar": true}}""") - val msg = """Class co.blocke.scalajack.mongo.Two missing required fields: foo""".stripMargin - interceptMessage[ScalaJackError](msg){ - mongoScalaJack.read[Five](dbo) - } - } - - test( - "DBKey Annotation (_id field generation) - single key -- Missing Key Field" - ) { - val dbo = new BsonDocument( - List( - new BsonElement( - "two", - new BsonDocument( - List( - new BsonElement("foo", new BsonString("blah")), - new BsonElement("bar", new BsonBoolean(true)) - ).asJava - ) - ) - ).asJava - ) - assertEquals(dbo.toJson, """{"two": {"foo": "blah", "bar": true}}""") - val msg = - """Missing key (_id) field, or a component of a compound key field""" - interceptMessage[ScalaJackError](msg){ - mongoScalaJack.read[Five](dbo) - } - } - - test("DBKey Annotation (_id field generation) - compound key") { - val six = Six("Fred", 12, Two("blah", true)) - val dbo = mongoScalaJack.render(six) - assertEquals(dbo.asDocument.toJson, - """{"_id": {"name": "Fred", "num": 12}, "two": {"foo": "blah", "bar": true}}""" - ) - assertEquals(mongoScalaJack.read[Six](dbo), six) - } - - test( - "DBKey Annotation (_id field generation) - compound key -- Missing Key Field component" - ) { - val js = - """{"_id": {"name": "Fred"},"two": {"foo": "blah", "bar": true}}""" - val dbo = BsonDocument.parse(js) - val msg = - """Class co.blocke.scalajack.mongo.Six missing required fields: num""" - interceptMessage[ScalaJackError](msg){ - mongoScalaJack.read[Six](dbo) - } - } - - test("ObjectId support -- Mongo") { - describe("Mongo ObjectID") - // val oid = (new BsonObjectId()).getValue() - val oid = new ObjectId() - val seven = Seven(oid, Two("blah", true)) - val dbo = mongoScalaJack.render(seven) - assertEquals(dbo.asDocument.toJson, - s"""{"_id": {"$$oid": "${oid.toString}"}, "two": {"foo": "blah", "bar": true}}""" - ) - assertEquals(mongoScalaJack.read[Seven](dbo), seven) - } - - test("ObjectId support (null) -- Mongo") { - val seven = Seven(null, Two("blah", bar = true)) - val dbo = mongoScalaJack.render(seven) - assertEquals(mongoScalaJack.read[Seven](dbo), seven) - } - - test("Must handle a case class with default values - defaults specified") { - describe("Basic Case Class Support") - val wd = WithDefaults( - "Greg", - 49, - Some(5), - Some(false), - GrumpyPet(Cat("Fluffy"), "fish") - ) - val dbo = mongoScalaJack.render(wd) - assertEquals(dbo.asDocument.toJson, - """{"name": "Greg", "age": 49, "num": 5, "hasStuff": false, "pet": {"_hint": "co.blocke.scalajack.mongo.GrumpyPet", "kind": {"_hint": "co.blocke.scalajack.mongo.Cat", "name": "Fluffy"}, "food": "fish"}}""" - ) - val b = mongoScalaJack.read[WithDefaults](dbo) - assertEquals(b, wd) - } - - test("Case class as value for Any parameter") { - val f = Flexible("foo", Two("bar", bar = true)) - val d = mongoScalaJack.render(f) - assertEquals(d.toString, - """{"name": "foo", "dunno": {"_hint": "co.blocke.scalajack.mongo.Two", "foo": "bar", "bar": true}}""" - ) - assertEquals(mongoScalaJack.read[Flexible](d), f) - } - - test( - "Must handle a case class with default values - defaults not specified" - ) { - val wd = WithDefaults("Greg", 49, None) - val dbo = mongoScalaJack.render(wd) - assertEquals(dbo.asDocument.toJson, - """{"name": "Greg", "age": 49, "hasStuff": true, "pet": {"_hint": "co.blocke.scalajack.mongo.NicePet", "kind": {"_hint": "co.blocke.scalajack.mongo.Dog", "name": "Fido"}, "food": "bones"}}""" - ) - val b = mongoScalaJack.read[WithDefaults](dbo) - assertEquals(b, wd) - } - - test("Simple parameters - Foo[A](x:A) where A -> simple type") { - describe("Basic Parameterized Case Class") - val w = Wrap("number", true, 15) - val w2 = Wrap("number", true, "wow") - val db = mongoScalaJack.render(w) - val db2 = mongoScalaJack.render(w2) - assertEquals(db.asDocument.toJson, - """{"name": "number", "data": true, "stuff": 15}""" - ) - assertEquals(db2.asDocument.toJson, - """{"name": "number", "data": true, "stuff": "wow"}""" - ) - assertEquals(mongoScalaJack.read[Wrap[Boolean, Int]](db), w) - assertEquals(mongoScalaJack.read[Wrap[Boolean, String]](db2), w2) - } - - test( - "Non-parameter case clase as a field member - Foo[A](x:A, b:Bar) where A -> simple type" - ) { - val w = Truck(false, Two("z", true)) - val dbo = mongoScalaJack.render(w) - assertEquals(dbo.asDocument.toJson, - """{"s": false, "t": {"foo": "z", "bar": true}}""" - ) - assertEquals(mongoScalaJack.read[Truck[Boolean]](dbo), w) - } - - test("Non-parameter case class as a parameter - Foo[A](x:A) where A -> Bar") { - val w = Wrap("number", true, Two("a", false)) - val db = mongoScalaJack.render(w) - assertEquals(db.asDocument.toJson, - """{"name": "number", "data": true, "stuff": {"foo": "a", "bar": false}}""" - ) - assertEquals(mongoScalaJack.read[Wrap[Boolean, Two]](db), w) - } - - test( - "Parameterized case class as parameter - Foo[A](x:A) where A -> Bar[Int]" - ) { - describe("Advanced Parameterized Case Class") - val w = Carry("Bob", Wrap("Mary", 3, "Available")) - val x = Carry("Mary", Wrap("Greg", false, "Done")) - val y = Carry("Fred", Wrap("Mike", Two("Steam", true), "OK")) - val db = mongoScalaJack.render(w) - val db2 = mongoScalaJack.render(x) - val db3 = mongoScalaJack.render(y) - assertEquals(db.asDocument.toJson, - """{"s": "Bob", "w": {"name": "Mary", "data": 3, "stuff": "Available"}}""" - ) - assertEquals(db2.asDocument.toJson, - """{"s": "Mary", "w": {"name": "Greg", "data": false, "stuff": "Done"}}""" - ) - assertEquals(db3.asDocument.toJson, - """{"s": "Fred", "w": {"name": "Mike", "data": {"foo": "Steam", "bar": true}, "stuff": "OK"}}""" - ) - assertEquals(mongoScalaJack.read[Carry[Int]](db), w) - assertEquals(mongoScalaJack.read[Carry[Boolean]](db2), x) - assertEquals(mongoScalaJack.read[Carry[Two]](db3), y) - } - - test( - "Case class having value class parameter - Foo[A](x:A) where A -> value class (no value class handler)" - ) { - val w = Carry("Mike", Wrap("Sally", new Wrapper(15), "Fine")) - val db = mongoScalaJack.render(w) - assertEquals(db.asDocument.asDocument.toJson, - """{"s": "Mike", "w": {"name": "Sally", "data": 15, "stuff": "Fine"}}""" - ) - assertEquals(mongoScalaJack.read[Carry[Wrapper]](db), w) - } - - test( - "Case class having value class parameter - Foo[A](x:A) where A -> value class (WITH value class handler)" - ) { - val offsetDateTime = - OffsetDateTime.of(2015, 7, 1, 0, 0, 0, 0, ZoneOffset.UTC) - val w = Carry( - "Mike", - Wrap("Sally", new WrappedOffsetDateTime(offsetDateTime), "Fine") - ) - val db = mongoScalaJack.render(w) - - val timeval = offsetDateTime.toInstant.toEpochMilli - assertEquals(db.asDocument.toJson, - s"""{"s": "Mike", "w": {"name": "Sally", "data": {"$$date": $timeval}, "stuff": "Fine"}}""" - ) - assertEquals(mongoScalaJack.read[Carry[WrappedOffsetDateTime]](db), w) - } - - test( - "Case class having parameterized case class as a parameter: Foo[A](x:A) where A -> Bar[Blah[Long]]" - ) { - val w = Carry("Bill", Wrap("Betty", Zoo("dog", false), "ok")) - val db = mongoScalaJack.render(w) - assertEquals(db.asDocument.toJson, - """{"s": "Bill", "w": {"name": "Betty", "data": {"name": "dog", "z": false}, "stuff": "ok"}}""" - ) - assertEquals(mongoScalaJack.read[Carry[Zoo[Boolean]]](db), w) - } diff --git a/mongo/src/test/scala/co.blocke.scalajack/mongo/TryAndCapture.scala b/mongo/src/test/scala/co.blocke.scalajack/mongo/TryAndCapture.scala deleted file mode 100644 index f073a35c..00000000 --- a/mongo/src/test/scala/co.blocke.scalajack/mongo/TryAndCapture.scala +++ /dev/null @@ -1,57 +0,0 @@ -package co.blocke.scalajack -package mongo - -import TestUtil._ -import munit._ -import munit.internal.console -import org.bson._ -import org.bson.types.ObjectId - -import scala.util.{ Failure, Success } - -class TryAndCapture extends FunSuite: - - val sj = ScalaJack(MongoFlavor()) - - test("Try success") { - describe( - "-------------------------------\n: Try and Capture (MongoDB) :\n-------------------------------", Console.BLUE - ) - val d = BsonDocument.parse( - """{"name":"Greg","other":{"stuff":["a","b","c"],"num":2}}""" - ) - val obj = sj.read[Boom](d) - assertEquals(Boom("Greg", Success(Embed(List("a", "b", "c"), 2))), obj) - assertEquals(sj.render(obj), d) - } - - test("Try failure") { - val d = BsonDocument.parse("""{"name":"Greg","other":[1,2,3]}""") - val obj = sj.read[Boom](d) - val msg = - """Expected document (object) here, not 'BsonArray{values=[BsonInt32{value=1}, BsonInt32{value=2}, BsonInt32{value=3}]}'""".stripMargin - assertEquals(msg, obj.other.asInstanceOf[Failure[_]].exception.getMessage) - assertEquals(sj.render(obj), d) - } - - test("Try failure 2") { - val d = - BsonDocument.parse("""{"name":"Greg","other": -12.45 ,"num":2}""") - val obj = sj.read[Boom](d) - val msg = - """Expected document (object) here, not 'BsonDouble{value=-12.45}'""".stripMargin - assertEquals(msg, obj.other.asInstanceOf[Failure[_]].exception.getMessage) - assert(BsonDocument.parse("""{"name":"Greg","other":-12.45}""") == sj.render(obj)) - } - - test("SJCapture should work") { - val s = PersonCapture(new ObjectId(), "Fred", 52, Map(5 -> 1, 6 -> 2)) - val m = sj.render(s) - m.asDocument.append("extra", new BsonString("hey")) - val readIn = sj.read[PersonCapture](m) - assertEquals(readIn, s) - assert(sj.render(readIn) - .asDocument - .toJson - .endsWith(""""stuff": {"5": 1, "6": 2}, "extra": "hey"}""")) - } \ No newline at end of file diff --git a/mongo/src/test/scala/co.blocke.scalajack/mongo/TupleSpec.scala b/mongo/src/test/scala/co.blocke.scalajack/mongo/TupleSpec.scala deleted file mode 100644 index 3148b49d..00000000 --- a/mongo/src/test/scala/co.blocke.scalajack/mongo/TupleSpec.scala +++ /dev/null @@ -1,81 +0,0 @@ -package co.blocke.scalajack -package mongo - -import TestUtil._ -import munit._ -import munit.internal.console -import org.bson._ -import scala.jdk.CollectionConverters._ - -case class TT2(name: String, rec: Map[String, List[(String, Int, Boolean)]]) - -class TupleSpec extends FunSuite: - - val sjM = ScalaJack(MongoFlavor()) - - object ScalaMaster { - val r = List(("a", 1, true)) - val r2 = List(("x", 8, false), ("r", 3, true)) - val a = TT2("Larry", Map("foo" -> r, "hey" -> r2)) - } - - object MongoMaster { - val a = new BsonDocument( - List( - new BsonElement("name", new BsonString("Larry")), - new BsonElement( - "rec", - new BsonDocument( - List( - new BsonElement( - "foo", - new BsonArray( - List( - new BsonArray( - List( - new BsonString("a"), - new BsonInt32(1), - new BsonBoolean(true) - ).asJava - ) - ).asJava - ) - ), - new BsonElement( - "hey", - new BsonArray( - List( - new BsonArray( - List( - new BsonString("x"), - new BsonInt32(8), - new BsonBoolean(false), - ).asJava - ), - new BsonArray( - List( - new BsonString("r"), - new BsonInt32(3), - new BsonBoolean(true) - ).asJava - ) - ).asJava - ) - ) - ).asJava - ) - ) - ).asJava - ) - } - - test("Render Tests") { - describe( - "---------------------------\n: Tuple Tests (MongoDB) :\n---------------------------" - ) - assertEquals(sjM.render(ScalaMaster.a), MongoMaster.a) - } - - test("Read Tests") { - assertEquals(sjM.read[TT2](MongoMaster.a), ScalaMaster.a) - } \ No newline at end of file diff --git a/project/build.properties b/project/build.properties index 6a9f0388..27430827 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.7.3 +sbt.version=1.9.6 diff --git a/project/metals.sbt b/project/metals.sbt new file mode 100644 index 00000000..d62c0d42 --- /dev/null +++ b/project/metals.sbt @@ -0,0 +1,6 @@ +// DO NOT EDIT! This file is auto-generated. + +// This file enables sbt-bloop to create bloop config files. + +addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.5.8") + diff --git a/project/plugins.sbt b/project/plugins.sbt index acb1310c..2424290e 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -2,3 +2,4 @@ addSbtPlugin("co.blocke" % "gitflow-packager" % "0.1.32") addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.5.11") addSbtPlugin("org.typelevel" % "sbt-typelevel-sonatype-ci-release" % "0.5.0-M6") addSbtPlugin("org.typelevel" % "sbt-typelevel-github-actions" % "0.4.16") +addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.2") diff --git a/src/main/scala/co.blocke.scalajack/Codec.scala b/src/main/scala/co.blocke.scalajack/Codec.scala new file mode 100644 index 00000000..5e789029 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/Codec.scala @@ -0,0 +1,21 @@ +package co.blocke.scalajack + +import co.blocke.scala_reflection.TypedName +import co.blocke.scala_reflection.reflect.ReflectOnType +import scala.quoted.* +import quoted.Quotes +import json.* + +object Codec: + + inline def write[T](t: T): String = ${ writeImpl[T]('t) } + + def writeImpl[T:Type](t: Expr[T])(using q: Quotes): Expr[String] = + import quotes.reflect.* + + val rtRef = ReflectOnType[T](q)(TypeRepr.of[T])(using scala.collection.mutable.Map.empty[TypedName, Boolean]) + val fn = JsonWriter.writeJsonFn[T](rtRef) + '{ + val sb = new StringBuilder() + $fn($t, sb).toString + } diff --git a/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala b/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala new file mode 100644 index 00000000..4224c61d --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala @@ -0,0 +1,76 @@ +package co.blocke.scalajack +package json + +import co.blocke.scala_reflection.RTypeRef +import co.blocke.scala_reflection.reflect.rtypeRefs.* +import scala.quoted.* + +object JsonWriter: + + def writeJsonFn[T](rtRef: RTypeRef[T])(using Type[T])(using q: Quotes): Expr[(T,StringBuilder) => StringBuilder] = + import quotes.reflect.* + rtRef match + case rt: PrimitiveRef[?] if rt.isStringish => + '{(a:T, sb:StringBuilder) => + sb.append('"') + sb.append(a.toString) + sb.append('"') + } + case rt: PrimitiveRef[?] => + '{(a:T, sb:StringBuilder) => sb.append(a.toString) } + + case rt: AliasRef[?] => + writeJsonFn[rt.T](rt.unwrappedType.asInstanceOf[RTypeRef[rt.T]])(using Type.of[rt.T]) + + case rt: ArrayRef[?] => + rt.elementRef.refType match + case '[t] => + val elementFn = writeJsonFn[t](rt.elementRef.asInstanceOf[RTypeRef[t]]) + '{(a:T, sb:StringBuilder) => + sb.append('[') + a.asInstanceOf[Array[?]].map{ e => + $elementFn(e.asInstanceOf[t],sb) + sb.append(',') + } + sb.setCharAt(sb.length()-1,']') + } + + case rt: ClassRef[?] => + val fieldFns = rt.fields.map{ f => + f.fieldRef.refType match + case '[t] => (writeJsonFn[t](f.fieldRef.asInstanceOf[RTypeRef[t]]), f) + } + '{(a:T, sb:StringBuilder) => + sb.append('{') + ${ + val stmts = fieldFns.map{ case (fn, field) => + field.fieldRef.refType match + case '[t] => + '{ + sb.append(${Expr(field.name)}) + sb.append(':') + val fieldValue = ${ + Select.unique('{ a }.asTerm, field.name).asExpr + } + val fn2 = $fn.asInstanceOf[(t, StringBuilder) => StringBuilder] + fn2(fieldValue.asInstanceOf[t], sb) + sb.append(',') + } + } + Expr.ofList(stmts) + } + sb.setCharAt(sb.length()-1,'}') + } + + case rt: SeqRef[?] => + rt.elementRef.refType match + case '[t] => + val elementFn = writeJsonFn[t](rt.elementRef.asInstanceOf[RTypeRef[t]]) + '{(a:T, sb:StringBuilder) => + sb.append('[') + a.asInstanceOf[Seq[?]].map{ e => + $elementFn(e.asInstanceOf[t],sb) + sb.append(',') + } + sb.setCharAt(sb.length()-1,']') + } \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala new file mode 100644 index 00000000..1c73d974 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -0,0 +1,34 @@ +package co.blocke.scalajack +package run + +import co.blocke.scala_reflection.* + +object RunMe extends App: + + val p = Person("Greg", 57, List(false,true,true)) + + val i: Array[Int] = Array(1,2,3) + + println(Codec.write(p)) + + // println(RType.of[Person].pretty) +/* + + +val SJConfig = .... (JSON or Binary fmt specified in config) + +val sj = ScalaJack(using SJConfig) + +sj.write(person) +sj.read[Person](js): Either[Error,Person] + + +JsonFlavor: + +def write[T](t: T): String = ${ writeImpl[T]('t) } + +def writeImpl[T:Type](t: Expr[T])(using quotes: Quotes): Expr[String] = + import quotes.reflect.* + + +*/ \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/run/Sample.scala b/src/main/scala/co.blocke.scalajack/run/Sample.scala new file mode 100644 index 00000000..74860ac5 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/run/Sample.scala @@ -0,0 +1,3 @@ +package co.blocke.scalajack.run + +case class Person(name: String, age: Int, isOk: List[Boolean]) \ No newline at end of file diff --git a/core/src/main/java/co/blocke/scalajack/Change.java b/src_old/main/java/co/blocke/scalajack/Change.java similarity index 100% rename from core/src/main/java/co/blocke/scalajack/Change.java rename to src_old/main/java/co/blocke/scalajack/Change.java diff --git a/core/src/main/java/co/blocke/scalajack/Collection.java b/src_old/main/java/co/blocke/scalajack/Collection.java similarity index 100% rename from core/src/main/java/co/blocke/scalajack/Collection.java rename to src_old/main/java/co/blocke/scalajack/Collection.java diff --git a/core/src/main/java/co/blocke/scalajack/DBKey.java b/src_old/main/java/co/blocke/scalajack/DBKey.java similarity index 100% rename from core/src/main/java/co/blocke/scalajack/DBKey.java rename to src_old/main/java/co/blocke/scalajack/DBKey.java diff --git a/core/src/main/java/co/blocke/scalajack/Ignore.java b/src_old/main/java/co/blocke/scalajack/Ignore.java similarity index 100% rename from core/src/main/java/co/blocke/scalajack/Ignore.java rename to src_old/main/java/co/blocke/scalajack/Ignore.java diff --git a/core/src/main/java/co/blocke/scalajack/JavaStuff.java b/src_old/main/java/co/blocke/scalajack/JavaStuff.java similarity index 100% rename from core/src/main/java/co/blocke/scalajack/JavaStuff.java rename to src_old/main/java/co/blocke/scalajack/JavaStuff.java diff --git a/core/src/main/java/co/blocke/scalajack/Optional.java b/src_old/main/java/co/blocke/scalajack/Optional.java similarity index 100% rename from core/src/main/java/co/blocke/scalajack/Optional.java rename to src_old/main/java/co/blocke/scalajack/Optional.java diff --git a/core/src/main/scala/co.blocke.scalajack/Converters.scala b/src_old/main/scala/co.blocke.scalajack/Converters.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/Converters.scala rename to src_old/main/scala/co.blocke.scalajack/Converters.scala diff --git a/core/src/main/scala/co.blocke.scalajack/Main.scalax b/src_old/main/scala/co.blocke.scalajack/Main.scalax similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/Main.scalax rename to src_old/main/scala/co.blocke.scalajack/Main.scalax diff --git a/core/src/main/scala/co.blocke.scalajack/Performance.scalax b/src_old/main/scala/co.blocke.scalajack/Performance.scalax similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/Performance.scalax rename to src_old/main/scala/co.blocke.scalajack/Performance.scalax diff --git a/core/src/main/scala/co.blocke.scalajack/SJCapture.scala b/src_old/main/scala/co.blocke.scalajack/SJCapture.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/SJCapture.scala rename to src_old/main/scala/co.blocke.scalajack/SJCapture.scala diff --git a/core/src/main/scala/co.blocke.scalajack/ScalaJack.scala b/src_old/main/scala/co.blocke.scalajack/ScalaJack.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/ScalaJack.scala rename to src_old/main/scala/co.blocke.scalajack/ScalaJack.scala diff --git a/core/src/main/scala/co.blocke.scalajack/delimited/DelimitedEitherTypeAdapterFactory.scala b/src_old/main/scala/co.blocke.scalajack/delimited/DelimitedEitherTypeAdapterFactory.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/delimited/DelimitedEitherTypeAdapterFactory.scala rename to src_old/main/scala/co.blocke.scalajack/delimited/DelimitedEitherTypeAdapterFactory.scala diff --git a/core/src/main/scala/co.blocke.scalajack/delimited/DelimitedFlavor.scala b/src_old/main/scala/co.blocke.scalajack/delimited/DelimitedFlavor.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/delimited/DelimitedFlavor.scala rename to src_old/main/scala/co.blocke.scalajack/delimited/DelimitedFlavor.scala diff --git a/core/src/main/scala/co.blocke.scalajack/delimited/DelimitedOptionTypeAdapterFactory.scala b/src_old/main/scala/co.blocke.scalajack/delimited/DelimitedOptionTypeAdapterFactory.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/delimited/DelimitedOptionTypeAdapterFactory.scala rename to src_old/main/scala/co.blocke.scalajack/delimited/DelimitedOptionTypeAdapterFactory.scala diff --git a/core/src/main/scala/co.blocke.scalajack/delimited/DelimitedParser.scala b/src_old/main/scala/co.blocke.scalajack/delimited/DelimitedParser.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/delimited/DelimitedParser.scala rename to src_old/main/scala/co.blocke.scalajack/delimited/DelimitedParser.scala diff --git a/core/src/main/scala/co.blocke.scalajack/delimited/DelimitedWriter.scala b/src_old/main/scala/co.blocke.scalajack/delimited/DelimitedWriter.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/delimited/DelimitedWriter.scala rename to src_old/main/scala/co.blocke.scalajack/delimited/DelimitedWriter.scala diff --git a/core/src/main/scala/co.blocke.scalajack/json/JsonFlavor.scala b/src_old/main/scala/co.blocke.scalajack/json/JsonFlavor.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/json/JsonFlavor.scala rename to src_old/main/scala/co.blocke.scalajack/json/JsonFlavor.scala diff --git a/core/src/main/scala/co.blocke.scalajack/json/JsonParser.scala b/src_old/main/scala/co.blocke.scalajack/json/JsonParser.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/json/JsonParser.scala rename to src_old/main/scala/co.blocke.scalajack/json/JsonParser.scala diff --git a/core/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala b/src_old/main/scala/co.blocke.scalajack/json/JsonWriter.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala rename to src_old/main/scala/co.blocke.scalajack/json/JsonWriter.scala diff --git a/core/src/main/scala/co.blocke.scalajack/json4s/JValueBuilder.scala b/src_old/main/scala/co.blocke.scalajack/json4s/JValueBuilder.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/json4s/JValueBuilder.scala rename to src_old/main/scala/co.blocke.scalajack/json4s/JValueBuilder.scala diff --git a/core/src/main/scala/co.blocke.scalajack/json4s/Json4sFlavor.scala b/src_old/main/scala/co.blocke.scalajack/json4s/Json4sFlavor.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/json4s/Json4sFlavor.scala rename to src_old/main/scala/co.blocke.scalajack/json4s/Json4sFlavor.scala diff --git a/core/src/main/scala/co.blocke.scalajack/json4s/Json4sParser.scala b/src_old/main/scala/co.blocke.scalajack/json4s/Json4sParser.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/json4s/Json4sParser.scala rename to src_old/main/scala/co.blocke.scalajack/json4s/Json4sParser.scala diff --git a/core/src/main/scala/co.blocke.scalajack/json4s/Json4sWriter.scala b/src_old/main/scala/co.blocke.scalajack/json4s/Json4sWriter.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/json4s/Json4sWriter.scala rename to src_old/main/scala/co.blocke.scalajack/json4s/Json4sWriter.scala diff --git a/core/src/main/scala/co.blocke.scalajack/json4s/StringWrapTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/json4s/StringWrapTypeAdapter.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/json4s/StringWrapTypeAdapter.scala rename to src_old/main/scala/co.blocke.scalajack/json4s/StringWrapTypeAdapter.scala diff --git a/core/src/main/scala/co.blocke.scalajack/model/ClassFieldMember.scala b/src_old/main/scala/co.blocke.scalajack/model/ClassFieldMember.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/model/ClassFieldMember.scala rename to src_old/main/scala/co.blocke.scalajack/model/ClassFieldMember.scala diff --git a/core/src/main/scala/co.blocke.scalajack/model/HintValueModifier.scala b/src_old/main/scala/co.blocke.scalajack/model/HintValueModifier.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/model/HintValueModifier.scala rename to src_old/main/scala/co.blocke.scalajack/model/HintValueModifier.scala diff --git a/core/src/main/scala/co.blocke.scalajack/model/JackFlavor.scala b/src_old/main/scala/co.blocke.scalajack/model/JackFlavor.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/model/JackFlavor.scala rename to src_old/main/scala/co.blocke.scalajack/model/JackFlavor.scala diff --git a/core/src/main/scala/co.blocke.scalajack/model/LazyTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/model/LazyTypeAdapter.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/model/LazyTypeAdapter.scala rename to src_old/main/scala/co.blocke.scalajack/model/LazyTypeAdapter.scala diff --git a/core/src/main/scala/co.blocke.scalajack/model/Parser.scala b/src_old/main/scala/co.blocke.scalajack/model/Parser.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/model/Parser.scala rename to src_old/main/scala/co.blocke.scalajack/model/Parser.scala diff --git a/core/src/main/scala/co.blocke.scalajack/model/StringBuilder.scala b/src_old/main/scala/co.blocke.scalajack/model/StringBuilder.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/model/StringBuilder.scala rename to src_old/main/scala/co.blocke.scalajack/model/StringBuilder.scala diff --git a/core/src/main/scala/co.blocke.scalajack/model/TypeAdapaterCache.scala b/src_old/main/scala/co.blocke.scalajack/model/TypeAdapaterCache.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/model/TypeAdapaterCache.scala rename to src_old/main/scala/co.blocke.scalajack/model/TypeAdapaterCache.scala diff --git a/core/src/main/scala/co.blocke.scalajack/model/TypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/model/TypeAdapter.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/model/TypeAdapter.scala rename to src_old/main/scala/co.blocke.scalajack/model/TypeAdapter.scala diff --git a/core/src/main/scala/co.blocke.scalajack/model/TypeAdapterFactory.scala b/src_old/main/scala/co.blocke.scalajack/model/TypeAdapterFactory.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/model/TypeAdapterFactory.scala rename to src_old/main/scala/co.blocke.scalajack/model/TypeAdapterFactory.scala diff --git a/core/src/main/scala/co.blocke.scalajack/model/ViewSplice.scala b/src_old/main/scala/co.blocke.scalajack/model/ViewSplice.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/model/ViewSplice.scala rename to src_old/main/scala/co.blocke.scalajack/model/ViewSplice.scala diff --git a/core/src/main/scala/co.blocke.scalajack/model/Writer.scala b/src_old/main/scala/co.blocke.scalajack/model/Writer.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/model/Writer.scala rename to src_old/main/scala/co.blocke.scalajack/model/Writer.scala diff --git a/core/src/main/scala/co.blocke.scalajack/typeadapter/AnyTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/AnyTypeAdapter.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/typeadapter/AnyTypeAdapter.scala rename to src_old/main/scala/co.blocke.scalajack/typeadapter/AnyTypeAdapter.scala diff --git a/core/src/main/scala/co.blocke.scalajack/typeadapter/ArrayTypeAdatper.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/ArrayTypeAdatper.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/typeadapter/ArrayTypeAdatper.scala rename to src_old/main/scala/co.blocke.scalajack/typeadapter/ArrayTypeAdatper.scala diff --git a/core/src/main/scala/co.blocke.scalajack/typeadapter/CollectionTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/CollectionTypeAdapter.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/typeadapter/CollectionTypeAdapter.scala rename to src_old/main/scala/co.blocke.scalajack/typeadapter/CollectionTypeAdapter.scala diff --git a/core/src/main/scala/co.blocke.scalajack/typeadapter/EitherTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/EitherTypeAdapter.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/typeadapter/EitherTypeAdapter.scala rename to src_old/main/scala/co.blocke.scalajack/typeadapter/EitherTypeAdapter.scala diff --git a/core/src/main/scala/co.blocke.scalajack/typeadapter/EnumTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/EnumTypeAdapter.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/typeadapter/EnumTypeAdapter.scala rename to src_old/main/scala/co.blocke.scalajack/typeadapter/EnumTypeAdapter.scala diff --git a/core/src/main/scala/co.blocke.scalajack/typeadapter/FallbackTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/FallbackTypeAdapter.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/typeadapter/FallbackTypeAdapter.scala rename to src_old/main/scala/co.blocke.scalajack/typeadapter/FallbackTypeAdapter.scala diff --git a/core/src/main/scala/co.blocke.scalajack/typeadapter/IntersectionTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/IntersectionTypeAdapter.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/typeadapter/IntersectionTypeAdapter.scala rename to src_old/main/scala/co.blocke.scalajack/typeadapter/IntersectionTypeAdapter.scala diff --git a/core/src/main/scala/co.blocke.scalajack/typeadapter/JavaPrimitives.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/JavaPrimitives.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/typeadapter/JavaPrimitives.scala rename to src_old/main/scala/co.blocke.scalajack/typeadapter/JavaPrimitives.scala diff --git a/core/src/main/scala/co.blocke.scalajack/typeadapter/MaybeStringWrapTypeAadapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/MaybeStringWrapTypeAadapter.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/typeadapter/MaybeStringWrapTypeAadapter.scala rename to src_old/main/scala/co.blocke.scalajack/typeadapter/MaybeStringWrapTypeAadapter.scala diff --git a/core/src/main/scala/co.blocke.scalajack/typeadapter/OptionTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/OptionTypeAdapter.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/typeadapter/OptionTypeAdapter.scala rename to src_old/main/scala/co.blocke.scalajack/typeadapter/OptionTypeAdapter.scala diff --git a/core/src/main/scala/co.blocke.scalajack/typeadapter/PermissiveJavaPrimitives.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/PermissiveJavaPrimitives.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/typeadapter/PermissiveJavaPrimitives.scala rename to src_old/main/scala/co.blocke.scalajack/typeadapter/PermissiveJavaPrimitives.scala diff --git a/core/src/main/scala/co.blocke.scalajack/typeadapter/PermissiveScalaPrimitives.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/PermissiveScalaPrimitives.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/typeadapter/PermissiveScalaPrimitives.scala rename to src_old/main/scala/co.blocke.scalajack/typeadapter/PermissiveScalaPrimitives.scala diff --git a/core/src/main/scala/co.blocke.scalajack/typeadapter/ScalaPrimitives.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/ScalaPrimitives.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/typeadapter/ScalaPrimitives.scala rename to src_old/main/scala/co.blocke.scalajack/typeadapter/ScalaPrimitives.scala diff --git a/core/src/main/scala/co.blocke.scalajack/typeadapter/SealedTraitTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/SealedTraitTypeAdapter.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/typeadapter/SealedTraitTypeAdapter.scala rename to src_old/main/scala/co.blocke.scalajack/typeadapter/SealedTraitTypeAdapter.scala diff --git a/core/src/main/scala/co.blocke.scalajack/typeadapter/StringWrapTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/StringWrapTypeAdapter.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/typeadapter/StringWrapTypeAdapter.scala rename to src_old/main/scala/co.blocke.scalajack/typeadapter/StringWrapTypeAdapter.scala diff --git a/core/src/main/scala/co.blocke.scalajack/typeadapter/TimePrimitives.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/TimePrimitives.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/typeadapter/TimePrimitives.scala rename to src_old/main/scala/co.blocke.scalajack/typeadapter/TimePrimitives.scala diff --git a/core/src/main/scala/co.blocke.scalajack/typeadapter/TraitTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/TraitTypeAdapter.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/typeadapter/TraitTypeAdapter.scala rename to src_old/main/scala/co.blocke.scalajack/typeadapter/TraitTypeAdapter.scala diff --git a/core/src/main/scala/co.blocke.scalajack/typeadapter/TryTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/TryTypeAdapter.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/typeadapter/TryTypeAdapter.scala rename to src_old/main/scala/co.blocke.scalajack/typeadapter/TryTypeAdapter.scala diff --git a/core/src/main/scala/co.blocke.scalajack/typeadapter/TupleTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/TupleTypeAdapter.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/typeadapter/TupleTypeAdapter.scala rename to src_old/main/scala/co.blocke.scalajack/typeadapter/TupleTypeAdapter.scala diff --git a/core/src/main/scala/co.blocke.scalajack/typeadapter/UUIDTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/UUIDTypeAdapter.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/typeadapter/UUIDTypeAdapter.scala rename to src_old/main/scala/co.blocke.scalajack/typeadapter/UUIDTypeAdapter.scala diff --git a/core/src/main/scala/co.blocke.scalajack/typeadapter/UnionTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/UnionTypeAdapter.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/typeadapter/UnionTypeAdapter.scala rename to src_old/main/scala/co.blocke.scalajack/typeadapter/UnionTypeAdapter.scala diff --git a/core/src/main/scala/co.blocke.scalajack/typeadapter/ValueClassTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/ValueClassTypeAdapter.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/typeadapter/ValueClassTypeAdapter.scala rename to src_old/main/scala/co.blocke.scalajack/typeadapter/ValueClassTypeAdapter.scala diff --git a/core/src/main/scala/co.blocke.scalajack/typeadapter/classes/CaseClassTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/classes/CaseClassTypeAdapter.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/typeadapter/classes/CaseClassTypeAdapter.scala rename to src_old/main/scala/co.blocke.scalajack/typeadapter/classes/CaseClassTypeAdapter.scala diff --git a/core/src/main/scala/co.blocke.scalajack/typeadapter/classes/ClassTypeAdapterBase.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/classes/ClassTypeAdapterBase.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/typeadapter/classes/ClassTypeAdapterBase.scala rename to src_old/main/scala/co.blocke.scalajack/typeadapter/classes/ClassTypeAdapterBase.scala diff --git a/core/src/main/scala/co.blocke.scalajack/typeadapter/classes/JavaClassTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/classes/JavaClassTypeAdapter.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/typeadapter/classes/JavaClassTypeAdapter.scala rename to src_old/main/scala/co.blocke.scalajack/typeadapter/classes/JavaClassTypeAdapter.scala diff --git a/core/src/main/scala/co.blocke.scalajack/typeadapter/classes/NonCaseClassTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/classes/NonCaseClassTypeAdapter.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/typeadapter/classes/NonCaseClassTypeAdapter.scala rename to src_old/main/scala/co.blocke.scalajack/typeadapter/classes/NonCaseClassTypeAdapter.scala diff --git a/core/src/main/scala/co.blocke.scalajack/typeadapter/classes/ScalaClassTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/classes/ScalaClassTypeAdapter.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/typeadapter/classes/ScalaClassTypeAdapter.scala rename to src_old/main/scala/co.blocke.scalajack/typeadapter/classes/ScalaClassTypeAdapter.scala diff --git a/core/src/main/scala/co.blocke.scalajack/typeadapter/classes/ScalaClassTypeAdapterFactory.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/classes/ScalaClassTypeAdapterFactory.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/typeadapter/classes/ScalaClassTypeAdapterFactory.scala rename to src_old/main/scala/co.blocke.scalajack/typeadapter/classes/ScalaClassTypeAdapterFactory.scala diff --git a/core/src/main/scala/co.blocke.scalajack/typeadapter/collection/JavaMapLikeTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/collection/JavaMapLikeTypeAdapter.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/typeadapter/collection/JavaMapLikeTypeAdapter.scala rename to src_old/main/scala/co.blocke.scalajack/typeadapter/collection/JavaMapLikeTypeAdapter.scala diff --git a/core/src/main/scala/co.blocke.scalajack/typeadapter/collection/JavaSeqLikeTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/collection/JavaSeqLikeTypeAdapter.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/typeadapter/collection/JavaSeqLikeTypeAdapter.scala rename to src_old/main/scala/co.blocke.scalajack/typeadapter/collection/JavaSeqLikeTypeAdapter.scala diff --git a/core/src/main/scala/co.blocke.scalajack/typeadapter/collection/JavaStackTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/collection/JavaStackTypeAdapter.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/typeadapter/collection/JavaStackTypeAdapter.scala rename to src_old/main/scala/co.blocke.scalajack/typeadapter/collection/JavaStackTypeAdapter.scala diff --git a/core/src/main/scala/co.blocke.scalajack/typeadapter/collection/MapLikeTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/collection/MapLikeTypeAdapter.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/typeadapter/collection/MapLikeTypeAdapter.scala rename to src_old/main/scala/co.blocke.scalajack/typeadapter/collection/MapLikeTypeAdapter.scala diff --git a/core/src/main/scala/co.blocke.scalajack/typeadapter/collection/SeqLikeTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/collection/SeqLikeTypeAdapter.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/typeadapter/collection/SeqLikeTypeAdapter.scala rename to src_old/main/scala/co.blocke.scalajack/typeadapter/collection/SeqLikeTypeAdapter.scala diff --git a/core/src/main/scala/co.blocke.scalajack/util/BijectiveFunction.scala b/src_old/main/scala/co.blocke.scalajack/util/BijectiveFunction.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/util/BijectiveFunction.scala rename to src_old/main/scala/co.blocke.scalajack/util/BijectiveFunction.scala diff --git a/core/src/main/scala/co.blocke.scalajack/util/FixFloat.scala b/src_old/main/scala/co.blocke.scalajack/util/FixFloat.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/util/FixFloat.scala rename to src_old/main/scala/co.blocke.scalajack/util/FixFloat.scala diff --git a/core/src/main/scala/co.blocke.scalajack/yaml/YamlBuilder.scala b/src_old/main/scala/co.blocke.scalajack/yaml/YamlBuilder.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/yaml/YamlBuilder.scala rename to src_old/main/scala/co.blocke.scalajack/yaml/YamlBuilder.scala diff --git a/core/src/main/scala/co.blocke.scalajack/yaml/YamlFlavor.scala b/src_old/main/scala/co.blocke.scalajack/yaml/YamlFlavor.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/yaml/YamlFlavor.scala rename to src_old/main/scala/co.blocke.scalajack/yaml/YamlFlavor.scala diff --git a/core/src/main/scala/co.blocke.scalajack/yaml/YamlParser.scala b/src_old/main/scala/co.blocke.scalajack/yaml/YamlParser.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/yaml/YamlParser.scala rename to src_old/main/scala/co.blocke.scalajack/yaml/YamlParser.scala diff --git a/core/src/main/scala/co.blocke.scalajack/yaml/YamlWriter.scala b/src_old/main/scala/co.blocke.scalajack/yaml/YamlWriter.scala similarity index 100% rename from core/src/main/scala/co.blocke.scalajack/yaml/YamlWriter.scala rename to src_old/main/scala/co.blocke.scalajack/yaml/YamlWriter.scala diff --git a/core/src/test/java/co/blocke/scalajack/JavaArray.java b/src_old/test/java/co/blocke/scalajack/JavaArray.java similarity index 100% rename from core/src/test/java/co/blocke/scalajack/JavaArray.java rename to src_old/test/java/co/blocke/scalajack/JavaArray.java diff --git a/core/src/test/java/co/blocke/scalajack/JavaCap.java b/src_old/test/java/co/blocke/scalajack/JavaCap.java similarity index 100% rename from core/src/test/java/co/blocke/scalajack/JavaCap.java rename to src_old/test/java/co/blocke/scalajack/JavaCap.java diff --git a/core/src/test/java/co/blocke/scalajack/JavaEnum.java b/src_old/test/java/co/blocke/scalajack/JavaEnum.java similarity index 100% rename from core/src/test/java/co/blocke/scalajack/JavaEnum.java rename to src_old/test/java/co/blocke/scalajack/JavaEnum.java diff --git a/core/src/test/java/co/blocke/scalajack/JavaSimpleBase.java b/src_old/test/java/co/blocke/scalajack/JavaSimpleBase.java similarity index 100% rename from core/src/test/java/co/blocke/scalajack/JavaSimpleBase.java rename to src_old/test/java/co/blocke/scalajack/JavaSimpleBase.java diff --git a/core/src/test/java/co/blocke/scalajack/JavaSimpleChild.java b/src_old/test/java/co/blocke/scalajack/JavaSimpleChild.java similarity index 100% rename from core/src/test/java/co/blocke/scalajack/JavaSimpleChild.java rename to src_old/test/java/co/blocke/scalajack/JavaSimpleChild.java diff --git a/core/src/test/java/co/blocke/scalajack/Maybe.java b/src_old/test/java/co/blocke/scalajack/Maybe.java similarity index 100% rename from core/src/test/java/co/blocke/scalajack/Maybe.java rename to src_old/test/java/co/blocke/scalajack/Maybe.java diff --git a/core/src/test/java/co/blocke/scalajack/Maybe2.java b/src_old/test/java/co/blocke/scalajack/Maybe2.java similarity index 100% rename from core/src/test/java/co/blocke/scalajack/Maybe2.java rename to src_old/test/java/co/blocke/scalajack/Maybe2.java diff --git a/core/src/test/java/co/blocke/scalajack/OnSetter.java b/src_old/test/java/co/blocke/scalajack/OnSetter.java similarity index 100% rename from core/src/test/java/co/blocke/scalajack/OnSetter.java rename to src_old/test/java/co/blocke/scalajack/OnSetter.java diff --git a/core/src/test/java/co/blocke/scalajack/Temperature.java b/src_old/test/java/co/blocke/scalajack/Temperature.java similarity index 100% rename from core/src/test/java/co/blocke/scalajack/Temperature.java rename to src_old/test/java/co/blocke/scalajack/Temperature.java diff --git a/core/src/test/java/co/blocke/scalajack/Unsupported.java b/src_old/test/java/co/blocke/scalajack/Unsupported.java similarity index 100% rename from core/src/test/java/co/blocke/scalajack/Unsupported.java rename to src_old/test/java/co/blocke/scalajack/Unsupported.java diff --git a/core/src/test/scala/co.blocke.scalajack/ConverterSpec.scala b/src_old/test/scala/co.blocke.scalajack/ConverterSpec.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/ConverterSpec.scala rename to src_old/test/scala/co.blocke.scalajack/ConverterSpec.scala diff --git a/core/src/test/scala/co.blocke.scalajack/JsonDiff.scala b/src_old/test/scala/co.blocke.scalajack/JsonDiff.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/JsonDiff.scala rename to src_old/test/scala/co.blocke.scalajack/JsonDiff.scala diff --git a/core/src/test/scala/co.blocke.scalajack/JsonMatcher.scala b/src_old/test/scala/co.blocke.scalajack/JsonMatcher.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/JsonMatcher.scala rename to src_old/test/scala/co.blocke.scalajack/JsonMatcher.scala diff --git a/core/src/test/scala/co.blocke.scalajack/TestUtil.scala b/src_old/test/scala/co.blocke.scalajack/TestUtil.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/TestUtil.scala rename to src_old/test/scala/co.blocke.scalajack/TestUtil.scala diff --git a/core/src/test/scala/co.blocke.scalajack/delimited/DelimitedSpec.scala b/src_old/test/scala/co.blocke.scalajack/delimited/DelimitedSpec.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/delimited/DelimitedSpec.scala rename to src_old/test/scala/co.blocke.scalajack/delimited/DelimitedSpec.scala diff --git a/core/src/test/scala/co.blocke.scalajack/delimited/Model.scala b/src_old/test/scala/co.blocke.scalajack/delimited/Model.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/delimited/Model.scala rename to src_old/test/scala/co.blocke.scalajack/delimited/Model.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json/collections/AnyCollections.scala b/src_old/test/scala/co.blocke.scalajack/json/collections/AnyCollections.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json/collections/AnyCollections.scala rename to src_old/test/scala/co.blocke.scalajack/json/collections/AnyCollections.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json/collections/Arrays.scala b/src_old/test/scala/co.blocke.scalajack/json/collections/Arrays.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json/collections/Arrays.scala rename to src_old/test/scala/co.blocke.scalajack/json/collections/Arrays.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json/collections/Maps.scala b/src_old/test/scala/co.blocke.scalajack/json/collections/Maps.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json/collections/Maps.scala rename to src_old/test/scala/co.blocke.scalajack/json/collections/Maps.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json/collections/Model.scala b/src_old/test/scala/co.blocke.scalajack/json/collections/Model.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json/collections/Model.scala rename to src_old/test/scala/co.blocke.scalajack/json/collections/Model.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json/collections/Options.scala b/src_old/test/scala/co.blocke.scalajack/json/collections/Options.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json/collections/Options.scala rename to src_old/test/scala/co.blocke.scalajack/json/collections/Options.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json/collections/Seqs.scala b/src_old/test/scala/co.blocke.scalajack/json/collections/Seqs.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json/collections/Seqs.scala rename to src_old/test/scala/co.blocke.scalajack/json/collections/Seqs.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json/collections/Tuples.scala b/src_old/test/scala/co.blocke.scalajack/json/collections/Tuples.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json/collections/Tuples.scala rename to src_old/test/scala/co.blocke.scalajack/json/collections/Tuples.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json/custom/CustomAdapter.scala b/src_old/test/scala/co.blocke.scalajack/json/custom/CustomAdapter.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json/custom/CustomAdapter.scala rename to src_old/test/scala/co.blocke.scalajack/json/custom/CustomAdapter.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json/custom/CustomTypeHints.scala b/src_old/test/scala/co.blocke.scalajack/json/custom/CustomTypeHints.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json/custom/CustomTypeHints.scala rename to src_old/test/scala/co.blocke.scalajack/json/custom/CustomTypeHints.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json/custom/Model.scala b/src_old/test/scala/co.blocke.scalajack/json/custom/Model.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json/custom/Model.scala rename to src_old/test/scala/co.blocke.scalajack/json/custom/Model.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json/custom/ParseOrElse.scala b/src_old/test/scala/co.blocke.scalajack/json/custom/ParseOrElse.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json/custom/ParseOrElse.scala rename to src_old/test/scala/co.blocke.scalajack/json/custom/ParseOrElse.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json/mapkeys/ClassPrimKeys.scala b/src_old/test/scala/co.blocke.scalajack/json/mapkeys/ClassPrimKeys.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json/mapkeys/ClassPrimKeys.scala rename to src_old/test/scala/co.blocke.scalajack/json/mapkeys/ClassPrimKeys.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json/mapkeys/JavaPrimKeys.scala b/src_old/test/scala/co.blocke.scalajack/json/mapkeys/JavaPrimKeys.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json/mapkeys/JavaPrimKeys.scala rename to src_old/test/scala/co.blocke.scalajack/json/mapkeys/JavaPrimKeys.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json/mapkeys/ListCollKeys.scala b/src_old/test/scala/co.blocke.scalajack/json/mapkeys/ListCollKeys.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json/mapkeys/ListCollKeys.scala rename to src_old/test/scala/co.blocke.scalajack/json/mapkeys/ListCollKeys.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json/mapkeys/MapCollKeys.scala b/src_old/test/scala/co.blocke.scalajack/json/mapkeys/MapCollKeys.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json/mapkeys/MapCollKeys.scala rename to src_old/test/scala/co.blocke.scalajack/json/mapkeys/MapCollKeys.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json/mapkeys/Model.scala b/src_old/test/scala/co.blocke.scalajack/json/mapkeys/Model.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json/mapkeys/Model.scala rename to src_old/test/scala/co.blocke.scalajack/json/mapkeys/Model.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json/mapkeys/ScalaPrimKeys.scala b/src_old/test/scala/co.blocke.scalajack/json/mapkeys/ScalaPrimKeys.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json/mapkeys/ScalaPrimKeys.scala rename to src_old/test/scala/co.blocke.scalajack/json/mapkeys/ScalaPrimKeys.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json/mapkeys/TupleCollKeys.scala b/src_old/test/scala/co.blocke.scalajack/json/mapkeys/TupleCollKeys.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json/mapkeys/TupleCollKeys.scala rename to src_old/test/scala/co.blocke.scalajack/json/mapkeys/TupleCollKeys.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json/mapkeys/ValueClassKeys.scala b/src_old/test/scala/co.blocke.scalajack/json/mapkeys/ValueClassKeys.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json/mapkeys/ValueClassKeys.scala rename to src_old/test/scala/co.blocke.scalajack/json/mapkeys/ValueClassKeys.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json/parameters/ClassParams.scala b/src_old/test/scala/co.blocke.scalajack/json/parameters/ClassParams.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json/parameters/ClassParams.scala rename to src_old/test/scala/co.blocke.scalajack/json/parameters/ClassParams.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json/parameters/Model.scala b/src_old/test/scala/co.blocke.scalajack/json/parameters/Model.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json/parameters/Model.scala rename to src_old/test/scala/co.blocke.scalajack/json/parameters/Model.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json/parameters/TraitParams.scala b/src_old/test/scala/co.blocke.scalajack/json/parameters/TraitParams.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json/parameters/TraitParams.scala rename to src_old/test/scala/co.blocke.scalajack/json/parameters/TraitParams.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json/plainclass/Inheritance.scala b/src_old/test/scala/co.blocke.scalajack/json/plainclass/Inheritance.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json/plainclass/Inheritance.scala rename to src_old/test/scala/co.blocke.scalajack/json/plainclass/Inheritance.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json/plainclass/Misc.scala b/src_old/test/scala/co.blocke.scalajack/json/plainclass/Misc.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json/plainclass/Misc.scala rename to src_old/test/scala/co.blocke.scalajack/json/plainclass/Misc.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json/plainclass/Model.scala b/src_old/test/scala/co.blocke.scalajack/json/plainclass/Model.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json/plainclass/Model.scala rename to src_old/test/scala/co.blocke.scalajack/json/plainclass/Model.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json/plainclass/TryAndCapture.scala b/src_old/test/scala/co.blocke.scalajack/json/plainclass/TryAndCapture.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json/plainclass/TryAndCapture.scala rename to src_old/test/scala/co.blocke.scalajack/json/plainclass/TryAndCapture.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json/plainclass/ValueClass.scala b/src_old/test/scala/co.blocke.scalajack/json/plainclass/ValueClass.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json/plainclass/ValueClass.scala rename to src_old/test/scala/co.blocke.scalajack/json/plainclass/ValueClass.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json/primitives/AnyPrim.scala b/src_old/test/scala/co.blocke.scalajack/json/primitives/AnyPrim.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json/primitives/AnyPrim.scala rename to src_old/test/scala/co.blocke.scalajack/json/primitives/AnyPrim.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json/primitives/Enums.scala b/src_old/test/scala/co.blocke.scalajack/json/primitives/Enums.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json/primitives/Enums.scala rename to src_old/test/scala/co.blocke.scalajack/json/primitives/Enums.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json/primitives/JavaPrim.scala b/src_old/test/scala/co.blocke.scalajack/json/primitives/JavaPrim.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json/primitives/JavaPrim.scala rename to src_old/test/scala/co.blocke.scalajack/json/primitives/JavaPrim.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json/primitives/Model.scala b/src_old/test/scala/co.blocke.scalajack/json/primitives/Model.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json/primitives/Model.scala rename to src_old/test/scala/co.blocke.scalajack/json/primitives/Model.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json/primitives/PermissivePrimitives.scala b/src_old/test/scala/co.blocke.scalajack/json/primitives/PermissivePrimitives.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json/primitives/PermissivePrimitives.scala rename to src_old/test/scala/co.blocke.scalajack/json/primitives/PermissivePrimitives.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json/primitives/ScalaPrim.scala b/src_old/test/scala/co.blocke.scalajack/json/primitives/ScalaPrim.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json/primitives/ScalaPrim.scala rename to src_old/test/scala/co.blocke.scalajack/json/primitives/ScalaPrim.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json/primitives/TimePrim.scala b/src_old/test/scala/co.blocke.scalajack/json/primitives/TimePrim.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json/primitives/TimePrim.scala rename to src_old/test/scala/co.blocke.scalajack/json/primitives/TimePrim.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json/primitives/ValueClassPrim.scala b/src_old/test/scala/co.blocke.scalajack/json/primitives/ValueClassPrim.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json/primitives/ValueClassPrim.scala rename to src_old/test/scala/co.blocke.scalajack/json/primitives/ValueClassPrim.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json/structures/Eithers.scala b/src_old/test/scala/co.blocke.scalajack/json/structures/Eithers.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json/structures/Eithers.scala rename to src_old/test/scala/co.blocke.scalajack/json/structures/Eithers.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json/structures/Model.scala b/src_old/test/scala/co.blocke.scalajack/json/structures/Model.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json/structures/Model.scala rename to src_old/test/scala/co.blocke.scalajack/json/structures/Model.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json/structures/SealedTraits.scala b/src_old/test/scala/co.blocke.scalajack/json/structures/SealedTraits.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json/structures/SealedTraits.scala rename to src_old/test/scala/co.blocke.scalajack/json/structures/SealedTraits.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json/structures/TryAndCapture.scala b/src_old/test/scala/co.blocke.scalajack/json/structures/TryAndCapture.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json/structures/TryAndCapture.scala rename to src_old/test/scala/co.blocke.scalajack/json/structures/TryAndCapture.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json/structures/TypeMembers.scala b/src_old/test/scala/co.blocke.scalajack/json/structures/TypeMembers.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json/structures/TypeMembers.scala rename to src_old/test/scala/co.blocke.scalajack/json/structures/TypeMembers.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json/structures/UnionsAndIntersections.scala b/src_old/test/scala/co.blocke.scalajack/json/structures/UnionsAndIntersections.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json/structures/UnionsAndIntersections.scala rename to src_old/test/scala/co.blocke.scalajack/json/structures/UnionsAndIntersections.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json4s/AnyColl.scala b/src_old/test/scala/co.blocke.scalajack/json4s/AnyColl.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json4s/AnyColl.scala rename to src_old/test/scala/co.blocke.scalajack/json4s/AnyColl.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json4s/Custom.scala b/src_old/test/scala/co.blocke.scalajack/json4s/Custom.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json4s/Custom.scala rename to src_old/test/scala/co.blocke.scalajack/json4s/Custom.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json4s/Json4sSpec.scala b/src_old/test/scala/co.blocke.scalajack/json4s/Json4sSpec.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json4s/Json4sSpec.scala rename to src_old/test/scala/co.blocke.scalajack/json4s/Json4sSpec.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json4s/Model.scala b/src_old/test/scala/co.blocke.scalajack/json4s/Model.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json4s/Model.scala rename to src_old/test/scala/co.blocke.scalajack/json4s/Model.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json4s/Parsing.scala b/src_old/test/scala/co.blocke.scalajack/json4s/Parsing.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json4s/Parsing.scala rename to src_old/test/scala/co.blocke.scalajack/json4s/Parsing.scala diff --git a/core/src/test/scala/co.blocke.scalajack/json4s/ScalaPrim.scala b/src_old/test/scala/co.blocke.scalajack/json4s/ScalaPrim.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/json4s/ScalaPrim.scala rename to src_old/test/scala/co.blocke.scalajack/json4s/ScalaPrim.scala diff --git a/core/src/test/scala/co.blocke.scalajack/yaml/collections/AnyColl.scala b/src_old/test/scala/co.blocke.scalajack/yaml/collections/AnyColl.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/yaml/collections/AnyColl.scala rename to src_old/test/scala/co.blocke.scalajack/yaml/collections/AnyColl.scala diff --git a/core/src/test/scala/co.blocke.scalajack/yaml/mapkeys/ClassPrimKeys.scala b/src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/ClassPrimKeys.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/yaml/mapkeys/ClassPrimKeys.scala rename to src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/ClassPrimKeys.scala diff --git a/core/src/test/scala/co.blocke.scalajack/yaml/mapkeys/JavaPrimKeys.scala b/src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/JavaPrimKeys.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/yaml/mapkeys/JavaPrimKeys.scala rename to src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/JavaPrimKeys.scala diff --git a/core/src/test/scala/co.blocke.scalajack/yaml/mapkeys/ListCollKeys.scala b/src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/ListCollKeys.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/yaml/mapkeys/ListCollKeys.scala rename to src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/ListCollKeys.scala diff --git a/core/src/test/scala/co.blocke.scalajack/yaml/mapkeys/MapCollKeys.scala b/src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/MapCollKeys.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/yaml/mapkeys/MapCollKeys.scala rename to src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/MapCollKeys.scala diff --git a/core/src/test/scala/co.blocke.scalajack/yaml/mapkeys/Model.scala b/src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/Model.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/yaml/mapkeys/Model.scala rename to src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/Model.scala diff --git a/core/src/test/scala/co.blocke.scalajack/yaml/mapkeys/ScalaPrimKeys.scala b/src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/ScalaPrimKeys.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/yaml/mapkeys/ScalaPrimKeys.scala rename to src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/ScalaPrimKeys.scala diff --git a/core/src/test/scala/co.blocke.scalajack/yaml/mapkeys/TupleCollKeys.scala b/src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/TupleCollKeys.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/yaml/mapkeys/TupleCollKeys.scala rename to src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/TupleCollKeys.scala diff --git a/core/src/test/scala/co.blocke.scalajack/yaml/mapkeys/ValueClassKeys.scala b/src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/ValueClassKeys.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/yaml/mapkeys/ValueClassKeys.scala rename to src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/ValueClassKeys.scala diff --git a/core/src/test/scala/co.blocke.scalajack/yaml/misc/ReadWriterSpec.scala b/src_old/test/scala/co.blocke.scalajack/yaml/misc/ReadWriterSpec.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/yaml/misc/ReadWriterSpec.scala rename to src_old/test/scala/co.blocke.scalajack/yaml/misc/ReadWriterSpec.scala diff --git a/core/src/test/scala/co.blocke.scalajack/yaml/misc/TypeMembers.scala b/src_old/test/scala/co.blocke.scalajack/yaml/misc/TypeMembers.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/yaml/misc/TypeMembers.scala rename to src_old/test/scala/co.blocke.scalajack/yaml/misc/TypeMembers.scala diff --git a/core/src/test/scala/co.blocke.scalajack/yaml/misc/YamlFlavorSpec.scala b/src_old/test/scala/co.blocke.scalajack/yaml/misc/YamlFlavorSpec.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/yaml/misc/YamlFlavorSpec.scala rename to src_old/test/scala/co.blocke.scalajack/yaml/misc/YamlFlavorSpec.scala diff --git a/core/src/test/scala/co.blocke.scalajack/yaml/parameters/ClassParams.scala b/src_old/test/scala/co.blocke.scalajack/yaml/parameters/ClassParams.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/yaml/parameters/ClassParams.scala rename to src_old/test/scala/co.blocke.scalajack/yaml/parameters/ClassParams.scala diff --git a/core/src/test/scala/co.blocke.scalajack/yaml/parameters/Model.scala b/src_old/test/scala/co.blocke.scalajack/yaml/parameters/Model.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/yaml/parameters/Model.scala rename to src_old/test/scala/co.blocke.scalajack/yaml/parameters/Model.scala diff --git a/core/src/test/scala/co.blocke.scalajack/yaml/parameters/TraitParams.scala b/src_old/test/scala/co.blocke.scalajack/yaml/parameters/TraitParams.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/yaml/parameters/TraitParams.scala rename to src_old/test/scala/co.blocke.scalajack/yaml/parameters/TraitParams.scala diff --git a/core/src/test/scala/co.blocke.scalajack/yaml/primitives.plain/Misc.scala b/src_old/test/scala/co.blocke.scalajack/yaml/primitives.plain/Misc.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/yaml/primitives.plain/Misc.scala rename to src_old/test/scala/co.blocke.scalajack/yaml/primitives.plain/Misc.scala diff --git a/core/src/test/scala/co.blocke.scalajack/yaml/primitives.plain/Model.scala b/src_old/test/scala/co.blocke.scalajack/yaml/primitives.plain/Model.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/yaml/primitives.plain/Model.scala rename to src_old/test/scala/co.blocke.scalajack/yaml/primitives.plain/Model.scala diff --git a/core/src/test/scala/co.blocke.scalajack/yaml/primitives.plain/TryAndCapture.scala b/src_old/test/scala/co.blocke.scalajack/yaml/primitives.plain/TryAndCapture.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/yaml/primitives.plain/TryAndCapture.scala rename to src_old/test/scala/co.blocke.scalajack/yaml/primitives.plain/TryAndCapture.scala diff --git a/core/src/test/scala/co.blocke.scalajack/yaml/primitives.plain/ValueClassPrim.scala b/src_old/test/scala/co.blocke.scalajack/yaml/primitives.plain/ValueClassPrim.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/yaml/primitives.plain/ValueClassPrim.scala rename to src_old/test/scala/co.blocke.scalajack/yaml/primitives.plain/ValueClassPrim.scala diff --git a/core/src/test/scala/co.blocke.scalajack/yaml/primitives/AnyPrim.scala b/src_old/test/scala/co.blocke.scalajack/yaml/primitives/AnyPrim.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/yaml/primitives/AnyPrim.scala rename to src_old/test/scala/co.blocke.scalajack/yaml/primitives/AnyPrim.scala diff --git a/core/src/test/scala/co.blocke.scalajack/yaml/primitives/JavaPrim.scala b/src_old/test/scala/co.blocke.scalajack/yaml/primitives/JavaPrim.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/yaml/primitives/JavaPrim.scala rename to src_old/test/scala/co.blocke.scalajack/yaml/primitives/JavaPrim.scala diff --git a/core/src/test/scala/co.blocke.scalajack/yaml/primitives/Model.scala b/src_old/test/scala/co.blocke.scalajack/yaml/primitives/Model.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/yaml/primitives/Model.scala rename to src_old/test/scala/co.blocke.scalajack/yaml/primitives/Model.scala diff --git a/core/src/test/scala/co.blocke.scalajack/yaml/primitives/ScalaPrim.scala b/src_old/test/scala/co.blocke.scalajack/yaml/primitives/ScalaPrim.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/yaml/primitives/ScalaPrim.scala rename to src_old/test/scala/co.blocke.scalajack/yaml/primitives/ScalaPrim.scala diff --git a/core/src/test/scala/co.blocke.scalajack/yaml/primitives/TimePrim.scala b/src_old/test/scala/co.blocke.scalajack/yaml/primitives/TimePrim.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/yaml/primitives/TimePrim.scala rename to src_old/test/scala/co.blocke.scalajack/yaml/primitives/TimePrim.scala diff --git a/core/src/test/scala/co.blocke.scalajack/yaml/primitives/ValueClassPrim.scala b/src_old/test/scala/co.blocke.scalajack/yaml/primitives/ValueClassPrim.scala similarity index 100% rename from core/src/test/scala/co.blocke.scalajack/yaml/primitives/ValueClassPrim.scala rename to src_old/test/scala/co.blocke.scalajack/yaml/primitives/ValueClassPrim.scala From 4d7bb3ccf184ec60a66a305a0cbba7a4da3e9f90 Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Tue, 17 Oct 2023 13:25:37 -0500 Subject: [PATCH 02/65] config ability added --- .../scala/co.blocke.scalajack/Codec.scala | 18 +-- .../co.blocke.scalajack/json/JsonConfig.scala | 6 + .../co.blocke.scalajack/json/JsonError.scala | 4 + .../co.blocke.scalajack/json/JsonWriter.scala | 143 ++++++++++-------- .../scala/co.blocke.scalajack/run/Play.scala | 12 +- .../co.blocke.scalajack/run/Sample.scala | 5 +- 6 files changed, 109 insertions(+), 79 deletions(-) create mode 100644 src/main/scala/co.blocke.scalajack/json/JsonConfig.scala create mode 100644 src/main/scala/co.blocke.scalajack/json/JsonError.scala diff --git a/src/main/scala/co.blocke.scalajack/Codec.scala b/src/main/scala/co.blocke.scalajack/Codec.scala index 5e789029..1044f551 100644 --- a/src/main/scala/co.blocke.scalajack/Codec.scala +++ b/src/main/scala/co.blocke.scalajack/Codec.scala @@ -8,14 +8,14 @@ import json.* object Codec: - inline def write[T](t: T): String = ${ writeImpl[T]('t) } + inline def write[T](t: T)(using cfg: JsonConfig = JsonConfig()): String = ${ writeImpl[T]('t, 'cfg) } - def writeImpl[T:Type](t: Expr[T])(using q: Quotes): Expr[String] = - import quotes.reflect.* + def writeImpl[T: Type](t: Expr[T], cfg: Expr[JsonConfig])(using q: Quotes): Expr[String] = + import quotes.reflect.* - val rtRef = ReflectOnType[T](q)(TypeRepr.of[T])(using scala.collection.mutable.Map.empty[TypedName, Boolean]) - val fn = JsonWriter.writeJsonFn[T](rtRef) - '{ - val sb = new StringBuilder() - $fn($t, sb).toString - } + val rtRef = ReflectOnType[T](q)(TypeRepr.of[T])(using scala.collection.mutable.Map.empty[TypedName, Boolean]) + val fn = JsonWriter.writeJsonFn[T](rtRef) + '{ + val sb = new StringBuilder() + $fn($t, sb, $cfg).toString + } diff --git a/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala b/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala new file mode 100644 index 00000000..37ca8ee5 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala @@ -0,0 +1,6 @@ +package co.blocke.scalajack +package json + +case class JsonConfig( + enumsAsIds: Char | List[String] = Nil // Char is '*' for all enums as ids +) \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json/JsonError.scala b/src/main/scala/co.blocke.scalajack/json/JsonError.scala new file mode 100644 index 00000000..a2ab9a13 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/JsonError.scala @@ -0,0 +1,4 @@ +package co.blocke.scalajack +package json + +class JsonError(msg: String) extends Throwable \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala b/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala index 4224c61d..f2ce08d5 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala @@ -3,74 +3,91 @@ package json import co.blocke.scala_reflection.RTypeRef import co.blocke.scala_reflection.reflect.rtypeRefs.* -import scala.quoted.* +import co.blocke.scala_reflection.rtypes.* +import scala.quoted.* object JsonWriter: - def writeJsonFn[T](rtRef: RTypeRef[T])(using Type[T])(using q: Quotes): Expr[(T,StringBuilder) => StringBuilder] = - import quotes.reflect.* - rtRef match - case rt: PrimitiveRef[?] if rt.isStringish => - '{(a:T, sb:StringBuilder) => - sb.append('"') - sb.append(a.toString) - sb.append('"') - } - case rt: PrimitiveRef[?] => - '{(a:T, sb:StringBuilder) => sb.append(a.toString) } + def writeJsonFn[T](rtRef: RTypeRef[T])(using tt: Type[T], q: Quotes): Expr[(T, StringBuilder, JsonConfig) => StringBuilder] = + import quotes.reflect.* + rtRef match + case rt: PrimitiveRef[?] if rt.isStringish => + '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => + sb.append('"') + sb.append(a.toString) + sb.append('"') + } + case rt: PrimitiveRef[?] => + '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => sb.append(a.toString) } - case rt: AliasRef[?] => - writeJsonFn[rt.T](rt.unwrappedType.asInstanceOf[RTypeRef[rt.T]])(using Type.of[rt.T]) + case rt: AliasRef[?] => + writeJsonFn[rt.T](rt.unwrappedType.asInstanceOf[RTypeRef[rt.T]])(using Type.of[rt.T]) - case rt: ArrayRef[?] => - rt.elementRef.refType match - case '[t] => - val elementFn = writeJsonFn[t](rt.elementRef.asInstanceOf[RTypeRef[t]]) - '{(a:T, sb:StringBuilder) => - sb.append('[') - a.asInstanceOf[Array[?]].map{ e => - $elementFn(e.asInstanceOf[t],sb) - sb.append(',') - } - sb.setCharAt(sb.length()-1,']') - } + case rt: ArrayRef[?] => + rt.elementRef.refType match + case '[t] => + val elementFn = writeJsonFn[t](rt.elementRef.asInstanceOf[RTypeRef[t]]) + '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => + sb.append('[') + a.asInstanceOf[Array[?]].map { e => + $elementFn(e.asInstanceOf[t], sb, cfg: JsonConfig) + sb.append(',') + } + sb.setCharAt(sb.length() - 1, ']') + } - case rt: ClassRef[?] => - val fieldFns = rt.fields.map{ f => - f.fieldRef.refType match - case '[t] => (writeJsonFn[t](f.fieldRef.asInstanceOf[RTypeRef[t]]), f) - } - '{(a:T, sb:StringBuilder) => - sb.append('{') - ${ - val stmts = fieldFns.map{ case (fn, field) => - field.fieldRef.refType match - case '[t] => - '{ - sb.append(${Expr(field.name)}) - sb.append(':') - val fieldValue = ${ - Select.unique('{ a }.asTerm, field.name).asExpr - } - val fn2 = $fn.asInstanceOf[(t, StringBuilder) => StringBuilder] - fn2(fieldValue.asInstanceOf[t], sb) - sb.append(',') - } - } - Expr.ofList(stmts) + case rt: ClassRef[?] => + val fieldFns = rt.fields.map { f => + f.fieldRef.refType match + case '[t] => (writeJsonFn[t](f.fieldRef.asInstanceOf[RTypeRef[t]]), f) + } + '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => + sb.append('{') + ${ + val stmts = fieldFns.map { case (fn, field) => + field.fieldRef.refType match + case '[t] => + '{ + sb.append(${ Expr(field.name) }) + sb.append(':') + val fieldValue = ${ + Select.unique('{ a }.asTerm, field.name).asExpr } - sb.setCharAt(sb.length()-1,'}') - } + val fn2 = $fn.asInstanceOf[(t, StringBuilder, JsonConfig) => StringBuilder] + fn2(fieldValue.asInstanceOf[t], sb, cfg: JsonConfig) + sb.append(',') + } + } + Expr.ofList(stmts) + } + sb.setCharAt(sb.length() - 1, '}') + } - case rt: SeqRef[?] => - rt.elementRef.refType match - case '[t] => - val elementFn = writeJsonFn[t](rt.elementRef.asInstanceOf[RTypeRef[t]]) - '{(a:T, sb:StringBuilder) => - sb.append('[') - a.asInstanceOf[Seq[?]].map{ e => - $elementFn(e.asInstanceOf[t],sb) - sb.append(',') - } - sb.setCharAt(sb.length()-1,']') - } \ No newline at end of file + case rt: SeqRef[?] => + rt.elementRef.refType match + case '[t] => + val elementFn = writeJsonFn[t](rt.elementRef.asInstanceOf[RTypeRef[t]]) + '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => + sb.append('[') + a.asInstanceOf[Seq[?]].map { e => + $elementFn(e.asInstanceOf[t], sb, cfg: JsonConfig) + sb.append(',') + } + sb.setCharAt(sb.length() - 1, ']') + } + + case rt: EnumRef[?] => + val expr = rt.expr + '{ + val rtype = $expr.asInstanceOf[EnumRType[?]] + (a: T, sb: StringBuilder, cfg: JsonConfig) => + val enumAsId = cfg.enumsAsIds match + case '*' => true + case aList: List[String] if aList.contains(rtype.name) => true + case _ => false + if enumAsId then sb.append(rtype.ordinal(a.toString).getOrElse(throw new JsonError(s"Value $a is not a valid enum value for ${rtype.name}"))) + else + sb.append('"') + sb.append(a.toString) + sb.append('"') + } diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index 1c73d974..bf43558a 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -1,17 +1,17 @@ package co.blocke.scalajack package run -import co.blocke.scala_reflection.* +import co.blocke.scala_reflection.* object RunMe extends App: - val p = Person("Greg", 57, List(false,true,true)) + val p = Person("Greg", 57, List(false, true, true), Colors.Blue) - val i: Array[Int] = Array(1,2,3) + val i: Array[Int] = Array(1, 2, 3) - println(Codec.write(p)) + println(Codec.write(p)(using json.JsonConfig(List("co.blocke.scalajack.run.ColorsX")))) - // println(RType.of[Person].pretty) +// println(RType.of[Person].pretty) /* @@ -31,4 +31,4 @@ def writeImpl[T:Type](t: Expr[T])(using quotes: Quotes): Expr[String] = import quotes.reflect.* -*/ \ No newline at end of file + */ diff --git a/src/main/scala/co.blocke.scalajack/run/Sample.scala b/src/main/scala/co.blocke.scalajack/run/Sample.scala index 74860ac5..b9623f93 100644 --- a/src/main/scala/co.blocke.scalajack/run/Sample.scala +++ b/src/main/scala/co.blocke.scalajack/run/Sample.scala @@ -1,3 +1,6 @@ package co.blocke.scalajack.run -case class Person(name: String, age: Int, isOk: List[Boolean]) \ No newline at end of file +case class Person(name: String, age: Int, isOk: List[Boolean], favColor: Colors) + +enum Colors: + case Red, Blue, Green From 8cb99108ba271bd7fa4707e073a161f5bf30fa76 Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Wed, 18 Oct 2023 00:07:12 -0500 Subject: [PATCH 03/65] core json write working --- .../co.blocke.scalajack/json/JsonConfig.scala | 9 +- .../co.blocke.scalajack/json/JsonError.scala | 2 +- .../co.blocke.scalajack/json/JsonWriter.scala | 300 ++++++++++++++++-- .../scala/co.blocke.scalajack/run/Play.scala | 9 +- .../co.blocke.scalajack/run/Sample.scala | 11 +- 5 files changed, 297 insertions(+), 34 deletions(-) diff --git a/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala b/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala index 37ca8ee5..bc3d2afc 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala @@ -2,5 +2,10 @@ package co.blocke.scalajack package json case class JsonConfig( - enumsAsIds: Char | List[String] = Nil // Char is '*' for all enums as ids -) \ No newline at end of file + noneAsNull: Boolean = false, + tryFailureHandling: TryOption = TryOption.NO_WRITE, + enumsAsIds: Char | List[String] = Nil // Char is '*' for all enums as ids +) + +enum TryOption: + case AS_NULL, NO_WRITE, ERR_MSG_STRING diff --git a/src/main/scala/co.blocke.scalajack/json/JsonError.scala b/src/main/scala/co.blocke.scalajack/json/JsonError.scala index a2ab9a13..cc11598f 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonError.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonError.scala @@ -1,4 +1,4 @@ package co.blocke.scalajack package json -class JsonError(msg: String) extends Throwable \ No newline at end of file +class JsonError(msg: String) extends Throwable diff --git a/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala b/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala index f2ce08d5..5faebf48 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala @@ -1,13 +1,30 @@ package co.blocke.scalajack package json -import co.blocke.scala_reflection.RTypeRef import co.blocke.scala_reflection.reflect.rtypeRefs.* +import co.blocke.scala_reflection.reflect.ReflectOnType +import co.blocke.scala_reflection.{RTypeRef, TypedName} import co.blocke.scala_reflection.rtypes.* +import co.blocke.scala_reflection.Liftables.TypedNameToExpr import scala.quoted.* +import co.blocke.scala_reflection.RType +import scala.jdk.CollectionConverters.* +import java.util.concurrent.ConcurrentHashMap +import scala.util.{Failure, Success} object JsonWriter: + // Tests whether we should write something or not--mainly in the case of Option, or wrapped Option + def isOkToWrite(a: Any, cfg: JsonConfig) = + a match + case None if !cfg.noneAsNull => false + case Left(None) if !cfg.noneAsNull => false + case Right(None) if !cfg.noneAsNull => false + case Failure(_) if cfg.tryFailureHandling == TryOption.NO_WRITE => false + case _ => true + + val refCache = new ConcurrentHashMap[TypedName, (?, StringBuilder, JsonConfig) => StringBuilder] + def writeJsonFn[T](rtRef: RTypeRef[T])(using tt: Type[T], q: Quotes): Expr[(T, StringBuilder, JsonConfig) => StringBuilder] = import quotes.reflect.* rtRef match @@ -21,7 +38,8 @@ object JsonWriter: '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => sb.append(a.toString) } case rt: AliasRef[?] => - writeJsonFn[rt.T](rt.unwrappedType.asInstanceOf[RTypeRef[rt.T]])(using Type.of[rt.T]) + val fn = writeJsonFn[rt.T](rt.unwrappedType.asInstanceOf[RTypeRef[rt.T]])(using Type.of[rt.T]) + '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => $fn(a, sb, cfg) } case rt: ArrayRef[?] => rt.elementRef.refType match @@ -29,11 +47,14 @@ object JsonWriter: val elementFn = writeJsonFn[t](rt.elementRef.asInstanceOf[RTypeRef[t]]) '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => sb.append('[') - a.asInstanceOf[Array[?]].map { e => - $elementFn(e.asInstanceOf[t], sb, cfg: JsonConfig) - sb.append(',') + val sbLen = sb.length + a.asInstanceOf[Array[t]].foreach { e => + if isOkToWrite(e, cfg) then + $elementFn(e.asInstanceOf[t], sb, cfg) + sb.append(',') } - sb.setCharAt(sb.length() - 1, ']') + if sbLen == sb.length then sb.append(']') + else sb.setCharAt(sb.length() - 1, ']') } case rt: ClassRef[?] => @@ -41,26 +62,75 @@ object JsonWriter: f.fieldRef.refType match case '[t] => (writeJsonFn[t](f.fieldRef.asInstanceOf[RTypeRef[t]]), f) } - '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - sb.append('{') - ${ - val stmts = fieldFns.map { case (fn, field) => - field.fieldRef.refType match - case '[t] => - '{ - sb.append(${ Expr(field.name) }) - sb.append(':') - val fieldValue = ${ - Select.unique('{ a }.asTerm, field.name).asExpr + val typedName = Expr(rt.typedName) + '{ + val classFn = (a: T, sb: StringBuilder, cfg: JsonConfig) => + sb.append('{') + val sbLen = sb.length + ${ + val stmts = fieldFns.map { case (fn, field) => + '{ + val fieldValue = ${ + Select.unique('{ a }.asTerm, field.name).asExpr + } + if isOkToWrite(fieldValue, cfg) then + ${ + field.fieldRef.refType match + case '[t] => + '{ + sb.append(${ Expr(field.name) }) + sb.append(':') + val fn2 = $fn.asInstanceOf[(t, StringBuilder, JsonConfig) => StringBuilder] + fn2(fieldValue.asInstanceOf[t], sb, cfg) + sb.append(',') + } } - val fn2 = $fn.asInstanceOf[(t, StringBuilder, JsonConfig) => StringBuilder] - fn2(fieldValue.asInstanceOf[t], sb, cfg: JsonConfig) - sb.append(',') + } + } + Expr.ofList(stmts) + } + if sbLen == sb.length then sb.append('}') + else sb.setCharAt(sb.length() - 1, '}') + refCache.put($typedName, classFn) + classFn + } + + case rt: TraitRef[?] => + val fieldFns = rt.fields.map { f => + f.fieldRef.refType match + case '[t] => (writeJsonFn[t](f.fieldRef.asInstanceOf[RTypeRef[t]]), f) + } + val typedName = Expr(rt.typedName) + '{ + val traitFn = (a: T, sb: StringBuilder, cfg: JsonConfig) => + sb.append('{') + val sbLen = sb.length + ${ + val stmts = fieldFns.map { case (fn, field) => + '{ + val fieldValue = ${ + Select.unique('{ a }.asTerm, field.name).asExpr } + if isOkToWrite(fieldValue, cfg) then + ${ + field.fieldRef.refType match + case '[t] => + '{ + sb.append(${ Expr(field.name) }) + sb.append(':') + val fn2 = $fn.asInstanceOf[(t, StringBuilder, JsonConfig) => StringBuilder] + fn2(fieldValue.asInstanceOf[t], sb, cfg) + sb.append(',') + } + } + } + } + Expr.ofList(stmts) } - Expr.ofList(stmts) - } - sb.setCharAt(sb.length() - 1, '}') + if sbLen == sb.length then sb.append('}') + else sb.setCharAt(sb.length() - 1, '}') + refCache.put($typedName, traitFn) + traitFn } case rt: SeqRef[?] => @@ -69,13 +139,83 @@ object JsonWriter: val elementFn = writeJsonFn[t](rt.elementRef.asInstanceOf[RTypeRef[t]]) '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => sb.append('[') - a.asInstanceOf[Seq[?]].map { e => - $elementFn(e.asInstanceOf[t], sb, cfg: JsonConfig) - sb.append(',') + val sbLen = sb.length + a.asInstanceOf[Seq[?]].foreach { e => + if isOkToWrite(e, cfg) then + $elementFn(e.asInstanceOf[t], sb, cfg) + sb.append(',') } - sb.setCharAt(sb.length() - 1, ']') + if sbLen == sb.length then sb.append(']') + else sb.setCharAt(sb.length() - 1, ']') } + case rt: OptionRef[?] => + rt.optionParamType.refType match + case '[t] => + val fn = writeJsonFn[t](rt.optionParamType.asInstanceOf[RTypeRef[t]]) + '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => + a match + case None => sb.append("null") + case Some(v) => $fn(v.asInstanceOf[t], sb, cfg) + } + + case rt: TryRef[?] => + rt.tryRef.refType match + case '[t] => + val fn = writeJsonFn[t](rt.tryRef.asInstanceOf[RTypeRef[t]]) + '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => + a match + case Success(v) => $fn(v.asInstanceOf[t], sb, cfg) + case Failure(_) if cfg.tryFailureHandling == TryOption.AS_NULL => sb.append("null") + case Failure(v) => + sb.append('"') + sb.append(v.getMessage) + sb.append('"') + } + + case rt: MapRef[?] => + rt.elementRef.refType match + case '[k] => + rt.elementRef2.refType match + case '[v] => + val keyFn = writeJsonFn[k](rt.elementRef.asInstanceOf[RTypeRef[k]]) + val valueFn = writeJsonFn[v](rt.elementRef.asInstanceOf[RTypeRef[v]]) + '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => + sb.append('{') + val sbLen = sb.length + a.asInstanceOf[Map[?, ?]].foreach { case (key, value) => + if isOkToWrite(value, cfg) then + $keyFn(key.asInstanceOf[k], sb, cfg) + sb.append(':') + $valueFn(value.asInstanceOf[v], sb, cfg) + sb.append(',') + } + if sbLen == sb.length then sb.append('}') + else sb.setCharAt(sb.length() - 1, '}') + } + + case rt: LeftRightRef[?] => + rt.leftRef.refType match + case '[lt] => + val leftFn = writeJsonFn[lt](rt.leftRef.asInstanceOf[RTypeRef[lt]]) + rt.rightRef.refType match + case '[rt] => + val rightFn = writeJsonFn[rt](rt.rightRef.asInstanceOf[RTypeRef[rt]]) + val rtypeExpr = rt.expr + '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => + $rtypeExpr match + case r if r.clazz == classOf[Either[_, _]] => + a match + case Left(v) => + $leftFn(v.asInstanceOf[lt], sb, cfg) + case Right(v) => + $rightFn(v.asInstanceOf[rt], sb, cfg) + case _: RType[?] => // Intersection & Union types.... take your best shot! It's all we've got. No definitive info here. + val trial = new StringBuilder() + if scala.util.Try($rightFn(a.asInstanceOf[rt], trial, cfg)).isFailure then $leftFn(a.asInstanceOf[lt], sb, cfg) + else sb ++= trial + } + case rt: EnumRef[?] => val expr = rt.expr '{ @@ -91,3 +231,109 @@ object JsonWriter: sb.append(a.toString) sb.append('"') } + + case rt: SealedTraitRef[?] => + '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => + sb.append('"') + a.toString + sb.append('"') + } + + case rt: TupleRef[?] => + val elementFns = rt.tupleRefs.map { f => + f.refType match + case '[t] => (writeJsonFn[t](f.asInstanceOf[RTypeRef[t]]), f) + } + val numElementsExpr = Expr(elementFns.size) + '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => + sb.append('[') + val sbLen = sb.length + ${ + val stmts = elementFns.zipWithIndex.map { case ((fn, e), i) => + '{ + val fieldValue = ${ + Select.unique('{ a }.asTerm, "_" + (i + 1)).asExpr + } + ${ + e.refType match + case '[t] => + '{ + val fn2 = $fn.asInstanceOf[(t, StringBuilder, JsonConfig) => StringBuilder] + fn2(fieldValue.asInstanceOf[t], sb, cfg) + sb.append(',') + } + } + } + } + Expr.ofList(stmts) + } + if sbLen == sb.length then sb.append(']') + else sb.setCharAt(sb.length() - 1, ']') + } + + case rt: SelfRefRef[?] => + val e = rt.expr + '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => + val fn = refCache.get($e.typedName).asInstanceOf[(T, StringBuilder, JsonConfig) => StringBuilder] + fn(a, sb, cfg) + } + + case rt: JavaCollectionRef[?] => + rt.elementRef.refType match + case '[t] => + val elementFn = writeJsonFn[t](rt.elementRef.asInstanceOf[RTypeRef[t]]) + '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => + sb.append('[') + val sbLen = sb.length + a.asInstanceOf[java.util.Collection[?]].toArray.foreach { e => + if isOkToWrite(e, cfg) then + $elementFn(e.asInstanceOf[t], sb, cfg) + sb.append(',') + } + if sbLen == sb.length then sb.append(']') + else sb.setCharAt(sb.length() - 1, ']') + } + + case rt: JavaMapRef[?] => + rt.elementRef.refType match + case '[k] => + rt.elementRef2.refType match + case '[v] => + val keyFn = writeJsonFn[k](rt.elementRef.asInstanceOf[RTypeRef[k]]) + val valueFn = writeJsonFn[v](rt.elementRef.asInstanceOf[RTypeRef[v]]) + '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => + sb.append('{') + val sbLen = sb.length + a.asInstanceOf[java.util.Map[?, ?]].asScala.foreach { case (key, value) => + if isOkToWrite(value, cfg) then + $keyFn(key.asInstanceOf[k], sb, cfg) + sb.append(':') + $valueFn(value.asInstanceOf[v], sb, cfg) + sb.append(',') + } + if sbLen == sb.length then sb.append('}') + else sb.setCharAt(sb.length() - 1, '}') + } + + case rt: ObjectRef => + val name = Expr(rt.name) + '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => + sb.append('"') + sb.append($name) + sb.append('"') + } + + case rt: Scala2Ref[?] => + val name = Expr(rt.name) + '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => + sb.append('"') + sb.append($name) + sb.append('"') + } + + case rt: UnknownRef[?] => + '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => + sb.append('"') + sb.append("unknown") + sb.append('"') + } diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index bf43558a..2241bd06 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -5,11 +5,14 @@ import co.blocke.scala_reflection.* object RunMe extends App: - val p = Person("Greg", 57, List(false, true, true), Colors.Blue) + // val p = Person("Greg", 57, List(false, true, true), Colors.Blue, "Fred".asInstanceOf[BigName]) - val i: Array[Int] = Array(1, 2, 3) + // println(Codec.write(p)(using json.JsonConfig(tryFailureHandling = json.TryOption.ERR_MSG_STRING))) - println(Codec.write(p)(using json.JsonConfig(List("co.blocke.scalajack.run.ColorsX")))) + val animal: Dog = Dog("fido", 4, 2, Some(Dog("Mindy", 4, 0, None))) + println(Codec.write(animal)(using json.JsonConfig())) + + // println(RType.of[Person].pretty) // println(RType.of[Person].pretty) /* diff --git a/src/main/scala/co.blocke.scalajack/run/Sample.scala b/src/main/scala/co.blocke.scalajack/run/Sample.scala index b9623f93..dd70c7ce 100644 --- a/src/main/scala/co.blocke.scalajack/run/Sample.scala +++ b/src/main/scala/co.blocke.scalajack/run/Sample.scala @@ -1,6 +1,15 @@ package co.blocke.scalajack.run -case class Person(name: String, age: Int, isOk: List[Boolean], favColor: Colors) +opaque type BigName = String + +case class Person(name: String, age: Int, isOk: List[Boolean], favColor: Colors, boss: BigName) + +trait Animal: + val name: String + val numLegs: Int + val friend: Option[Animal] + +case class Dog(name: String, numLegs: Int, carsChased: Int, friend: Option[Animal]) extends Animal enum Colors: case Red, Blue, Green From 0304f9e1f84a390bd1ebb07b1ce86c9e32f707ae Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Thu, 19 Oct 2023 12:18:33 -0500 Subject: [PATCH 04/65] ugly but inTermsOf works --- .../co.blocke.scalajack/ReflectUtil.scala | 119 ++++++++++++++++++ .../co.blocke.scalajack/json/JsonConfig.scala | 1 + .../co.blocke.scalajack/json/JsonWriter.scala | 80 +++++++++++- .../scala/co.blocke.scalajack/run/Play.scala | 37 ++---- 4 files changed, 205 insertions(+), 32 deletions(-) create mode 100644 src/main/scala/co.blocke.scalajack/ReflectUtil.scala diff --git a/src/main/scala/co.blocke.scalajack/ReflectUtil.scala b/src/main/scala/co.blocke.scalajack/ReflectUtil.scala new file mode 100644 index 00000000..d361bfb7 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/ReflectUtil.scala @@ -0,0 +1,119 @@ +package co.blocke.scalajack + +import co.blocke.scala_reflection.{RType, RTypeRef, TypedName, ReflectException} +import co.blocke.scala_reflection.rtypes.TraitRType +import co.blocke.scala_reflection.reflect.rtypeRefs.* +import co.blocke.scala_reflection.reflect.* +import scala.quoted.* +import scala.quoted.staging.* + +object ReflectUtil: + + def makeTraitFn(): (String, Quotes) => Expr[List[?]] = + (className: String, q: Quotes) => + import q.reflect.* + implicit val q2 = q + + val clazz = Class.forName(className) + val classRef = ReflectOnType(quotes)(quotes.reflect.TypeRepr.typeConstructorOf(clazz), false)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) + println("HERE: "+classRef) + + Expr(Nil) + + + +// inline def mcr(f: (String => String) => String): String = ${ mcrImpl('f) } + +// def mcrImpl(ef: Expr[(String => String) => String])(using Quotes): Expr[String] = +// import quotes.reflect.* +// val customLambda = Lambda( +// owner = Symbol.spliceOwner, +// tpe = MethodType(List("x"))( +// paramInfosExp = methodType => List(TypeRepr.of[String]), +// resultTypeExp = methodType => TypeRepr.of[String] +// ), +// rhsFn = (sym: Symbol, paramRefs: List[Tree]) => { +// val x = paramRefs.head.asExprOf[String] +// '{ +// ${ +// Expr(t.toString) +// } +// "Hello, "+ $x +// }.asTerm +// } +// ) +// '{ $ef(${customLambda.asExprOf[String => String]}) } + + + +// PLAN: Make a runtime-level fn that bundles of() and inTermsOf, keeps everything at the ref level and +// utilizes staging/Compiler for Quotes. Not pretty, but cuts the burden of needing 2 calls to staging. + + + + + + + /* + def of(clazz: Class[?]): RType[?] = + rtypeCache.getOrElse( + clazz.getName, { + val newRType = { + val fn = (quotes: Quotes) ?=> reflect.ReflectOnType(quotes)(quotes.reflect.TypeRepr.typeConstructorOf(clazz), false)(using scala.collection.mutable.Map.empty[TypedName, Boolean]).expr + run(fn) + }.asInstanceOf[RType[?]] + rtypeCache.synchronized { + rtypeCache.put(clazz.getName, newRType) + } + newRType + } + ) + val classRType = of(clazz).asInstanceOf[rtypes.ScalaClassRType[_]] + */ + + def inTermsOf[T](traitType: RType[?], a: T, sb: StringBuilder, cfg: json.JsonConfig): Unit = + given Compiler = Compiler.make(getClass.getClassLoader) + + val clazz = a.getClass + + val fn = (quotes: Quotes) ?=> { + import quotes.reflect.* + + val classRef = ReflectOnType(quotes)(quotes.reflect.TypeRepr.typeConstructorOf(clazz), false)(using scala.collection.mutable.Map.empty[TypedName, Boolean]).asInstanceOf[ScalaClassRef[?]] + + val inTermsOfRef = traitType match + case ttype: TraitRType[?] if ttype.typeParamSymbols.nonEmpty => + val seenBefore = scala.collection.mutable.Map.empty[TypedName, Boolean] + val paths = classRef.typePaths.getOrElse(ttype.name, throw new ReflectException(s"No path in class ${classRef.name} for trait ${ttype.name}")) + ttype.toType(quotes) match + case '[t] => + val typeParamTypes = TypeSymbolMapper.runPath(quotes)(paths, TypeRepr.of[t]) + val classQuotedTypeRepr = TypeRepr.typeConstructorOf(clazz) + ReflectOnType(quotes)(classQuotedTypeRepr.appliedTo(typeParamTypes))(using seenBefore) + + case traitRef: TraitRType[?] => classRef + case x => throw new ReflectException(s"${x.name} is not of type trait") + + inTermsOfRef.refType match + case '[t] => + val asClassRef = inTermsOfRef.asInstanceOf[ScalaClassRef[t]].copy(renderHint = true) + json.JsonWriter.writeJsonFn[t](asClassRef.asInstanceOf[RTypeRef[t]]) + } + val writeFn = run(fn) + val writeFnTyped = writeFn.asInstanceOf[(T, StringBuilder, json.JsonConfig) => StringBuilder] + writeFnTyped(a, sb, cfg) + + + + + /* + + At compile-time: + + * Build a function that accepts a string and prints it + + At run-time: + + * Run the function with a.getClass.getName + + */ \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala b/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala index bc3d2afc..4ade349c 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala @@ -4,6 +4,7 @@ package json case class JsonConfig( noneAsNull: Boolean = false, tryFailureHandling: TryOption = TryOption.NO_WRITE, + typeHint: String = "_hint", enumsAsIds: Char | List[String] = Nil // Char is '*' for all enums as ids ) diff --git a/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala b/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala index 5faebf48..883bda92 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala @@ -2,7 +2,7 @@ package co.blocke.scalajack package json import co.blocke.scala_reflection.reflect.rtypeRefs.* -import co.blocke.scala_reflection.reflect.ReflectOnType +import co.blocke.scala_reflection.reflect.* import co.blocke.scala_reflection.{RTypeRef, TypedName} import co.blocke.scala_reflection.rtypes.* import co.blocke.scala_reflection.Liftables.TypedNameToExpr @@ -27,6 +27,12 @@ object JsonWriter: def writeJsonFn[T](rtRef: RTypeRef[T])(using tt: Type[T], q: Quotes): Expr[(T, StringBuilder, JsonConfig) => StringBuilder] = import quotes.reflect.* + + // def liftFunction[T: Type, U: Type](f: T => U): Expr[T => U] = { + // val fnExpr: Expr[T => U] = '{ (x: T) => ${ f('x) } } + // fnExpr + // } + rtRef match case rt: PrimitiveRef[?] if rt.isStringish => '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => @@ -68,7 +74,22 @@ object JsonWriter: sb.append('{') val sbLen = sb.length ${ - val stmts = fieldFns.map { case (fn, field) => + val hintStmt = (rt match + case s: ScalaClassRef[?] if s.renderHint => + '{ + sb.append('"') + sb.append(cfg.typeHint) + sb.append('"') + sb.append(':') + sb.append('"') + sb.append(a.getClass.getName) + sb.append('"') + sb.append(',') + () + } + case _ => + ).asInstanceOf[Expr[Unit]] + val stmts = hintStmt +: fieldFns.map { case (fn, field) => '{ val fieldValue = ${ Select.unique('{ a }.asTerm, field.name).asExpr @@ -96,16 +117,61 @@ object JsonWriter: } case rt: TraitRef[?] => - val fieldFns = rt.fields.map { f => - f.fieldRef.refType match - case '[t] => (writeJsonFn[t](f.fieldRef.asInstanceOf[RTypeRef[t]]), f) + val typedName = Expr(rt.typedName) + val traitType = rt.expr + '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => + ReflectUtil.inTermsOf[T]($traitType, a, sb, cfg) + sb } + + // def inTermsOf[T](a: T, sb: StringBuilder, cfg: json.JsonConfig) + /* + case rt: TraitRef[?] => val typedName = Expr(rt.typedName) + val zf = (s: String) => println("HERE: " + s) + // val zz = liftFunction(zf) '{ val traitFn = (a: T, sb: StringBuilder, cfg: JsonConfig) => sb.append('{') + sb.append(cfg.typeHint) + sb.append(':') + sb.append('"') + sb.append(a.getClass.getName) + sb.append('"') val sbLen = sb.length + + def f(g: String => String): String = g(a.getClass.getName) + val s = ReflectUtil.mcr(f) + println("S: " + s) + + // ${ + // val f = Expr((s: String) => println("HERE: " + s)) + // '{ f(a.getClass.getName) } + // lazy val z = '{ foo() } + // println("HERE: " + z.value) + // Expr(Nil) + // } + + // val q2 = quotes + // val traitFn: (String, Quotes) => Expr[List[?]] = ReflectUtil.makeTraitFn() + // traitFn(a.getClass.getName, q2) + + /* + val inTermsOf = RType.inTermsOf[T](a.getClass).asInstanceOf[ScalaClassRType[T]] + type U = inTermsOf.T ${ + import quotes.reflect.* + val seenBefore = scala.collection.mutable.Map.empty[TypedName, Boolean] + val inTermsOfRef = ReflectOnType[U](quotes)(TypeRepr.of[U])(using seenBefore) + println("--3-- " + inTermsOfRef) // PROBLEM <-- This is a TypeSymbolRef, and should be a class + + val fieldFns = inTermsOfRef + .asInstanceOf[ScalaClassRef[?]] + .fields + .map { f => + f.fieldRef.refType match + case '[t] => (writeJsonFn[t](f.fieldRef.asInstanceOf[RTypeRef[t]]), f) + } val stmts = fieldFns.map { case (fn, field) => '{ val fieldValue = ${ @@ -125,13 +191,15 @@ object JsonWriter: } } } - Expr.ofList(stmts) + Expr.ofList(Nil) } + */ if sbLen == sb.length then sb.append('}') else sb.setCharAt(sb.length() - 1, '}') refCache.put($typedName, traitFn) traitFn } + */ case rt: SeqRef[?] => rt.elementRef.refType match diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index 2241bd06..bf63b092 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -5,33 +5,18 @@ import co.blocke.scala_reflection.* object RunMe extends App: - // val p = Person("Greg", 57, List(false, true, true), Colors.Blue, "Fred".asInstanceOf[BigName]) + val p = Person("Greg", 57, List(false, true, true), Colors.Blue, "Fred".asInstanceOf[BigName]) - // println(Codec.write(p)(using json.JsonConfig(tryFailureHandling = json.TryOption.ERR_MSG_STRING))) + val d = Dog("Fido", 4, 2, Some(Dog("Mindy", 4, 0, None))) - val animal: Dog = Dog("fido", 4, 2, Some(Dog("Mindy", 4, 0, None))) - println(Codec.write(animal)(using json.JsonConfig())) + val t0 = System.currentTimeMillis() + val cfg = json.JsonConfig() + // for i <- 0 to 10000 do + // val x = Codec.write(p)(using cfg) + // if i % 100 == 0 then println(i) + // if i == 10000 then println(x) - // println(RType.of[Person].pretty) + println(Codec.write(d)(using cfg)) -// println(RType.of[Person].pretty) -/* - - -val SJConfig = .... (JSON or Binary fmt specified in config) - -val sj = ScalaJack(using SJConfig) - -sj.write(person) -sj.read[Person](js): Either[Error,Person] - - -JsonFlavor: - -def write[T](t: T): String = ${ writeImpl[T]('t) } - -def writeImpl[T:Type](t: Expr[T])(using quotes: Quotes): Expr[String] = - import quotes.reflect.* - - - */ + val t1 = System.currentTimeMillis() + println("TIME: " + (t1 - t0)) From eed5e6d624082122d58c409123c40264275d50ab Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Thu, 19 Oct 2023 17:30:40 -0500 Subject: [PATCH 05/65] Added caching for trait writers --- .../co.blocke.scalajack/ReflectUtil.scala | 119 ------------------ .../co.blocke.scalajack/json/JsonWriter.scala | 94 ++------------ .../json/ReflectUtil.scala | 53 ++++++++ .../scala/co.blocke.scalajack/run/Play.scala | 18 ++- 4 files changed, 77 insertions(+), 207 deletions(-) delete mode 100644 src/main/scala/co.blocke.scalajack/ReflectUtil.scala create mode 100644 src/main/scala/co.blocke.scalajack/json/ReflectUtil.scala diff --git a/src/main/scala/co.blocke.scalajack/ReflectUtil.scala b/src/main/scala/co.blocke.scalajack/ReflectUtil.scala deleted file mode 100644 index d361bfb7..00000000 --- a/src/main/scala/co.blocke.scalajack/ReflectUtil.scala +++ /dev/null @@ -1,119 +0,0 @@ -package co.blocke.scalajack - -import co.blocke.scala_reflection.{RType, RTypeRef, TypedName, ReflectException} -import co.blocke.scala_reflection.rtypes.TraitRType -import co.blocke.scala_reflection.reflect.rtypeRefs.* -import co.blocke.scala_reflection.reflect.* -import scala.quoted.* -import scala.quoted.staging.* - -object ReflectUtil: - - def makeTraitFn(): (String, Quotes) => Expr[List[?]] = - (className: String, q: Quotes) => - import q.reflect.* - implicit val q2 = q - - val clazz = Class.forName(className) - val classRef = ReflectOnType(quotes)(quotes.reflect.TypeRepr.typeConstructorOf(clazz), false)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) - println("HERE: "+classRef) - - Expr(Nil) - - - -// inline def mcr(f: (String => String) => String): String = ${ mcrImpl('f) } - -// def mcrImpl(ef: Expr[(String => String) => String])(using Quotes): Expr[String] = -// import quotes.reflect.* -// val customLambda = Lambda( -// owner = Symbol.spliceOwner, -// tpe = MethodType(List("x"))( -// paramInfosExp = methodType => List(TypeRepr.of[String]), -// resultTypeExp = methodType => TypeRepr.of[String] -// ), -// rhsFn = (sym: Symbol, paramRefs: List[Tree]) => { -// val x = paramRefs.head.asExprOf[String] -// '{ -// ${ -// Expr(t.toString) -// } -// "Hello, "+ $x -// }.asTerm -// } -// ) -// '{ $ef(${customLambda.asExprOf[String => String]}) } - - - -// PLAN: Make a runtime-level fn that bundles of() and inTermsOf, keeps everything at the ref level and -// utilizes staging/Compiler for Quotes. Not pretty, but cuts the burden of needing 2 calls to staging. - - - - - - - /* - def of(clazz: Class[?]): RType[?] = - rtypeCache.getOrElse( - clazz.getName, { - val newRType = { - val fn = (quotes: Quotes) ?=> reflect.ReflectOnType(quotes)(quotes.reflect.TypeRepr.typeConstructorOf(clazz), false)(using scala.collection.mutable.Map.empty[TypedName, Boolean]).expr - run(fn) - }.asInstanceOf[RType[?]] - rtypeCache.synchronized { - rtypeCache.put(clazz.getName, newRType) - } - newRType - } - ) - val classRType = of(clazz).asInstanceOf[rtypes.ScalaClassRType[_]] - */ - - def inTermsOf[T](traitType: RType[?], a: T, sb: StringBuilder, cfg: json.JsonConfig): Unit = - given Compiler = Compiler.make(getClass.getClassLoader) - - val clazz = a.getClass - - val fn = (quotes: Quotes) ?=> { - import quotes.reflect.* - - val classRef = ReflectOnType(quotes)(quotes.reflect.TypeRepr.typeConstructorOf(clazz), false)(using scala.collection.mutable.Map.empty[TypedName, Boolean]).asInstanceOf[ScalaClassRef[?]] - - val inTermsOfRef = traitType match - case ttype: TraitRType[?] if ttype.typeParamSymbols.nonEmpty => - val seenBefore = scala.collection.mutable.Map.empty[TypedName, Boolean] - val paths = classRef.typePaths.getOrElse(ttype.name, throw new ReflectException(s"No path in class ${classRef.name} for trait ${ttype.name}")) - ttype.toType(quotes) match - case '[t] => - val typeParamTypes = TypeSymbolMapper.runPath(quotes)(paths, TypeRepr.of[t]) - val classQuotedTypeRepr = TypeRepr.typeConstructorOf(clazz) - ReflectOnType(quotes)(classQuotedTypeRepr.appliedTo(typeParamTypes))(using seenBefore) - - case traitRef: TraitRType[?] => classRef - case x => throw new ReflectException(s"${x.name} is not of type trait") - - inTermsOfRef.refType match - case '[t] => - val asClassRef = inTermsOfRef.asInstanceOf[ScalaClassRef[t]].copy(renderHint = true) - json.JsonWriter.writeJsonFn[t](asClassRef.asInstanceOf[RTypeRef[t]]) - } - val writeFn = run(fn) - val writeFnTyped = writeFn.asInstanceOf[(T, StringBuilder, json.JsonConfig) => StringBuilder] - writeFnTyped(a, sb, cfg) - - - - - /* - - At compile-time: - - * Build a function that accepts a string and prints it - - At run-time: - - * Run the function with a.getClass.getName - - */ \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala b/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala index 883bda92..3fb84bba 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala @@ -87,9 +87,10 @@ object JsonWriter: sb.append(',') () } - case _ => - ).asInstanceOf[Expr[Unit]] - val stmts = hintStmt +: fieldFns.map { case (fn, field) => + case _ => '{ () } + ) // .asInstanceOf[Expr[Unit]] + val stmts = hintStmt :: fieldFns.map { case (fn, field) => + // val stmts = fieldFns.map { case (fn, field) => '{ val fieldValue = ${ Select.unique('{ a }.asTerm, field.name).asExpr @@ -120,87 +121,16 @@ object JsonWriter: val typedName = Expr(rt.typedName) val traitType = rt.expr '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - ReflectUtil.inTermsOf[T]($traitType, a, sb, cfg) - sb + val comboName = ($traitType.typedName.toString + "::" + a.getClass.getName).asInstanceOf[TypedName] + Option(refCache.get(comboName)) match + case Some(writerFn) => + writerFn.asInstanceOf[(T, StringBuilder, JsonConfig) => StringBuilder](a, sb, cfg) + case None => + val writerFn = ReflectUtil.inTermsOf[T]($traitType, a, sb, cfg) + refCache.put(comboName, writerFn) + writerFn(a, sb, cfg) } - // def inTermsOf[T](a: T, sb: StringBuilder, cfg: json.JsonConfig) - /* - case rt: TraitRef[?] => - val typedName = Expr(rt.typedName) - val zf = (s: String) => println("HERE: " + s) - // val zz = liftFunction(zf) - '{ - val traitFn = (a: T, sb: StringBuilder, cfg: JsonConfig) => - sb.append('{') - sb.append(cfg.typeHint) - sb.append(':') - sb.append('"') - sb.append(a.getClass.getName) - sb.append('"') - val sbLen = sb.length - - def f(g: String => String): String = g(a.getClass.getName) - val s = ReflectUtil.mcr(f) - println("S: " + s) - - // ${ - // val f = Expr((s: String) => println("HERE: " + s)) - // '{ f(a.getClass.getName) } - // lazy val z = '{ foo() } - // println("HERE: " + z.value) - // Expr(Nil) - // } - - // val q2 = quotes - // val traitFn: (String, Quotes) => Expr[List[?]] = ReflectUtil.makeTraitFn() - // traitFn(a.getClass.getName, q2) - - /* - val inTermsOf = RType.inTermsOf[T](a.getClass).asInstanceOf[ScalaClassRType[T]] - type U = inTermsOf.T - ${ - import quotes.reflect.* - val seenBefore = scala.collection.mutable.Map.empty[TypedName, Boolean] - val inTermsOfRef = ReflectOnType[U](quotes)(TypeRepr.of[U])(using seenBefore) - println("--3-- " + inTermsOfRef) // PROBLEM <-- This is a TypeSymbolRef, and should be a class - - val fieldFns = inTermsOfRef - .asInstanceOf[ScalaClassRef[?]] - .fields - .map { f => - f.fieldRef.refType match - case '[t] => (writeJsonFn[t](f.fieldRef.asInstanceOf[RTypeRef[t]]), f) - } - val stmts = fieldFns.map { case (fn, field) => - '{ - val fieldValue = ${ - Select.unique('{ a }.asTerm, field.name).asExpr - } - if isOkToWrite(fieldValue, cfg) then - ${ - field.fieldRef.refType match - case '[t] => - '{ - sb.append(${ Expr(field.name) }) - sb.append(':') - val fn2 = $fn.asInstanceOf[(t, StringBuilder, JsonConfig) => StringBuilder] - fn2(fieldValue.asInstanceOf[t], sb, cfg) - sb.append(',') - } - } - } - } - Expr.ofList(Nil) - } - */ - if sbLen == sb.length then sb.append('}') - else sb.setCharAt(sb.length() - 1, '}') - refCache.put($typedName, traitFn) - traitFn - } - */ - case rt: SeqRef[?] => rt.elementRef.refType match case '[t] => diff --git a/src/main/scala/co.blocke.scalajack/json/ReflectUtil.scala b/src/main/scala/co.blocke.scalajack/json/ReflectUtil.scala new file mode 100644 index 00000000..78d034d8 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/ReflectUtil.scala @@ -0,0 +1,53 @@ +package co.blocke.scalajack +package json + +import co.blocke.scala_reflection.{ReflectException, RType, RTypeRef, TypedName} +import co.blocke.scala_reflection.rtypes.TraitRType +import co.blocke.scala_reflection.reflect.rtypeRefs.* +import co.blocke.scala_reflection.reflect.* +import scala.quoted.* +import scala.quoted.staging.* + +object ReflectUtil: + + /** + * This function takes the RType of a trait and an instance of T and does two things. + * First it expresses the instance's class *in terms of* the trait's concrete type parameters (if any). + * Then it generates a writer function for the now correctly-typed class. + * + * @param traitType + * @param a + * @param sb + * @param cfg + * @return + */ + def inTermsOf[T](traitType: RType[?], a: T, sb: StringBuilder, cfg: JsonConfig) = + given Compiler = Compiler.make(getClass.getClassLoader) + + val clazz = a.getClass + + val fn = (quotes: Quotes) ?=> { + import quotes.reflect.* + + val classRef = ReflectOnType(quotes)(quotes.reflect.TypeRepr.typeConstructorOf(clazz), false)(using scala.collection.mutable.Map.empty[TypedName, Boolean]).asInstanceOf[ScalaClassRef[?]] + + val inTermsOfRef = traitType match + case ttype: TraitRType[?] if ttype.typeParamSymbols.nonEmpty => + val seenBefore = scala.collection.mutable.Map.empty[TypedName, Boolean] + val paths = classRef.typePaths.getOrElse(ttype.name, throw new ReflectException(s"No path in class ${classRef.name} for trait ${ttype.name}")) + ttype.toType(quotes) match + case '[t] => + val typeParamTypes = TypeSymbolMapper.runPath(quotes)(paths, TypeRepr.of[t]) + val classQuotedTypeRepr = TypeRepr.typeConstructorOf(clazz) + ReflectOnType(quotes)(classQuotedTypeRepr.appliedTo(typeParamTypes))(using seenBefore) + + case traitRef: TraitRType[?] => classRef + case x => throw new ReflectException(s"${x.name} is not of type trait") + + inTermsOfRef.refType match + case '[t] => + val asClassRef = inTermsOfRef.asInstanceOf[ScalaClassRef[t]].copy(renderHint = true) + JsonWriter.writeJsonFn[t](asClassRef.asInstanceOf[RTypeRef[t]]) + } + val writeFn = run(fn) + writeFn.asInstanceOf[(T, StringBuilder, JsonConfig) => StringBuilder] diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index bf63b092..2b9b3146 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -8,15 +8,21 @@ object RunMe extends App: val p = Person("Greg", 57, List(false, true, true), Colors.Blue, "Fred".asInstanceOf[BigName]) val d = Dog("Fido", 4, 2, Some(Dog("Mindy", 4, 0, None))) + val d2 = Dog("Spot", 4, 3, Some(Dog("Floppy", 3, 1, None))) - val t0 = System.currentTimeMillis() val cfg = json.JsonConfig() - // for i <- 0 to 10000 do - // val x = Codec.write(p)(using cfg) - // if i % 100 == 0 then println(i) - // if i == 10000 then println(x) - println(Codec.write(d)(using cfg)) + val t0 = System.currentTimeMillis() + for i <- 0 to 10000 do + Codec.write(d)(using cfg) + if i % 100 == 0 then println(i) + // if i == 10000 then println(i) + + // println(Codec.write(d)(using cfg)) + // println("") + // println(Codec.write(d2)(using cfg)) + // println("") + // println(Codec.write(d)(using cfg)) val t1 = System.currentTimeMillis() println("TIME: " + (t1 - t0)) From d11003e4935331e36e248c6107bcacff516d81d3 Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Thu, 19 Oct 2023 23:31:02 -0500 Subject: [PATCH 06/65] writing json is working --- .../{Codec.scala => ScalaJack.scala} | 2 +- .../co.blocke.scalajack/json/JsonConfig.scala | 9 ++++++-- .../co.blocke.scalajack/json/JsonWriter.scala | 15 +++++++------ .../json/ReflectUtil.scala | 5 ++--- .../scala/co.blocke.scalajack/run/Play.scala | 21 +++++++++++++++---- .../co.blocke.scalajack/run/Sample.scala | 7 ++++++- 6 files changed, 40 insertions(+), 19 deletions(-) rename src/main/scala/co.blocke.scalajack/{Codec.scala => ScalaJack.scala} (97%) diff --git a/src/main/scala/co.blocke.scalajack/Codec.scala b/src/main/scala/co.blocke.scalajack/ScalaJack.scala similarity index 97% rename from src/main/scala/co.blocke.scalajack/Codec.scala rename to src/main/scala/co.blocke.scalajack/ScalaJack.scala index 1044f551..04a3a5ec 100644 --- a/src/main/scala/co.blocke.scalajack/Codec.scala +++ b/src/main/scala/co.blocke.scalajack/ScalaJack.scala @@ -6,7 +6,7 @@ import scala.quoted.* import quoted.Quotes import json.* -object Codec: +object ScalaJack: inline def write[T](t: T)(using cfg: JsonConfig = JsonConfig()): String = ${ writeImpl[T]('t, 'cfg) } diff --git a/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala b/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala index 4ade349c..ff3b80af 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala @@ -4,8 +4,13 @@ package json case class JsonConfig( noneAsNull: Boolean = false, tryFailureHandling: TryOption = TryOption.NO_WRITE, - typeHint: String = "_hint", - enumsAsIds: Char | List[String] = Nil // Char is '*' for all enums as ids + // -------------------------- + typeHintLabel: String = "_hint", + typeHintLabelByTrait: Map[String, String] = Map.empty[String, String], // Trait name -> type hint label + typeHintDefaultTransformer: String => String = (v: String) => v, // in case you want something different than class name (simple name re-mapping) + typeHintTransformer: Map[String, Any => String] = Map.empty[String, Any => String], // if you want class-specific control (instance value => String) + // -------------------------- + enumsAsIds: Char | List[String] = Nil // Char is '*' for all enums as ids, or a list of fully-qualified class names ) enum TryOption: diff --git a/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala b/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala index 3fb84bba..67968593 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala @@ -28,11 +28,6 @@ object JsonWriter: def writeJsonFn[T](rtRef: RTypeRef[T])(using tt: Type[T], q: Quotes): Expr[(T, StringBuilder, JsonConfig) => StringBuilder] = import quotes.reflect.* - // def liftFunction[T: Type, U: Type](f: T => U): Expr[T => U] = { - // val fnExpr: Expr[T => U] = '{ (x: T) => ${ f('x) } } - // fnExpr - // } - rtRef match case rt: PrimitiveRef[?] if rt.isStringish => '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => @@ -75,14 +70,18 @@ object JsonWriter: val sbLen = sb.length ${ val hintStmt = (rt match - case s: ScalaClassRef[?] if s.renderHint => + case s: ScalaClassRef[?] if s.renderTrait.isDefined => + val traitName = Expr(s.renderTrait.get) '{ sb.append('"') - sb.append(cfg.typeHint) + sb.append(cfg.typeHintLabelByTrait.getOrElse($traitName, cfg.typeHintLabel)) sb.append('"') sb.append(':') sb.append('"') - sb.append(a.getClass.getName) + val hint = cfg.typeHintTransformer.get(a.getClass.getName) match + case Some(xform) => xform(a) + case None => cfg.typeHintDefaultTransformer(a.getClass.getName) + sb.append(hint) sb.append('"') sb.append(',') () diff --git a/src/main/scala/co.blocke.scalajack/json/ReflectUtil.scala b/src/main/scala/co.blocke.scalajack/json/ReflectUtil.scala index 78d034d8..950e333c 100644 --- a/src/main/scala/co.blocke.scalajack/json/ReflectUtil.scala +++ b/src/main/scala/co.blocke.scalajack/json/ReflectUtil.scala @@ -10,8 +10,7 @@ import scala.quoted.staging.* object ReflectUtil: - /** - * This function takes the RType of a trait and an instance of T and does two things. + /** This function takes the RType of a trait and an instance of T and does two things. * First it expresses the instance's class *in terms of* the trait's concrete type parameters (if any). * Then it generates a writer function for the now correctly-typed class. * @@ -46,7 +45,7 @@ object ReflectUtil: inTermsOfRef.refType match case '[t] => - val asClassRef = inTermsOfRef.asInstanceOf[ScalaClassRef[t]].copy(renderHint = true) + val asClassRef = inTermsOfRef.asInstanceOf[ScalaClassRef[t]].copy(renderTrait = Some(traitType.name)) JsonWriter.writeJsonFn[t](asClassRef.asInstanceOf[RTypeRef[t]]) } val writeFn = run(fn) diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index 2b9b3146..e7d84673 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -10,12 +10,25 @@ object RunMe extends App: val d = Dog("Fido", 4, 2, Some(Dog("Mindy", 4, 0, None))) val d2 = Dog("Spot", 4, 3, Some(Dog("Floppy", 3, 1, None))) - val cfg = json.JsonConfig() - println(Codec.write(d)(using cfg)) + val mapper = (a: Any) => + a.getClass.getPackage.getName match + case p if p.startsWith("co.blocke") => "Blocke" + case x => "Something " + x.getClass.getName + + given json.JsonConfig = json + .JsonConfig() + .copy( + typeHintDefaultTransformer = (s: String) => s.split("\\.").last, + typeHintLabelByTrait = Map("co.blocke.scalajack.run.Animal" -> "kind"), + typeHintTransformer = Map("co.blocke.scalajack.run.Dog" -> mapper) + ) + + println(ScalaJack.write(d)) + val t0 = System.currentTimeMillis() for i <- 0 to 10000 do - Codec.write(d)(using cfg) - if i % 100 == 0 then println(i) + ScalaJack.write(d) + // if i % 100 == 0 then println(i) // if i == 10000 then println(i) // println(Codec.write(d)(using cfg)) diff --git a/src/main/scala/co.blocke.scalajack/run/Sample.scala b/src/main/scala/co.blocke.scalajack/run/Sample.scala index dd70c7ce..577c467a 100644 --- a/src/main/scala/co.blocke.scalajack/run/Sample.scala +++ b/src/main/scala/co.blocke.scalajack/run/Sample.scala @@ -9,7 +9,12 @@ trait Animal: val numLegs: Int val friend: Option[Animal] -case class Dog(name: String, numLegs: Int, carsChased: Int, friend: Option[Animal]) extends Animal +trait Animal2: + val name: String + val numLegs: Int + val friend: Option[Animal2] + +case class Dog(name: String, numLegs: Int, carsChased: Int, friend: Option[Animal2]) extends Animal2 enum Colors: case Red, Blue, Green From 766f55a1e806e5478192d34fd6e9973c1ae22538 Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Mon, 23 Oct 2023 00:26:58 -0500 Subject: [PATCH 07/65] Rudimentary read working --- .../scala/co.blocke.scalajack/ScalaJack.scala | 19 +++ .../co.blocke.scalajack/json/JsonConfig.scala | 1 + .../co.blocke.scalajack/json/JsonParser.scala | 157 ++++++++++++++++++ .../co.blocke.scalajack/json/JsonReader.scala | 114 +++++++++++++ .../co.blocke.scalajack/json/JsonWriter.scala | 2 +- .../scala/co.blocke.scalajack/run/Play.scala | 55 ++++++ 6 files changed, 347 insertions(+), 1 deletion(-) create mode 100644 src/main/scala/co.blocke.scalajack/json/JsonParser.scala create mode 100644 src/main/scala/co.blocke.scalajack/json/JsonReader.scala diff --git a/src/main/scala/co.blocke.scalajack/ScalaJack.scala b/src/main/scala/co.blocke.scalajack/ScalaJack.scala index 04a3a5ec..751d7bb6 100644 --- a/src/main/scala/co.blocke.scalajack/ScalaJack.scala +++ b/src/main/scala/co.blocke.scalajack/ScalaJack.scala @@ -2,6 +2,7 @@ package co.blocke.scalajack import co.blocke.scala_reflection.TypedName import co.blocke.scala_reflection.reflect.ReflectOnType +import co.blocke.scala_reflection.reflect.rtypeRefs.ClassRef import scala.quoted.* import quoted.Quotes import json.* @@ -19,3 +20,21 @@ object ScalaJack: val sb = new StringBuilder() $fn($t, sb, $cfg).toString } + + inline def read[T](js: String)(using cfg: JsonConfig = JsonConfig()): T = ${ readImpl[T]('js, 'cfg) } + + def readImpl[T: Type](js: Expr[String], cfg: Expr[JsonConfig])(using q: Quotes): Expr[T] = + import quotes.reflect.* + + val classRef = ReflectOnType[T](quotes)(TypeRepr.of[T], true)(using scala.collection.mutable.Map.empty[TypedName, Boolean]).asInstanceOf[ClassRef[T]] + val parseTable = JsonReader.classParseMap[T](classRef) + val instantiator = JsonReader.classInstantiator[T](classRef) + '{ // run-time + val parser = JsonParser($js) + val classFieldMap = $parseTable(parser) // Map[String, JsonConfig => Either[ParseError, ?]] + parser.expectClass[T]($cfg, classFieldMap, $instantiator) match + case Right(v) => v + case Left(t) => + println("BOOM: " + t.msg) + throw t + } diff --git a/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala b/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala index ff3b80af..8982166c 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala @@ -3,6 +3,7 @@ package json case class JsonConfig( noneAsNull: Boolean = false, + forbidNullsInInput: Boolean = false, tryFailureHandling: TryOption = TryOption.NO_WRITE, // -------------------------- typeHintLabel: String = "_hint", diff --git a/src/main/scala/co.blocke.scalajack/json/JsonParser.scala b/src/main/scala/co.blocke.scalajack/json/JsonParser.scala new file mode 100644 index 00000000..b17b9834 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/JsonParser.scala @@ -0,0 +1,157 @@ +package co.blocke.scalajack +package json + +import scala.util.* + +case class ParseError(msg: String) extends Throwable + +case class JsonParser( js: String ): + + private val jsChars: Array[Char] = js.toCharArray + private var i = 0 + private val max: Int = jsChars.length + + // Housekeeping + //------------------------------ + @inline def nullCheck: Boolean = + val res = i+3 + i += 1 + val mark = i + while (i < max && jsChars(i) != '"') i += 1 + i += 1 + Right(js.substring(mark, i-1)) + case x => Left(ParseError(s"Unexpected character '$x' where beginning of label expected at position [$i]")) + + + // Data Types + //------------------------------ + def expectBoolean(cfg: JsonConfig): Either[ParseError,Boolean] = + jsChars(i) match + case 't' if i+3 + i += 4 + Right(true) + case 'f' if i+4 + i += 5 + Right(false) + case x => Left(ParseError(s"Unexpected character '$x' where beginning of boolean value expected at position [$i]")) + + def expectLong(cfg: JsonConfig): Either[ParseError,Long] = + val mark = i + var done = false + while !done do + jsChars(i) match + case c if (c >= '0' && c <= '9') || c == '-' => i += 1 + case _ => done = true + Try( js.substring(mark,i).toLong ) match + case Success(g) => Right(g) + case Failure(f) => + val msg = if mark == i then + s"""Int/Long expected but couldn't parse from "${jsChars(i)}" at position [$i]""" + else + s"""Int/Long expected but couldn't parse from "${js.substring(mark,i)}" at position [$i]""" + i = mark + Left(ParseError(msg)) + + def expectDouble(cfg: JsonConfig): Either[ParseError,Double] = + val mark = i + var done = false + while !done do + jsChars(i) match + case c if (c >= '0' && c <= '9') || c == '-' || c == '.' || c == 'e' || c == 'E' || c == '+' => i += 1 + case _ => done = true + Try( js.substring(mark,i-1).toDouble ) match + case Success(g) => Right(g) + case Failure(_) => + val msg = if mark == i then + s"Float/Double expected but couldn't parse from \"${jsChars(i)}\" at position [$i]" + else + s"Float/Double expected but couldn't parse from \"${js.substring(mark,i-1)}\" at position [$i]" + i = mark + Left(ParseError(msg)) + + def expectOption[T](cfg: JsonConfig, expectElement: JsonConfig=>Either[ParseError,T]): Either[ParseError, Option[T]] = + nullCheck match + case false => expectElement(cfg).map(t => Some(t)) + case true if cfg.noneAsNull => Right(None) + case true if cfg.forbidNullsInInput => Left(ParseError(s"Forbidden 'null' value received at position [$i]")) + case true => Right(Some(null.asInstanceOf[T])) + + def expectList[T]( cfg: JsonConfig, expectElement: JsonConfig=>Either[ParseError,T]): Either[ParseError,List[T]] = + if jsChars(i) != '[' then Left(ParseError(s"Beginning of list expected at position [$i]")) + else + i += 1 + eatWhitespace + val acc = scala.collection.mutable.ListBuffer.empty[T] + var done: Option[Either[ParseError,List[T]]] = None + while done.isEmpty do + (for { + el <- expectElement(cfg) + _ = acc.addOne(el) + _ <- expectComma + } yield el) match + case Left(_) if jsChars(i) == ']' => + i += 1 + eatWhitespace + done = Some(Right(acc.toList)) + case Left(e) => + done = Some(Left(e)) + case Right(_) => + done.get + + def expectClass[T]( + cfg: JsonConfig, + fieldMap: Map[String, (JsonConfig) => Either[ParseError, ?]], + instantiator: Map[String, ?] => T + ): Either[ParseError,T] = + if jsChars(i) != '{' then Left(ParseError(s"Beginning of object expected at position [$i]")) + else + i += 1 + eatWhitespace + var done: Option[Either[ParseError,T]] = None + val fields = scala.collection.mutable.HashMap.empty[String,Any] + while done.isEmpty do + (for { + fieldLabel <- expectLabel + _ <- expectColon + fieldValue <- fieldMap(fieldLabel)(cfg) + _ = fields.put(fieldLabel, fieldValue) + _ <- expectComma + } yield fieldValue) match + case Left(_) if jsChars(i) == '}' => + i += 1 + eatWhitespace + done = Some(Right( instantiator(fields.toMap) )) // instantiate the class here!!! + case Left(e) => + done = Some(Left(e)) + case Right(_) => + done.get \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json/JsonReader.scala b/src/main/scala/co.blocke.scalajack/json/JsonReader.scala new file mode 100644 index 00000000..b3eb8e95 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/JsonReader.scala @@ -0,0 +1,114 @@ +package co.blocke.scalajack +package json + +import co.blocke.scala_reflection.reflect.rtypeRefs.* +import co.blocke.scala_reflection.reflect.* +import co.blocke.scala_reflection.{RTypeRef, TypedName, Clazzes} +import co.blocke.scala_reflection.rtypes.* +import co.blocke.scala_reflection.Liftables.TypedNameToExpr +import scala.quoted.* +import co.blocke.scala_reflection.RType +import scala.jdk.CollectionConverters.* +import java.util.concurrent.ConcurrentHashMap +import scala.util.{Failure, Success} + + +case class ParserRead(): + def expectInt(): Either[ParseError,Long] = Right(1L) + +case class Blah(msg: String, age: Int, isOk: Boolean) + + +object JsonReader: + + def classInstantiator[T:Type]( ref: ClassRef[T] )(using Quotes): Expr[Map[String, ?] => T] = + import quotes.reflect.* + val sym = TypeRepr.of[T].classSymbol.get + '{ + (fieldMap: Map[String, ?]) => + ${ + val tree = Apply(Select.unique(New(TypeIdent(sym)), ""), + ref.fields.map{f => + f.fieldRef.refType match + case '[t] => + '{ fieldMap(${Expr(f.name)}).asInstanceOf[t] }.asTerm + } + ) + tree.asExpr.asExprOf[T] + } + } + + def classParseMap[T:Type]( ref: ClassRef[T] )(using Quotes): Expr[JsonParser => Map[String, JsonConfig=>Either[ParseError, ?]]] = + '{ + (parser: JsonParser) => + val daList = ${ + val fieldList = ref.fields.map(f => f.fieldRef match + case t: PrimitiveRef[?] if t.name == Clazzes.CHAR_CLASS => + '{ + ${Expr(f.name)} -> {(j:JsonConfig)=> + for { + strVal <- parser.expectLabel + charVal = strVal.toArray.headOption match + case Some(c) => Right(c) + case None => ParseError(s"Cannot convert value '$strVal' into a Char.") + } yield charVal + } + } + case t: PrimitiveRef[?] if t.family == PrimFamily.Stringish => + '{ + ${Expr(f.name)} -> {(j:JsonConfig)=>parser.expectLabel} + } + case t: PrimitiveRef[?] if t.name == Clazzes.INT_CLASS => + '{ + ${Expr(f.name)} -> {(j:JsonConfig)=> + for { + longVal <- parser.expectLong(j) + intVal = longVal.toInt + } yield intVal + } + } + case t: PrimitiveRef[?] if t.name == Clazzes.SHORT_CLASS => + '{ + ${Expr(f.name)} -> {(j:JsonConfig)=> + for { + longVal <- parser.expectLong(j) + intVal = longVal.toShort + } yield intVal + } + } + case t: PrimitiveRef[?] if t.name == Clazzes.BYTE_CLASS => + '{ + ${Expr(f.name)} -> {(j:JsonConfig)=> + for { + longVal <- parser.expectLong(j) + intVal = longVal.toByte + } yield intVal + } + } + case t: PrimitiveRef[?] if t.family == PrimFamily.Longish => + '{ + ${Expr(f.name)} -> {(j:JsonConfig)=>parser.expectLong(j)} + } + case t: PrimitiveRef[?] if t.name == Clazzes.FLOAT_CLASS => + '{ + ${Expr(f.name)} -> {(j:JsonConfig)=> + for { + longVal <- parser.expectDouble(j) + intVal = longVal.toFloat + } yield intVal + } + } + case t: PrimitiveRef[?] if t.family == PrimFamily.Doublish => + '{ + ${Expr(f.name)} -> {(j:JsonConfig)=>parser.expectDouble(j)} + } + case t: PrimitiveRef[?] if t.family == PrimFamily.Boolish => + '{ + ${Expr(f.name)} -> {(j:JsonConfig)=>parser.expectBoolean(j)} + } + ) + Expr.ofList(fieldList) + } + daList.asInstanceOf[List[(String, JsonConfig=>Either[ParseError, ?])]].toMap + } + diff --git a/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala b/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala index 67968593..995ef84b 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala @@ -29,7 +29,7 @@ object JsonWriter: import quotes.reflect.* rtRef match - case rt: PrimitiveRef[?] if rt.isStringish => + case rt: PrimitiveRef[?] if rt.family == PrimFamily.Stringish => '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => sb.append('"') sb.append(a.toString) diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index e7d84673..85e861e9 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -5,6 +5,37 @@ import co.blocke.scala_reflection.* object RunMe extends App: + val js = """[[123,-456],[394,-2]]""" + + val parser = json.JsonParser(js) + + /* + val f = () => parser.expectLong + val f2 = () => parser.expectList[Long](() => parser.expectLong) + val r = parser.expectList[List[Long]](f2) + + println("R: " + r) + */ + + given json.JsonConfig = json + .JsonConfig() + + try + println("RESULT: " + ScalaJack.read[json.Blah]("""{"msg":"Greg","isOk":true,"age":57}""")) + catch { + case t: Throwable => println("BOOM: " + t.getMessage) + } + + val t0 = System.currentTimeMillis() + for i <- (0 to 10000) do ScalaJack.read[json.Blah]("""{"msg":"Greg","isOk":true,"age":57}""") + val t1 = System.currentTimeMillis() + println("TIME: " + (t1 - t0)) + + // inline def read[T](js: String)(using cfg: JsonConfig = JsonConfig()): T = ${ readImpl[T]('js, 'cfg) } + + // def expectList[T]( expectElement: ()=>Either[ParseError,T]): Either[ParseError,List[T]] = + +/* val p = Person("Greg", 57, List(false, true, true), Colors.Blue, "Fred".asInstanceOf[BigName]) val d = Dog("Fido", 4, 2, Some(Dog("Mindy", 4, 0, None))) @@ -39,3 +70,27 @@ object RunMe extends App: val t1 = System.currentTimeMillis() println("TIME: " + (t1 - t0)) + */ + +/* + + case SomeRef => + + // Compile-Time + // * Quotes + // * Class details + + // Build field parse map: + Map( + "name" -> ()=>expectString() + "lists" -> ()=>expectList(()=>expectString()) + ) + + '{ + // Runtime + // * json + // * cfg + } + + + */ From b6bd07ecf80415bac9a17850f39642a21cab6edd Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Tue, 24 Oct 2023 12:22:02 -0500 Subject: [PATCH 08/65] fixed seq parsing --- .../co.blocke.scalajack/json/JsonParser.scala | 357 +++++++++++------- .../co.blocke.scalajack/json/JsonReader.scala | 174 ++++----- .../scala/co.blocke.scalajack/run/Play.scala | 11 +- 3 files changed, 301 insertions(+), 241 deletions(-) diff --git a/src/main/scala/co.blocke.scalajack/json/JsonParser.scala b/src/main/scala/co.blocke.scalajack/json/JsonParser.scala index b17b9834..890315ac 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonParser.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonParser.scala @@ -5,153 +5,220 @@ import scala.util.* case class ParseError(msg: String) extends Throwable -case class JsonParser( js: String ): - - private val jsChars: Array[Char] = js.toCharArray - private var i = 0 - private val max: Int = jsChars.length - - // Housekeeping - //------------------------------ - @inline def nullCheck: Boolean = - val res = i+3 - i += 1 - val mark = i - while (i < max && jsChars(i) != '"') i += 1 - i += 1 - Right(js.substring(mark, i-1)) - case x => Left(ParseError(s"Unexpected character '$x' where beginning of label expected at position [$i]")) - - - // Data Types - //------------------------------ - def expectBoolean(cfg: JsonConfig): Either[ParseError,Boolean] = - jsChars(i) match - case 't' if i+3 - i += 4 - Right(true) - case 'f' if i+4 - i += 5 - Right(false) - case x => Left(ParseError(s"Unexpected character '$x' where beginning of boolean value expected at position [$i]")) - - def expectLong(cfg: JsonConfig): Either[ParseError,Long] = - val mark = i - var done = false - while !done do - jsChars(i) match - case c if (c >= '0' && c <= '9') || c == '-' => i += 1 - case _ => done = true - Try( js.substring(mark,i).toLong ) match - case Success(g) => Right(g) - case Failure(f) => - val msg = if mark == i then - s"""Int/Long expected but couldn't parse from "${jsChars(i)}" at position [$i]""" - else - s"""Int/Long expected but couldn't parse from "${js.substring(mark,i)}" at position [$i]""" - i = mark - Left(ParseError(msg)) - - def expectDouble(cfg: JsonConfig): Either[ParseError,Double] = +case class JsonParser(js: String): + + private val jsChars: Array[Char] = js.toCharArray + private var i = 0 + private val max: Int = jsChars.length + + // Housekeeping + // ------------------------------ + @inline def nullCheck: Boolean = + val res = i + 3 < max && jsChars(i) == 'n' && jsChars(i + 1) == 'u' && jsChars(i + 2) == 'l' && jsChars(i + 3) == 'l' + if res then i += 4 + res + @inline def eatWhitespace: Either[ParseError, Unit] = + while i < max && jsChars(i).isWhitespace do i += 1 + Right(()) + @inline def expectComma: Either[ParseError, Unit] = // Note: this consumes whitespace before/after the ',' + for { + _ <- eatWhitespace + r <- + if jsChars(i) == ',' then + i += 1 + eatWhitespace + Right(()) + else Left(ParseError(s"Expected comma at position [$i]")) + } yield r + @inline def expectColon: Either[ParseError, Unit] = // Note: this consumes whitespace before/after the ':' + for { + _ <- eatWhitespace + r <- + if jsChars(i) == ':' then + i += 1 + eatWhitespace + Right(()) + else Left(ParseError(s"Expected colon at position [$i]")) + } yield r + + // JSON label (i.e. object key, which is always a simple string, ie no escaped/special chars) + def expectLabel: Either[ParseError, String] = + jsChars(i) match + case '"' => + i += 1 val mark = i - var done = false - while !done do - jsChars(i) match - case c if (c >= '0' && c <= '9') || c == '-' || c == '.' || c == 'e' || c == 'E' || c == '+' => i += 1 - case _ => done = true - Try( js.substring(mark,i-1).toDouble ) match - case Success(g) => Right(g) - case Failure(_) => - val msg = if mark == i then - s"Float/Double expected but couldn't parse from \"${jsChars(i)}\" at position [$i]" - else - s"Float/Double expected but couldn't parse from \"${js.substring(mark,i-1)}\" at position [$i]" - i = mark - Left(ParseError(msg)) - - def expectOption[T](cfg: JsonConfig, expectElement: JsonConfig=>Either[ParseError,T]): Either[ParseError, Option[T]] = - nullCheck match - case false => expectElement(cfg).map(t => Some(t)) - case true if cfg.noneAsNull => Right(None) - case true if cfg.forbidNullsInInput => Left(ParseError(s"Forbidden 'null' value received at position [$i]")) - case true => Right(Some(null.asInstanceOf[T])) - - def expectList[T]( cfg: JsonConfig, expectElement: JsonConfig=>Either[ParseError,T]): Either[ParseError,List[T]] = - if jsChars(i) != '[' then Left(ParseError(s"Beginning of list expected at position [$i]")) - else + while i < max && jsChars(i) != '"' do i += 1 + i += 1 + Right(js.substring(mark, i - 1)) + case x => Left(ParseError(s"Unexpected character '$x' where beginning of label expected at position [$i]")) + + // Data Types + // ------------------------------ + def expectBoolean(cfg: JsonConfig, p: JsonParser): Either[ParseError, Boolean] = + jsChars(i) match + case 't' if i + 3 < max && jsChars(i + 1) == 'r' && jsChars(i + 2) == 'u' && jsChars(i + 3) == 'e' => + i += 4 + Right(true) + case 'f' if i + 4 < max && jsChars(i + 1) == 'a' && jsChars(i + 2) == 'l' && jsChars(i + 3) == 's' && jsChars(i + 3) == 'e' => + i += 5 + Right(false) + case x => Left(ParseError(s"Unexpected character '$x' where beginning of boolean value expected at position [$i]")) + + def expectLong(cfg: JsonConfig, p: JsonParser): Either[ParseError, Long] = + val mark = i + var done = false + while !done do + jsChars(i) match + case c if (c >= '0' && c <= '9') || c == '-' => i += 1 + case _ => done = true + Try(js.substring(mark, i).toLong) match + case Success(g) => Right(g) + case Failure(f) => + val msg = + if mark == i then s"""Int/Long expected but couldn't parse from "${jsChars(i)}" at position [$i]""" + else s"""Int/Long expected but couldn't parse from "${js.substring(mark, i)}" at position [$i]""" + i = mark + Left(ParseError(msg)) + + def expectDouble(cfg: JsonConfig, p: JsonParser): Either[ParseError, Double] = + val mark = i + var done = false + while !done do + jsChars(i) match + case c if (c >= '0' && c <= '9') || c == '-' || c == '.' || c == 'e' || c == 'E' || c == '+' => i += 1 + case _ => done = true + Try(js.substring(mark, i - 1).toDouble) match + case Success(g) => Right(g) + case Failure(_) => + val msg = + if mark == i then s"Float/Double expected but couldn't parse from \"${jsChars(i)}\" at position [$i]" + else s"Float/Double expected but couldn't parse from \"${js.substring(mark, i - 1)}\" at position [$i]" + i = mark + Left(ParseError(msg)) + + def expectString(cfg: JsonConfig, p: JsonParser): Either[ParseError, String] = + nullCheck match + case true if cfg.forbidNullsInInput => Left(ParseError(s"Forbidden 'null' value received at position [$i]")) + case true => Right(null.asInstanceOf[String]) + case false => + jsChars(i) match + case '"' => + i += 1 + val mark = i // save position in case we need complex string parse + var captured: Option[String] = None + while i < max && jsChars(i) != '"' do + jsChars(i) match + case '\\' => // oops! special char found--do slow string parse + i = mark + captured = Some(_expectString) + case _ => i += 1 + i += 1 + Right(captured.getOrElse(js.substring(mark, i - 1))) + case x => Left(ParseError(s"Unexpected character '$x' where beginning of label expected at position [$i]")) + + def expectOption[T](cfg: JsonConfig, expectElement: (JsonConfig, JsonParser) => Either[ParseError, T]): Either[ParseError, Option[T]] = + nullCheck match + case false => expectElement(cfg, this).map(t => Some(t)) + case true if cfg.noneAsNull => Right(None) + case true if cfg.forbidNullsInInput => Left(ParseError(s"Forbidden 'null' value received at position [$i]")) + case true => Right(Some(null.asInstanceOf[T])) + + def expectList[T](cfg: JsonConfig, expectElement: (JsonConfig, JsonParser) => Either[ParseError, T]): Either[ParseError, List[T]] = + if jsChars(i) != '[' then Left(ParseError(s"Beginning of list expected at position [$i]")) + else + i += 1 + eatWhitespace + val acc = scala.collection.mutable.ListBuffer.empty[T] + var done: Option[Either[ParseError, List[T]]] = None + while done.isEmpty do + (for { + el <- expectElement(cfg, this) + _ = acc.addOne(el) + _ <- expectComma + } yield el) match + case Left(_) if jsChars(i) == ']' => i += 1 eatWhitespace - val acc = scala.collection.mutable.ListBuffer.empty[T] - var done: Option[Either[ParseError,List[T]]] = None - while done.isEmpty do - (for { - el <- expectElement(cfg) - _ = acc.addOne(el) - _ <- expectComma - } yield el) match - case Left(_) if jsChars(i) == ']' => - i += 1 - eatWhitespace - done = Some(Right(acc.toList)) - case Left(e) => - done = Some(Left(e)) - case Right(_) => - done.get - - def expectClass[T]( - cfg: JsonConfig, - fieldMap: Map[String, (JsonConfig) => Either[ParseError, ?]], - instantiator: Map[String, ?] => T - ): Either[ParseError,T] = - if jsChars(i) != '{' then Left(ParseError(s"Beginning of object expected at position [$i]")) - else + done = Some(Right(acc.toList)) + case Left(e) => + done = Some(Left(e)) + case Right(_) => + done.get + + def expectClass[T]( + cfg: JsonConfig, + fieldMap: Map[String, (JsonConfig, JsonParser) => Either[ParseError, ?]], + instantiator: Map[String, ?] => T + ): Either[ParseError, T] = + if jsChars(i) != '{' then Left(ParseError(s"Beginning of object expected at position [$i]")) + else + i += 1 + eatWhitespace + var done: Option[Either[ParseError, T]] = None + val fields = scala.collection.mutable.HashMap.empty[String, Any] + while done.isEmpty do + (for { + fieldLabel <- expectLabel + _ <- expectColon + fieldValue <- fieldMap(fieldLabel)(cfg, this) + _ = fields.put(fieldLabel, fieldValue) + _ <- expectComma + } yield fieldValue) match + case Left(_) if jsChars(i) == '}' => i += 1 eatWhitespace - var done: Option[Either[ParseError,T]] = None - val fields = scala.collection.mutable.HashMap.empty[String,Any] - while done.isEmpty do - (for { - fieldLabel <- expectLabel - _ <- expectColon - fieldValue <- fieldMap(fieldLabel)(cfg) - _ = fields.put(fieldLabel, fieldValue) - _ <- expectComma - } yield fieldValue) match - case Left(_) if jsChars(i) == '}' => - i += 1 - eatWhitespace - done = Some(Right( instantiator(fields.toMap) )) // instantiate the class here!!! - case Left(e) => - done = Some(Left(e)) - case Right(_) => - done.get \ No newline at end of file + done = Some(Right(instantiator(fields.toMap))) // instantiate the class here!!! + case Left(e) => + done = Some(Left(e)) + case Right(_) => + done.get + + // Slower String parsing that handles special escaped chars + def _expectString = + val builder = new java.lang.StringBuilder() + while i < max && jsChars(i) != '"' do + if jsChars(i) == '\\' then { + jsChars(i + 1) match { + case '"' => + builder.append('\"') + i += 2 + + case '\\' => + builder.append('\\') + i += 2 + + case 'b' => + builder.append('\b') + i += 2 + + case 'f' => + builder.append('\f') + i += 2 + + case 'n' => + builder.append('\n') + i += 2 + + case 'r' => + builder.append('\r') + i += 2 + + case 't' => + builder.append('\t') + i += 2 + + case 'u' => + val hexEncoded = js.substring(i + 2, i + 6) + val unicodeChar = Integer.parseInt(hexEncoded, 16).toChar + builder.append(unicodeChar.toString) + i += 6 + + case c => + builder.append(c) + i += 2 + } + } else { + builder.append(jsChars(i)) + i += 1 + } + builder.toString diff --git a/src/main/scala/co.blocke.scalajack/json/JsonReader.scala b/src/main/scala/co.blocke.scalajack/json/JsonReader.scala index b3eb8e95..b90f2bde 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonReader.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonReader.scala @@ -3,112 +3,104 @@ package json import co.blocke.scala_reflection.reflect.rtypeRefs.* import co.blocke.scala_reflection.reflect.* -import co.blocke.scala_reflection.{RTypeRef, TypedName, Clazzes} +import co.blocke.scala_reflection.{Clazzes, RTypeRef, TypedName} import co.blocke.scala_reflection.rtypes.* import co.blocke.scala_reflection.Liftables.TypedNameToExpr import scala.quoted.* +import scala.collection.Factory import co.blocke.scala_reflection.RType import scala.jdk.CollectionConverters.* import java.util.concurrent.ConcurrentHashMap import scala.util.{Failure, Success} - case class ParserRead(): - def expectInt(): Either[ParseError,Long] = Right(1L) - -case class Blah(msg: String, age: Int, isOk: Boolean) + def expectInt(): Either[ParseError, Long] = Right(1L) +import scala.collection.immutable.* +case class Blah(msg: String, stuff: Array[List[String]]) //, age: Int, isOk: Boolean) object JsonReader: - def classInstantiator[T:Type]( ref: ClassRef[T] )(using Quotes): Expr[Map[String, ?] => T] = + def classInstantiator[T: Type](ref: ClassRef[T])(using Quotes): Expr[Map[String, ?] => T] = import quotes.reflect.* val sym = TypeRepr.of[T].classSymbol.get - '{ - (fieldMap: Map[String, ?]) => - ${ - val tree = Apply(Select.unique(New(TypeIdent(sym)), ""), - ref.fields.map{f => - f.fieldRef.refType match - case '[t] => - '{ fieldMap(${Expr(f.name)}).asInstanceOf[t] }.asTerm - } - ) - tree.asExpr.asExprOf[T] - } + '{ (fieldMap: Map[String, ?]) => + ${ + val tree = Apply( + Select.unique(New(TypeIdent(sym)), ""), + ref.fields.map { f => + f.fieldRef.refType match + case '[t] => + '{ fieldMap(${ Expr(f.name) }).asInstanceOf[t] }.asTerm + } + ) + tree.asExpr.asExprOf[T] + } } - def classParseMap[T:Type]( ref: ClassRef[T] )(using Quotes): Expr[JsonParser => Map[String, JsonConfig=>Either[ParseError, ?]]] = - '{ - (parser: JsonParser) => - val daList = ${ - val fieldList = ref.fields.map(f => f.fieldRef match - case t: PrimitiveRef[?] if t.name == Clazzes.CHAR_CLASS => - '{ - ${Expr(f.name)} -> {(j:JsonConfig)=> - for { - strVal <- parser.expectLabel - charVal = strVal.toArray.headOption match - case Some(c) => Right(c) - case None => ParseError(s"Cannot convert value '$strVal' into a Char.") - } yield charVal - } - } - case t: PrimitiveRef[?] if t.family == PrimFamily.Stringish => - '{ - ${Expr(f.name)} -> {(j:JsonConfig)=>parser.expectLabel} - } - case t: PrimitiveRef[?] if t.name == Clazzes.INT_CLASS => - '{ - ${Expr(f.name)} -> {(j:JsonConfig)=> - for { - longVal <- parser.expectLong(j) - intVal = longVal.toInt - } yield intVal - } - } - case t: PrimitiveRef[?] if t.name == Clazzes.SHORT_CLASS => - '{ - ${Expr(f.name)} -> {(j:JsonConfig)=> - for { - longVal <- parser.expectLong(j) - intVal = longVal.toShort - } yield intVal - } - } - case t: PrimitiveRef[?] if t.name == Clazzes.BYTE_CLASS => - '{ - ${Expr(f.name)} -> {(j:JsonConfig)=> - for { - longVal <- parser.expectLong(j) - intVal = longVal.toByte - } yield intVal - } - } - case t: PrimitiveRef[?] if t.family == PrimFamily.Longish => - '{ - ${Expr(f.name)} -> {(j:JsonConfig)=>parser.expectLong(j)} - } - case t: PrimitiveRef[?] if t.name == Clazzes.FLOAT_CLASS => - '{ - ${Expr(f.name)} -> {(j:JsonConfig)=> - for { - longVal <- parser.expectDouble(j) - intVal = longVal.toFloat - } yield intVal - } - } - case t: PrimitiveRef[?] if t.family == PrimFamily.Doublish => - '{ - ${Expr(f.name)} -> {(j:JsonConfig)=>parser.expectDouble(j)} - } - case t: PrimitiveRef[?] if t.family == PrimFamily.Boolish => - '{ - ${Expr(f.name)} -> {(j:JsonConfig)=>parser.expectBoolean(j)} - } - ) - Expr.ofList(fieldList) - } - daList.asInstanceOf[List[(String, JsonConfig=>Either[ParseError, ?])]].toMap - } + def refFn[T: Type](ref: RTypeRef[T])(using Quotes): Expr[(JsonConfig, JsonParser) => Either[ParseError, T]] = + import Clazzes.* + import quotes.reflect.* + ref match + case t: PrimitiveRef[?] if t.name == BOOLEAN_CLASS => + '{ (j: JsonConfig, p: JsonParser) => p.expectBoolean(j, p).map(_.asInstanceOf[T]) } + case t: PrimitiveRef[?] if t.name == BYTE_CLASS => + '{ (j: JsonConfig, p: JsonParser) => p.expectLong(j, p).map(_.toByte.asInstanceOf[T]) } + case t: PrimitiveRef[?] if t.name == CHAR_CLASS => + '{ (j: JsonConfig, p: JsonParser) => + p.expectString(j, p) + .flatMap(s => + s.toArray.headOption match + case Some(c) => Right(c.asInstanceOf[T]) + case None => Left(ParseError(s"Cannot convert value '$s' into a Char.")) + ) + } + case t: PrimitiveRef[?] if t.name == DOUBLE_CLASS => + '{ (j: JsonConfig, p: JsonParser) => p.expectDouble(j, p).map(_.asInstanceOf[T]) } + case t: PrimitiveRef[?] if t.name == FLOAT_CLASS => + '{ (j: JsonConfig, p: JsonParser) => p.expectDouble(j, p).map(_.toFloat.asInstanceOf[T]) } + case t: PrimitiveRef[?] if t.name == INT_CLASS => + '{ (j: JsonConfig, p: JsonParser) => p.expectLong(j, p).map(_.toInt.asInstanceOf[T]) } + case t: PrimitiveRef[?] if t.name == LONG_CLASS => + '{ (j: JsonConfig, p: JsonParser) => p.expectLong(j, p).map(_.asInstanceOf[T]) } + case t: PrimitiveRef[?] if t.name == SHORT_CLASS => + '{ (j: JsonConfig, p: JsonParser) => p.expectLong(j, p).map(_.toShort.asInstanceOf[T]) } + case t: PrimitiveRef[T] if t.name == STRING_CLASS => + '{ (j: JsonConfig, p: JsonParser) => p.expectString(j, p).map(_.asInstanceOf[T]) } + + case t: SeqRef[T] => + t.refType match + case '[s] => + t.elementRef.refType match + case '[e] => + val subFn = refFn[e](t.elementRef.asInstanceOf[RTypeRef[e]]).asInstanceOf[Expr[(JsonConfig, JsonParser) => Either[ParseError, e]]] + '{ (j: JsonConfig, p: JsonParser) => + p.expectList[e](j, $subFn).map(_.to(${ Expr.summon[Factory[e, T]].get })) // Convert List to whatever the target type should be + } + case t: ArrayRef[T] => + t.refType match + case '[s] => + t.elementRef.refType match + case '[e] => + val subFn = refFn[e](t.elementRef.asInstanceOf[RTypeRef[e]]).asInstanceOf[Expr[(JsonConfig, JsonParser) => Either[ParseError, e]]] + '{ (j: JsonConfig, p: JsonParser) => + p.expectList[e](j, $subFn).map(_.to(${ Expr.summon[Factory[e, T]].get })) // Convert List to whatever the target type should be + } + + def classParseMap[T: Type](ref: ClassRef[T])(using Quotes): Expr[JsonParser => Map[String, (JsonConfig, JsonParser) => Either[ParseError, ?]]] = + import Clazzes.* + '{ (parser: JsonParser) => + val daList = ${ + val fieldList = ref.fields.map(f => + f.fieldRef.refType match + case '[m] => + val fn = refFn[m](f.fieldRef.asInstanceOf[RTypeRef[m]]) + '{ + ${ Expr(f.name) } -> $fn + } + ) + Expr.ofList(fieldList) + } + daList.toMap + } diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index 85e861e9..4dfc6277 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -21,15 +21,16 @@ object RunMe extends App: .JsonConfig() try - println("RESULT: " + ScalaJack.read[json.Blah]("""{"msg":"Greg","isOk":true,"age":57}""")) + println("RESULT: " + ScalaJack.read[json.Blah]("""{"msg":"Greg\nZoller", "stuff": [["a","b","c"],["x","y","z"]] }""")) + // println("RESULT: " + ScalaJack.read[json.Blah]("""{"msg":"Greg","isOk":true,"age":57}""")) catch { case t: Throwable => println("BOOM: " + t.getMessage) } - val t0 = System.currentTimeMillis() - for i <- (0 to 10000) do ScalaJack.read[json.Blah]("""{"msg":"Greg","isOk":true,"age":57}""") - val t1 = System.currentTimeMillis() - println("TIME: " + (t1 - t0)) + // val t0 = System.currentTimeMillis() + // for i <- (0 to 10000) do ScalaJack.read[json.Blah]("""{"msg":"Greg","isOk":true,"age":57}""") + // val t1 = System.currentTimeMillis() + // println("TIME: " + (t1 - t0)) // inline def read[T](js: String)(using cfg: JsonConfig = JsonConfig()): T = ${ readImpl[T]('js, 'cfg) } From f95e73cf1ba626637d85f953c30504a18ee60da8 Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Wed, 25 Oct 2023 11:16:37 -0500 Subject: [PATCH 09/65] enum parse works --- project/metals.sbt | 2 +- .../scala/co.blocke.scalajack/ScalaJack.scala | 4 +- .../co.blocke.scalajack/json/JsonParser.scala | 28 +++++++------ .../co.blocke.scalajack/json/JsonReader.scala | 40 +++++++++++++++---- .../scala/co.blocke.scalajack/run/Play.scala | 19 ++++++++- .../co.blocke.scalajack/run/Sample.scala | 8 ++++ 6 files changed, 74 insertions(+), 27 deletions(-) diff --git a/project/metals.sbt b/project/metals.sbt index d62c0d42..cbb25c6a 100644 --- a/project/metals.sbt +++ b/project/metals.sbt @@ -2,5 +2,5 @@ // This file enables sbt-bloop to create bloop config files. -addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.5.8") +addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.5.11") diff --git a/src/main/scala/co.blocke.scalajack/ScalaJack.scala b/src/main/scala/co.blocke.scalajack/ScalaJack.scala index 751d7bb6..00ed498f 100644 --- a/src/main/scala/co.blocke.scalajack/ScalaJack.scala +++ b/src/main/scala/co.blocke.scalajack/ScalaJack.scala @@ -34,7 +34,5 @@ object ScalaJack: val classFieldMap = $parseTable(parser) // Map[String, JsonConfig => Either[ParseError, ?]] parser.expectClass[T]($cfg, classFieldMap, $instantiator) match case Right(v) => v - case Left(t) => - println("BOOM: " + t.msg) - throw t + case Left(t) => throw t } diff --git a/src/main/scala/co.blocke.scalajack/json/JsonParser.scala b/src/main/scala/co.blocke.scalajack/json/JsonParser.scala index 890315ac..6f237078 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonParser.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonParser.scala @@ -3,7 +3,9 @@ package json import scala.util.* -case class ParseError(msg: String) extends Throwable +class ParseError(message: String) extends Throwable(message) +case class JsonParseError(message: String) extends ParseError(message) +case class CommaExpected(message: String = "") extends ParseError(message) case class JsonParser(js: String): @@ -28,7 +30,7 @@ case class JsonParser(js: String): i += 1 eatWhitespace Right(()) - else Left(ParseError(s"Expected comma at position [$i]")) + else Left(CommaExpected()) } yield r @inline def expectColon: Either[ParseError, Unit] = // Note: this consumes whitespace before/after the ':' for { @@ -38,7 +40,7 @@ case class JsonParser(js: String): i += 1 eatWhitespace Right(()) - else Left(ParseError(s"Expected colon at position [$i]")) + else Left(JsonParseError(s"Expected colon at position [$i]")) } yield r // JSON label (i.e. object key, which is always a simple string, ie no escaped/special chars) @@ -50,7 +52,7 @@ case class JsonParser(js: String): while i < max && jsChars(i) != '"' do i += 1 i += 1 Right(js.substring(mark, i - 1)) - case x => Left(ParseError(s"Unexpected character '$x' where beginning of label expected at position [$i]")) + case x => Left(JsonParseError(s"Unexpected character '$x' where beginning of label expected at position [$i]")) // Data Types // ------------------------------ @@ -62,7 +64,7 @@ case class JsonParser(js: String): case 'f' if i + 4 < max && jsChars(i + 1) == 'a' && jsChars(i + 2) == 'l' && jsChars(i + 3) == 's' && jsChars(i + 3) == 'e' => i += 5 Right(false) - case x => Left(ParseError(s"Unexpected character '$x' where beginning of boolean value expected at position [$i]")) + case x => Left(JsonParseError(s"Unexpected character '$x' where beginning of boolean value expected at position [$i]")) def expectLong(cfg: JsonConfig, p: JsonParser): Either[ParseError, Long] = val mark = i @@ -78,7 +80,7 @@ case class JsonParser(js: String): if mark == i then s"""Int/Long expected but couldn't parse from "${jsChars(i)}" at position [$i]""" else s"""Int/Long expected but couldn't parse from "${js.substring(mark, i)}" at position [$i]""" i = mark - Left(ParseError(msg)) + Left(JsonParseError(msg)) def expectDouble(cfg: JsonConfig, p: JsonParser): Either[ParseError, Double] = val mark = i @@ -94,11 +96,11 @@ case class JsonParser(js: String): if mark == i then s"Float/Double expected but couldn't parse from \"${jsChars(i)}\" at position [$i]" else s"Float/Double expected but couldn't parse from \"${js.substring(mark, i - 1)}\" at position [$i]" i = mark - Left(ParseError(msg)) + Left(JsonParseError(msg)) def expectString(cfg: JsonConfig, p: JsonParser): Either[ParseError, String] = nullCheck match - case true if cfg.forbidNullsInInput => Left(ParseError(s"Forbidden 'null' value received at position [$i]")) + case true if cfg.forbidNullsInInput => Left(JsonParseError(s"Forbidden 'null' value received at position [$i]")) case true => Right(null.asInstanceOf[String]) case false => jsChars(i) match @@ -114,17 +116,17 @@ case class JsonParser(js: String): case _ => i += 1 i += 1 Right(captured.getOrElse(js.substring(mark, i - 1))) - case x => Left(ParseError(s"Unexpected character '$x' where beginning of label expected at position [$i]")) + case x => Left(JsonParseError(s"Unexpected character '$x' where beginning of label expected at position [$i]")) def expectOption[T](cfg: JsonConfig, expectElement: (JsonConfig, JsonParser) => Either[ParseError, T]): Either[ParseError, Option[T]] = nullCheck match case false => expectElement(cfg, this).map(t => Some(t)) case true if cfg.noneAsNull => Right(None) - case true if cfg.forbidNullsInInput => Left(ParseError(s"Forbidden 'null' value received at position [$i]")) + case true if cfg.forbidNullsInInput => Left(JsonParseError(s"Forbidden 'null' value received at position [$i]")) case true => Right(Some(null.asInstanceOf[T])) def expectList[T](cfg: JsonConfig, expectElement: (JsonConfig, JsonParser) => Either[ParseError, T]): Either[ParseError, List[T]] = - if jsChars(i) != '[' then Left(ParseError(s"Beginning of list expected at position [$i]")) + if jsChars(i) != '[' then Left(JsonParseError(s"Beginning of list expected at position [$i]")) else i += 1 eatWhitespace @@ -150,7 +152,7 @@ case class JsonParser(js: String): fieldMap: Map[String, (JsonConfig, JsonParser) => Either[ParseError, ?]], instantiator: Map[String, ?] => T ): Either[ParseError, T] = - if jsChars(i) != '{' then Left(ParseError(s"Beginning of object expected at position [$i]")) + if jsChars(i) != '{' then Left(JsonParseError(s"Beginning of object expected at position [$i]")) else i += 1 eatWhitespace @@ -164,7 +166,7 @@ case class JsonParser(js: String): _ = fields.put(fieldLabel, fieldValue) _ <- expectComma } yield fieldValue) match - case Left(_) if jsChars(i) == '}' => + case Left(CommaExpected(_)) if jsChars(i) == '}' => i += 1 eatWhitespace done = Some(Right(instantiator(fields.toMap))) // instantiate the class here!!! diff --git a/src/main/scala/co.blocke.scalajack/json/JsonReader.scala b/src/main/scala/co.blocke.scalajack/json/JsonReader.scala index b90f2bde..95b3621b 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonReader.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonReader.scala @@ -11,13 +11,7 @@ import scala.collection.Factory import co.blocke.scala_reflection.RType import scala.jdk.CollectionConverters.* import java.util.concurrent.ConcurrentHashMap -import scala.util.{Failure, Success} - -case class ParserRead(): - def expectInt(): Either[ParseError, Long] = Right(1L) - -import scala.collection.immutable.* -case class Blah(msg: String, stuff: Array[List[String]]) //, age: Int, isOk: Boolean) +import scala.util.{Failure, Success, Try} object JsonReader: @@ -53,7 +47,7 @@ object JsonReader: .flatMap(s => s.toArray.headOption match case Some(c) => Right(c.asInstanceOf[T]) - case None => Left(ParseError(s"Cannot convert value '$s' into a Char.")) + case None => Left(JsonParseError(s"Cannot convert value '$s' into a Char.")) ) } case t: PrimitiveRef[?] if t.name == DOUBLE_CLASS => @@ -88,6 +82,36 @@ object JsonReader: p.expectList[e](j, $subFn).map(_.to(${ Expr.summon[Factory[e, T]].get })) // Convert List to whatever the target type should be } + case t: ScalaEnumRef[T] => + t.refType match + case '[s] => + val rtypeExpr = t.expr + '{ (j: JsonConfig, p: JsonParser) => + val rtype = $rtypeExpr.asInstanceOf[ScalaEnumRType[T]] + j.enumsAsIds match + case '*' => + p.expectLong(j, p).flatMap { v => + val fromOrdinalMethod = Class.forName(rtype.name).getMethod("fromOrdinal", classOf[Int]) + scala.util.Try(fromOrdinalMethod.invoke(null, v.toInt).asInstanceOf[T]) match + case Success(v2) => Right(v2) + case Failure(e) => Left(JsonParseError(s"No enum value in ${rtype.name} for ordinal value '$v'")) + } + case enumList: List[String] if enumList.contains(rtype.name) => + p.expectLong(j, p).flatMap { v => + val fromOrdinalMethod = Class.forName(rtype.name).getMethod("fromOrdinal", classOf[Int]) + scala.util.Try(fromOrdinalMethod.invoke(null, v.toInt).asInstanceOf[T]) match + case Success(v2) => Right(v2) + case Failure(e) => Left(JsonParseError(s"No enum value in ${rtype.name} for ordinal value '$v'")) + } + case _ => + p.expectString(j, p).flatMap { v => + val valueOfMethod = Class.forName(rtype.name).getMethod("valueOf", classOf[String]) + scala.util.Try(valueOfMethod.invoke(null, v).asInstanceOf[T]) match + case Success(v2) => Right(v2) + case Failure(e) => Left(JsonParseError(s"No enum value in ${rtype.name} for value '$v'")) + } + } + def classParseMap[T: Type](ref: ClassRef[T])(using Quotes): Expr[JsonParser => Map[String, (JsonConfig, JsonParser) => Either[ParseError, ?]]] = import Clazzes.* '{ (parser: JsonParser) => diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index 4dfc6277..3eb6f7da 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -3,6 +3,9 @@ package run import co.blocke.scala_reflection.* +enum Color: + case Red, Green, Blue + object RunMe extends App: val js = """[[123,-456],[394,-2]]""" @@ -19,12 +22,24 @@ object RunMe extends App: given json.JsonConfig = json .JsonConfig() + .copy(enumsAsIds = '*') try - println("RESULT: " + ScalaJack.read[json.Blah]("""{"msg":"Greg\nZoller", "stuff": [["a","b","c"],["x","y","z"]] }""")) + // Works! + // val c = Class.forName("co.blocke.scalajack.run.Color") + // val valueOfMethod = c.getMethod("valueOf", classOf[String]) + // println(valueOfMethod.invoke(null, "Boom")) + + // println("Worked? " + c.valueOf("Blue")) + // println("Color: " + c.getClass.getName) + + println("RESULT: " + ScalaJack.read[Blah]("""{"msg":"Greg\nZoller", "stuff": 2}""")) + // println("RESULT: " + ScalaJack.read[json.Blah]("""{"msg":"Greg","isOk":true,"age":57}""")) catch { - case t: Throwable => println("BOOM: " + t.getMessage) + case t: Throwable => + println(s"BOOM ($t): " + t.getMessage) + println(t.getClass.getName) } // val t0 = System.currentTimeMillis() diff --git a/src/main/scala/co.blocke.scalajack/run/Sample.scala b/src/main/scala/co.blocke.scalajack/run/Sample.scala index 577c467a..866d015e 100644 --- a/src/main/scala/co.blocke.scalajack/run/Sample.scala +++ b/src/main/scala/co.blocke.scalajack/run/Sample.scala @@ -18,3 +18,11 @@ case class Dog(name: String, numLegs: Int, carsChased: Int, friend: Option[Anima enum Colors: case Red, Blue, Green + +import scala.collection.immutable.* +enum Vehicle: + case Car, Bus, Train +case class Blah(msg: String, stuff: Vehicle) + +object Talk: + def say(s: String): String = s"Say $s!" From b8d75be82ed3772a961d3324e3dbc5ae3837bfb5 Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Wed, 25 Oct 2023 16:49:21 -0500 Subject: [PATCH 10/65] Classes with default values work --- .../scala/co.blocke.scalajack/ScalaJack.scala | 8 +-- .../co.blocke.scalajack/json/JsonParser.scala | 19 +++--- .../co.blocke.scalajack/json/JsonReader.scala | 62 +++++++++++++++++++ .../scala/co.blocke.scalajack/run/Play.scala | 2 +- .../co.blocke.scalajack/run/Sample.scala | 5 +- 5 files changed, 77 insertions(+), 19 deletions(-) diff --git a/src/main/scala/co.blocke.scalajack/ScalaJack.scala b/src/main/scala/co.blocke.scalajack/ScalaJack.scala index 00ed498f..d7befa50 100644 --- a/src/main/scala/co.blocke.scalajack/ScalaJack.scala +++ b/src/main/scala/co.blocke.scalajack/ScalaJack.scala @@ -26,13 +26,11 @@ object ScalaJack: def readImpl[T: Type](js: Expr[String], cfg: Expr[JsonConfig])(using q: Quotes): Expr[T] = import quotes.reflect.* - val classRef = ReflectOnType[T](quotes)(TypeRepr.of[T], true)(using scala.collection.mutable.Map.empty[TypedName, Boolean]).asInstanceOf[ClassRef[T]] - val parseTable = JsonReader.classParseMap[T](classRef) - val instantiator = JsonReader.classInstantiator[T](classRef) + val classRef = ReflectOnType[T](quotes)(TypeRepr.of[T], true)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) + val fn = JsonReader.refFn[T](classRef) '{ // run-time val parser = JsonParser($js) - val classFieldMap = $parseTable(parser) // Map[String, JsonConfig => Either[ParseError, ?]] - parser.expectClass[T]($cfg, classFieldMap, $instantiator) match + $fn($cfg, parser) match case Right(v) => v case Left(t) => throw t } diff --git a/src/main/scala/co.blocke.scalajack/json/JsonParser.scala b/src/main/scala/co.blocke.scalajack/json/JsonParser.scala index 6f237078..14e06721 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonParser.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonParser.scala @@ -13,6 +13,8 @@ case class JsonParser(js: String): private var i = 0 private val max: Int = jsChars.length + def getPos = i + // Housekeeping // ------------------------------ @inline def nullCheck: Boolean = @@ -61,7 +63,7 @@ case class JsonParser(js: String): case 't' if i + 3 < max && jsChars(i + 1) == 'r' && jsChars(i + 2) == 'u' && jsChars(i + 3) == 'e' => i += 4 Right(true) - case 'f' if i + 4 < max && jsChars(i + 1) == 'a' && jsChars(i + 2) == 'l' && jsChars(i + 3) == 's' && jsChars(i + 3) == 'e' => + case 'f' if i + 4 < max && jsChars(i + 1) == 'a' && jsChars(i + 2) == 'l' && jsChars(i + 3) == 's' && jsChars(i + 4) == 'e' => i += 5 Right(false) case x => Left(JsonParseError(s"Unexpected character '$x' where beginning of boolean value expected at position [$i]")) @@ -118,13 +120,6 @@ case class JsonParser(js: String): Right(captured.getOrElse(js.substring(mark, i - 1))) case x => Left(JsonParseError(s"Unexpected character '$x' where beginning of label expected at position [$i]")) - def expectOption[T](cfg: JsonConfig, expectElement: (JsonConfig, JsonParser) => Either[ParseError, T]): Either[ParseError, Option[T]] = - nullCheck match - case false => expectElement(cfg, this).map(t => Some(t)) - case true if cfg.noneAsNull => Right(None) - case true if cfg.forbidNullsInInput => Left(JsonParseError(s"Forbidden 'null' value received at position [$i]")) - case true => Right(Some(null.asInstanceOf[T])) - def expectList[T](cfg: JsonConfig, expectElement: (JsonConfig, JsonParser) => Either[ParseError, T]): Either[ParseError, List[T]] = if jsChars(i) != '[' then Left(JsonParseError(s"Beginning of list expected at position [$i]")) else @@ -150,26 +145,26 @@ case class JsonParser(js: String): def expectClass[T]( cfg: JsonConfig, fieldMap: Map[String, (JsonConfig, JsonParser) => Either[ParseError, ?]], - instantiator: Map[String, ?] => T + instantiator: Map[String, ?] => T, + fieldValues: scala.collection.mutable.HashMap[String, Any] // pre-set values (Option:None, default values) ): Either[ParseError, T] = if jsChars(i) != '{' then Left(JsonParseError(s"Beginning of object expected at position [$i]")) else i += 1 eatWhitespace var done: Option[Either[ParseError, T]] = None - val fields = scala.collection.mutable.HashMap.empty[String, Any] while done.isEmpty do (for { fieldLabel <- expectLabel _ <- expectColon fieldValue <- fieldMap(fieldLabel)(cfg, this) - _ = fields.put(fieldLabel, fieldValue) + _ = fieldValues.put(fieldLabel, fieldValue) _ <- expectComma } yield fieldValue) match case Left(CommaExpected(_)) if jsChars(i) == '}' => i += 1 eatWhitespace - done = Some(Right(instantiator(fields.toMap))) // instantiate the class here!!! + done = Some(Right(instantiator(fieldValues.toMap))) // instantiate the class here!!! case Left(e) => done = Some(Left(e)) case Right(_) => diff --git a/src/main/scala/co.blocke.scalajack/json/JsonReader.scala b/src/main/scala/co.blocke.scalajack/json/JsonReader.scala index 95b3621b..1e2adbde 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonReader.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonReader.scala @@ -82,6 +82,23 @@ object JsonReader: p.expectList[e](j, $subFn).map(_.to(${ Expr.summon[Factory[e, T]].get })) // Convert List to whatever the target type should be } + case t: ScalaOptionRef[T] => + t.refType match + case '[s] => + t.optionParamType.refType match + case '[e] => + val subFn = refFn[e](t.optionParamType.asInstanceOf[RTypeRef[e]]).asInstanceOf[Expr[(JsonConfig, JsonParser) => Either[ParseError, e]]] + val isNullable = Expr(t.optionParamType.isNullable) + '{ (j: JsonConfig, p: JsonParser) => + p.nullCheck match { + case true if j.noneAsNull => Right(None.asInstanceOf[T]) + case true if j.forbidNullsInInput => Left(JsonParseError(s"Forbidden 'null' value received at position [${p.getPos}]")) + case true if ! $isNullable => Left(JsonParseError(s"Null value given for non-nullable value type at position [${p.getPos}]")) + case true => Right(Some(null).asInstanceOf[T]) + case false => $subFn(j, p).map(v => Some(v).asInstanceOf[T]) + } + } + case t: ScalaEnumRef[T] => t.refType match case '[s] => @@ -112,6 +129,51 @@ object JsonReader: } } + case t: ScalaClassRef[T] => + t.refType match + case '[s] => + val parseTable = classParseMap[T](t) + val instantiator = classInstantiator[T](t) + + '{ (j: JsonConfig, p: JsonParser) => + val rtype = ${ t.expr }.asInstanceOf[ScalaClassRType[T]] + val presetFieldValues = scala.collection.mutable.HashMap.empty[String, Any] + rtype.fields.foreach(f => + if f.fieldType.clazz == Clazzes.OptionClazz then presetFieldValues.put(f.name, None) + else if f.asInstanceOf[ScalaFieldInfo].defaultValueAccessorName.isDefined then + val (companion, accessor) = f.asInstanceOf[ScalaFieldInfo].defaultValueAccessorName.get + + // Have to use Java reflection here to get default value--Scala compiler won't have access to companion + // or accessor if we do a ${} block, and using compiler staging would murder performance. + val defaultValue = { + val c = Class.forName(companion) + val cons = c.getDeclaredConstructor() + cons.setAccessible(true) + val m = c.getMethod(accessor) + m.setAccessible(true) + m.invoke(cons.newInstance()) + } + println("Got: " + defaultValue) + + presetFieldValues.put(f.name, defaultValue) + ) + val classFieldMap = $parseTable(p) // Map[String, JsonConfig => Either[ParseError, ?]] + p.expectClass[T](j, classFieldMap, $instantiator, presetFieldValues) + } + + case t: AliasRef[T] => + t.refType match + case '[s] => + t.unwrappedType.refType match + case '[e] => + val subFn = refFn[e](t.unwrappedType.asInstanceOf[RTypeRef[e]]).asInstanceOf[Expr[(JsonConfig, JsonParser) => Either[ParseError, e]]] + '{ (j: JsonConfig, p: JsonParser) => + $subFn(j,p).asInstanceOf[Either[co.blocke.scalajack.json.ParseError, T]] + } + + + // ----------------------------------- + def classParseMap[T: Type](ref: ClassRef[T])(using Quotes): Expr[JsonParser => Map[String, (JsonConfig, JsonParser) => Either[ParseError, ?]]] = import Clazzes.* '{ (parser: JsonParser) => diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index 3eb6f7da..afd650e2 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -33,7 +33,7 @@ object RunMe extends App: // println("Worked? " + c.valueOf("Blue")) // println("Color: " + c.getClass.getName) - println("RESULT: " + ScalaJack.read[Blah]("""{"msg":"Greg\nZoller", "stuff": 2}""")) + println("RESULT: " + ScalaJack.read[Blah]("""{"msg":"Greg\nZoller", "stuff": {"a":-100, "b":false}}""")) // println("RESULT: " + ScalaJack.read[json.Blah]("""{"msg":"Greg","isOk":true,"age":57}""")) catch { diff --git a/src/main/scala/co.blocke.scalajack/run/Sample.scala b/src/main/scala/co.blocke.scalajack/run/Sample.scala index 866d015e..532fb43f 100644 --- a/src/main/scala/co.blocke.scalajack/run/Sample.scala +++ b/src/main/scala/co.blocke.scalajack/run/Sample.scala @@ -22,7 +22,10 @@ enum Colors: import scala.collection.immutable.* enum Vehicle: case Car, Bus, Train -case class Blah(msg: String, stuff: Vehicle) + +case class Simple(a: Int, b: Boolean, c: Option[String], z: Int = 5) + +case class Blah(msg: String, stuff: Simple) object Talk: def say(s: String): String = s"Say $s!" From 242eafb1dded173d1e24b1de75eda42886352143 Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Thu, 26 Oct 2023 11:10:01 -0500 Subject: [PATCH 11/65] SelfRefs working --- .../scala/co.blocke.scalajack/ScalaJack.scala | 35 +++++++++++++- .../co.blocke.scalajack/json/JsonError.scala | 4 ++ .../co.blocke.scalajack/json/JsonParser.scala | 7 +-- .../co.blocke.scalajack/json/JsonReader.scala | 46 +++++++++++++++---- .../scala/co.blocke.scalajack/run/Play.scala | 6 +-- .../co.blocke.scalajack/run/Sample.scala | 2 +- 6 files changed, 77 insertions(+), 23 deletions(-) diff --git a/src/main/scala/co.blocke.scalajack/ScalaJack.scala b/src/main/scala/co.blocke.scalajack/ScalaJack.scala index d7befa50..590b4721 100644 --- a/src/main/scala/co.blocke.scalajack/ScalaJack.scala +++ b/src/main/scala/co.blocke.scalajack/ScalaJack.scala @@ -3,6 +3,7 @@ package co.blocke.scalajack import co.blocke.scala_reflection.TypedName import co.blocke.scala_reflection.reflect.ReflectOnType import co.blocke.scala_reflection.reflect.rtypeRefs.ClassRef +import scala.collection.mutable.HashMap import scala.quoted.* import quoted.Quotes import json.* @@ -27,10 +28,40 @@ object ScalaJack: import quotes.reflect.* val classRef = ReflectOnType[T](quotes)(TypeRepr.of[T], true)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) - val fn = JsonReader.refFn[T](classRef) + + // Used to trap SelfRef's from going into endless loops and causing Stack Overflow. + val seenBeforeFnCache = HashMap.empty[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]] + + // def refFn[T: Type](ref: RTypeRef[T])(using q: Quotes)(using cache: HashMap[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]]): Expr[(JsonConfig, JsonParser) => Either[ParseError, T]] = + val fn = JsonReader.refFn[T](classRef)(using quotes, Type.of[T])(using seenBeforeFnCache) + val listifiedCache = Expr.ofList(seenBeforeFnCache.toList.map(t => Expr.ofTuple(t))) + '{ // run-time - val parser = JsonParser($js) + val parser = JsonParser($js, $listifiedCache.toMap) $fn($cfg, parser) match case Right(v) => v case Left(t) => throw t + + // val root = RootEnvelope($js, $fn) + // root.run[T]($cfg, root.parser) match + // case Right(v) => v + // case Left(t) => throw t } + +// abstract class Envelope: +// val rootEnvelope: Option[RootEnvelope] +// val innerFn: (JsonConfig, JsonParser) => Either[ParseError, ?] +// def run[T](j: JsonConfig, p: JsonParser): Either[ParseError, T] + +// case class RootEnvelope(js: String, fn: (JsonConfig, JsonParser) => Either[ParseError, ?]) extends Envelope: +// val rootEnvelope: Option[RootEnvelope] = None +// val seenBefore: scala.collection.mutable.HashMap[TypedName, (JsonConfig, JsonParser) => Either[ParseError, ?]] = +// scala.collection.mutable.HashMap.empty[TypedName, (JsonConfig, JsonParser) => Either[ParseError, ?]] +// val parser = JsonParser(js) +// val innerFn: (JsonConfig, JsonParser) => Either[ParseError, ?] = fn +// def run[T](j: JsonConfig, p: JsonParser): Either[ParseError, T] = innerFn(j, p).map(_.asInstanceOf[T]) + +// case class SimpleEnvelope(fn: (JsonConfig, JsonParser) => Either[ParseError, ?], root: RootEnvelope) extends Envelope: +// val rootEnvelope = Some(root) +// val innerFn = fn +// def run[T](j: JsonConfig, p: JsonParser): Either[ParseError, T] = innerFn(j, p).map(_.asInstanceOf[T]) diff --git a/src/main/scala/co.blocke.scalajack/json/JsonError.scala b/src/main/scala/co.blocke.scalajack/json/JsonError.scala index cc11598f..8b07a73e 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonError.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonError.scala @@ -2,3 +2,7 @@ package co.blocke.scalajack package json class JsonError(msg: String) extends Throwable + +class ParseError(message: String) extends Throwable(message) +case class JsonParseError(message: String) extends ParseError(message) +case class CommaExpected(message: String = "") extends ParseError(message) diff --git a/src/main/scala/co.blocke.scalajack/json/JsonParser.scala b/src/main/scala/co.blocke.scalajack/json/JsonParser.scala index 14e06721..61865d81 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonParser.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonParser.scala @@ -2,12 +2,9 @@ package co.blocke.scalajack package json import scala.util.* +import co.blocke.scala_reflection.TypedName -class ParseError(message: String) extends Throwable(message) -case class JsonParseError(message: String) extends ParseError(message) -case class CommaExpected(message: String = "") extends ParseError(message) - -case class JsonParser(js: String): +case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) => Either[ParseError, ?]]): private val jsChars: Array[Char] = js.toCharArray private var i = 0 diff --git a/src/main/scala/co.blocke.scalajack/json/JsonReader.scala b/src/main/scala/co.blocke.scalajack/json/JsonReader.scala index 1e2adbde..e29b1d50 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonReader.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonReader.scala @@ -10,7 +10,7 @@ import scala.quoted.* import scala.collection.Factory import co.blocke.scala_reflection.RType import scala.jdk.CollectionConverters.* -import java.util.concurrent.ConcurrentHashMap +import scala.collection.mutable.HashMap import scala.util.{Failure, Success, Try} object JsonReader: @@ -32,7 +32,7 @@ object JsonReader: } } - def refFn[T: Type](ref: RTypeRef[T])(using Quotes): Expr[(JsonConfig, JsonParser) => Either[ParseError, T]] = + def refFn[T](ref: RTypeRef[T])(using q: Quotes, tt: Type[T])(using cache: HashMap[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]]): Expr[(JsonConfig, JsonParser) => Either[ParseError, T]] = import Clazzes.* import quotes.reflect.* @@ -132,10 +132,11 @@ object JsonReader: case t: ScalaClassRef[T] => t.refType match case '[s] => + // IDEA: Somewhere at this level (globally tho) create a seenBefore cache. Pass this cache to classParseMap() and don't + // descend on any SelfRef's found. Do something else (TBD) with these that, when run, looks up the right fn from cache. val parseTable = classParseMap[T](t) val instantiator = classInstantiator[T](t) - - '{ (j: JsonConfig, p: JsonParser) => + val classFn = '{ (j: JsonConfig, p: JsonParser) => val rtype = ${ t.expr }.asInstanceOf[ScalaClassRType[T]] val presetFieldValues = scala.collection.mutable.HashMap.empty[String, Any] rtype.fields.foreach(f => @@ -153,13 +154,13 @@ object JsonReader: m.setAccessible(true) m.invoke(cons.newInstance()) } - println("Got: " + defaultValue) - presetFieldValues.put(f.name, defaultValue) ) val classFieldMap = $parseTable(p) // Map[String, JsonConfig => Either[ParseError, ?]] p.expectClass[T](j, classFieldMap, $instantiator, presetFieldValues) } + cache.put(Expr(t.typedName), classFn) + classFn case t: AliasRef[T] => t.refType match @@ -167,14 +168,39 @@ object JsonReader: t.unwrappedType.refType match case '[e] => val subFn = refFn[e](t.unwrappedType.asInstanceOf[RTypeRef[e]]).asInstanceOf[Expr[(JsonConfig, JsonParser) => Either[ParseError, e]]] - '{ (j: JsonConfig, p: JsonParser) => - $subFn(j,p).asInstanceOf[Either[co.blocke.scalajack.json.ParseError, T]] - } + '{ (j: JsonConfig, p: JsonParser) => $subFn(j, p).asInstanceOf[Either[co.blocke.scalajack.json.ParseError, T]] } + + case t: SelfRefRef[T] => + t.refType match + case '[s] => + val className = Expr(t.typedName.toString) + '{ (j: JsonConfig, p: JsonParser) => + val cname = $className + p.cache.get(cname.asInstanceOf[TypedName]) match + case Some(fn) => fn(j, p).asInstanceOf[Either[co.blocke.scalajack.json.ParseError, T]] + case None => Left(ParseError(s"Expected self-ref class $cname but none found in cache at position [${p.getPos}]")) + } + // TODO: + // * Enumeration + // * Java Primitives + // * Java Classes + // * Java Collections + // * Java Enums + // * Non-case Scala classes + // * Map + // * Scala2Ref + // * SealedTraitRef + // * SelfRefRef + // * TraitRef + // * TryRef + // * TupleRef // ----------------------------------- - def classParseMap[T: Type](ref: ClassRef[T])(using Quotes): Expr[JsonParser => Map[String, (JsonConfig, JsonParser) => Either[ParseError, ?]]] = + def classParseMap[T: Type](ref: ClassRef[T])(using q: Quotes)(using + cache: scala.collection.mutable.HashMap[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]] + ): Expr[JsonParser => Map[String, (JsonConfig, JsonParser) => Either[ParseError, ?]]] = import Clazzes.* '{ (parser: JsonParser) => val daList = ${ diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index afd650e2..b705008d 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -8,10 +8,6 @@ enum Color: object RunMe extends App: - val js = """[[123,-456],[394,-2]]""" - - val parser = json.JsonParser(js) - /* val f = () => parser.expectLong val f2 = () => parser.expectList[Long](() => parser.expectLong) @@ -33,7 +29,7 @@ object RunMe extends App: // println("Worked? " + c.valueOf("Blue")) // println("Color: " + c.getClass.getName) - println("RESULT: " + ScalaJack.read[Blah]("""{"msg":"Greg\nZoller", "stuff": {"a":-100, "b":false}}""")) + println("RESULT: " + ScalaJack.read[Blah]("""{"msg":"Greg\nZoller", "stuff": {"a":-100, "b":false, "c":{"a":2,"b":true}}}""")) // println("RESULT: " + ScalaJack.read[json.Blah]("""{"msg":"Greg","isOk":true,"age":57}""")) catch { diff --git a/src/main/scala/co.blocke.scalajack/run/Sample.scala b/src/main/scala/co.blocke.scalajack/run/Sample.scala index 532fb43f..20dd6833 100644 --- a/src/main/scala/co.blocke.scalajack/run/Sample.scala +++ b/src/main/scala/co.blocke.scalajack/run/Sample.scala @@ -23,7 +23,7 @@ import scala.collection.immutable.* enum Vehicle: case Car, Bus, Train -case class Simple(a: Int, b: Boolean, c: Option[String], z: Int = 5) +case class Simple(a: Int, b: Boolean, c: Option[Simple], z: Int = 5) case class Blah(msg: String, stuff: Simple) From 3dfcea09ecd99bdd221c4262fc98b99e2bbee1ec Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Thu, 26 Oct 2023 23:50:18 -0500 Subject: [PATCH 12/65] Maps and more work --- .../co.blocke.scalajack/json/JsonParser.scala | 42 ++++++- .../co.blocke.scalajack/json/JsonReader.scala | 112 +++++++++++++++--- .../co.blocke.scalajack/json/JsonWriter.scala | 43 +++++-- .../scala/co.blocke.scalajack/run/Play.scala | 17 ++- .../co.blocke.scalajack/run/Sample.scala | 2 + 5 files changed, 188 insertions(+), 28 deletions(-) diff --git a/src/main/scala/co.blocke.scalajack/json/JsonParser.scala b/src/main/scala/co.blocke.scalajack/json/JsonParser.scala index 61865d81..d8572800 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonParser.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonParser.scala @@ -21,6 +21,11 @@ case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) @inline def eatWhitespace: Either[ParseError, Unit] = while i < max && jsChars(i).isWhitespace do i += 1 Right(()) + @inline def expectQuote: Either[ParseError, Unit] = + if jsChars(i) == '\"' then + i += 1 + Right(()) + else Left(ParseError(s"Quote expected at position [$i]")) @inline def expectComma: Either[ParseError, Unit] = // Note: this consumes whitespace before/after the ',' for { _ <- eatWhitespace @@ -130,7 +135,7 @@ case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) _ = acc.addOne(el) _ <- expectComma } yield el) match - case Left(_) if jsChars(i) == ']' => + case Left(CommaExpected(_)) if jsChars(i) == ']' => i += 1 eatWhitespace done = Some(Right(acc.toList)) @@ -139,13 +144,42 @@ case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) case Right(_) => done.get + def expectObject[K, V]( + cfg: JsonConfig, + keyElement: (JsonConfig, JsonParser) => Either[ParseError, K], + valueElement: (JsonConfig, JsonParser) => Either[ParseError, V] + ): Either[ParseError, Map[K, V]] = + if jsChars(i) != '{' then Left(JsonParseError(s"Beginning of object expected at position [$i]")) + else + i += 1 + eatWhitespace + val acc = scala.collection.mutable.Map.empty[K, V] + var done: Option[Either[ParseError, Map[K, V]]] = None + while done.isEmpty do + (for { + keyLabel <- keyElement(cfg, this) + _ <- expectColon + mapVal <- valueElement(cfg, this) + _ = acc.put(keyLabel, mapVal) + _ <- expectComma + } yield (keyLabel, mapVal)) match + case Left(CommaExpected(_)) if jsChars(i) == '}' => + i += 1 + eatWhitespace + done = Some(Right(acc.toMap)) + case Left(e) => + done = Some(Left(e)) + case Right(_) => + done.get + + // Special case of JSON object where each entry is a field of a class def expectClass[T]( cfg: JsonConfig, fieldMap: Map[String, (JsonConfig, JsonParser) => Either[ParseError, ?]], instantiator: Map[String, ?] => T, fieldValues: scala.collection.mutable.HashMap[String, Any] // pre-set values (Option:None, default values) ): Either[ParseError, T] = - if jsChars(i) != '{' then Left(JsonParseError(s"Beginning of object expected at position [$i]")) + if jsChars(i) != '{' then Left(JsonParseError(s"Beginning of class object expected at position [$i]")) else i += 1 eatWhitespace @@ -161,7 +195,9 @@ case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) case Left(CommaExpected(_)) if jsChars(i) == '}' => i += 1 eatWhitespace - done = Some(Right(instantiator(fieldValues.toMap))) // instantiate the class here!!! + done = Try(instantiator(fieldValues.toMap)) match // instantiate the class here!!! + case Success(v) => Some(Right(v)) + case Failure(e) => Some(Left(ParseError(s"Unable to instantiate class at position [${i - 1}] with message ${e.getMessage}"))) case Left(e) => done = Some(Left(e)) case Right(_) => diff --git a/src/main/scala/co.blocke.scalajack/json/JsonReader.scala b/src/main/scala/co.blocke.scalajack/json/JsonReader.scala index e29b1d50..280a4209 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonReader.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonReader.scala @@ -32,15 +32,31 @@ object JsonReader: } } - def refFn[T](ref: RTypeRef[T])(using q: Quotes, tt: Type[T])(using cache: HashMap[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]]): Expr[(JsonConfig, JsonParser) => Either[ParseError, T]] = + def refFn[T](ref: RTypeRef[T], isMapKey: Boolean = false)(using q: Quotes, tt: Type[T])(using cache: HashMap[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]]): Expr[(JsonConfig, JsonParser) => Either[ParseError, T]] = import Clazzes.* import quotes.reflect.* ref match case t: PrimitiveRef[?] if t.name == BOOLEAN_CLASS => - '{ (j: JsonConfig, p: JsonParser) => p.expectBoolean(j, p).map(_.asInstanceOf[T]) } + if isMapKey then + '{ (j: JsonConfig, p: JsonParser) => + (for { + _ <- p.expectQuote + v <- p.expectBoolean(j, p).map(_.asInstanceOf[T]) + _ <- p.expectQuote + } yield v) + } + else '{ (j: JsonConfig, p: JsonParser) => p.expectBoolean(j, p).map(_.asInstanceOf[T]) } case t: PrimitiveRef[?] if t.name == BYTE_CLASS => - '{ (j: JsonConfig, p: JsonParser) => p.expectLong(j, p).map(_.toByte.asInstanceOf[T]) } + if isMapKey then + '{ (j: JsonConfig, p: JsonParser) => + (for { + _ <- p.expectQuote + v <- p.expectLong(j, p).map(_.toByte.asInstanceOf[T]) + _ <- p.expectQuote + } yield v) + } + else '{ (j: JsonConfig, p: JsonParser) => p.expectLong(j, p).map(_.toByte.asInstanceOf[T]) } case t: PrimitiveRef[?] if t.name == CHAR_CLASS => '{ (j: JsonConfig, p: JsonParser) => p.expectString(j, p) @@ -51,19 +67,60 @@ object JsonReader: ) } case t: PrimitiveRef[?] if t.name == DOUBLE_CLASS => - '{ (j: JsonConfig, p: JsonParser) => p.expectDouble(j, p).map(_.asInstanceOf[T]) } + if isMapKey then + '{ (j: JsonConfig, p: JsonParser) => + (for { + _ <- p.expectQuote + v <- p.expectDouble(j, p).map(_.asInstanceOf[T]) + _ <- p.expectQuote + } yield v) + } + else '{ (j: JsonConfig, p: JsonParser) => p.expectDouble(j, p).map(_.asInstanceOf[T]) } case t: PrimitiveRef[?] if t.name == FLOAT_CLASS => - '{ (j: JsonConfig, p: JsonParser) => p.expectDouble(j, p).map(_.toFloat.asInstanceOf[T]) } + if isMapKey then + '{ (j: JsonConfig, p: JsonParser) => + (for { + _ <- p.expectQuote + v <- p.expectDouble(j, p).map(_.toFloat.asInstanceOf[T]) + _ <- p.expectQuote + } yield v) + } + else '{ (j: JsonConfig, p: JsonParser) => p.expectDouble(j, p).map(_.toFloat.asInstanceOf[T]) } case t: PrimitiveRef[?] if t.name == INT_CLASS => - '{ (j: JsonConfig, p: JsonParser) => p.expectLong(j, p).map(_.toInt.asInstanceOf[T]) } + if isMapKey then + '{ (j: JsonConfig, p: JsonParser) => + (for { + _ <- p.expectQuote + v <- p.expectLong(j, p).map(_.toInt.asInstanceOf[T]) + _ <- p.expectQuote + } yield v) + } + else '{ (j: JsonConfig, p: JsonParser) => p.expectLong(j, p).map(_.toInt.asInstanceOf[T]) } case t: PrimitiveRef[?] if t.name == LONG_CLASS => - '{ (j: JsonConfig, p: JsonParser) => p.expectLong(j, p).map(_.asInstanceOf[T]) } + if isMapKey then + '{ (j: JsonConfig, p: JsonParser) => + (for { + _ <- p.expectQuote + v <- p.expectLong(j, p).map(_.asInstanceOf[T]) + _ <- p.expectQuote + } yield v) + } + else '{ (j: JsonConfig, p: JsonParser) => p.expectLong(j, p).map(_.asInstanceOf[T]) } case t: PrimitiveRef[?] if t.name == SHORT_CLASS => - '{ (j: JsonConfig, p: JsonParser) => p.expectLong(j, p).map(_.toShort.asInstanceOf[T]) } + if isMapKey then + '{ (j: JsonConfig, p: JsonParser) => + (for { + _ <- p.expectQuote + v <- p.expectLong(j, p).map(_.toShort.asInstanceOf[T]) + _ <- p.expectQuote + } yield v) + } + else '{ (j: JsonConfig, p: JsonParser) => p.expectLong(j, p).map(_.toShort.asInstanceOf[T]) } case t: PrimitiveRef[T] if t.name == STRING_CLASS => '{ (j: JsonConfig, p: JsonParser) => p.expectString(j, p).map(_.asInstanceOf[T]) } case t: SeqRef[T] => + if isMapKey then throw new JsonError("Seq types cannot be map keys.") t.refType match case '[s] => t.elementRef.refType match @@ -72,7 +129,23 @@ object JsonReader: '{ (j: JsonConfig, p: JsonParser) => p.expectList[e](j, $subFn).map(_.to(${ Expr.summon[Factory[e, T]].get })) // Convert List to whatever the target type should be } + case t: MapRef[T] => + if isMapKey then throw new JsonError("Map types cannot be map keys.") + t.refType match + case '[m] => + t.elementRef.refType match + case '[k] => + t.elementRef2.refType match + case '[v] => + val keyFn = refFn[k](t.elementRef.asInstanceOf[RTypeRef[k]], true).asInstanceOf[Expr[(JsonConfig, JsonParser) => Either[ParseError, k]]] + val valFn = refFn[v](t.elementRef2.asInstanceOf[RTypeRef[v]]).asInstanceOf[Expr[(JsonConfig, JsonParser) => Either[ParseError, v]]] + '{ (j: JsonConfig, p: JsonParser) => + val z = p.expectObject[k, v](j, $keyFn, $valFn).map(_.to(${ Expr.summon[Factory[(k, v), T]].get })) // Convert List to whatever the target type should be + println(s"Z ${p.getPos}: " + z) + z + } case t: ArrayRef[T] => + if isMapKey then throw new JsonError("Arrays cannot be map keys.") t.refType match case '[s] => t.elementRef.refType match @@ -83,6 +156,7 @@ object JsonReader: } case t: ScalaOptionRef[T] => + if isMapKey then throw new JsonError("Options cannot be map keys.") t.refType match case '[s] => t.optionParamType.refType match @@ -103,23 +177,31 @@ object JsonReader: t.refType match case '[s] => val rtypeExpr = t.expr + val wrappedMapKey = Expr(isMapKey) '{ (j: JsonConfig, p: JsonParser) => val rtype = $rtypeExpr.asInstanceOf[ScalaEnumRType[T]] - j.enumsAsIds match - case '*' => - p.expectLong(j, p).flatMap { v => + def readNumericEnum = + if $wrappedMapKey then + (for { + _ <- p.expectQuote + enumVal <- p.expectLong(j, p) + _ <- p.expectQuote + } yield enumVal).flatMap { v => val fromOrdinalMethod = Class.forName(rtype.name).getMethod("fromOrdinal", classOf[Int]) scala.util.Try(fromOrdinalMethod.invoke(null, v.toInt).asInstanceOf[T]) match case Success(v2) => Right(v2) case Failure(e) => Left(JsonParseError(s"No enum value in ${rtype.name} for ordinal value '$v'")) } - case enumList: List[String] if enumList.contains(rtype.name) => + else p.expectLong(j, p).flatMap { v => val fromOrdinalMethod = Class.forName(rtype.name).getMethod("fromOrdinal", classOf[Int]) scala.util.Try(fromOrdinalMethod.invoke(null, v.toInt).asInstanceOf[T]) match case Success(v2) => Right(v2) case Failure(e) => Left(JsonParseError(s"No enum value in ${rtype.name} for ordinal value '$v'")) } + j.enumsAsIds match + case '*' => readNumericEnum + case enumList: List[String] if enumList.contains(rtype.name) => readNumericEnum case _ => p.expectString(j, p).flatMap { v => val valueOfMethod = Class.forName(rtype.name).getMethod("valueOf", classOf[String]) @@ -130,6 +212,7 @@ object JsonReader: } case t: ScalaClassRef[T] => + if isMapKey then throw new JsonError("Class types cannot be map keys.") t.refType match case '[s] => // IDEA: Somewhere at this level (globally tho) create a seenBefore cache. Pass this cache to classParseMap() and don't @@ -167,10 +250,11 @@ object JsonReader: case '[s] => t.unwrappedType.refType match case '[e] => - val subFn = refFn[e](t.unwrappedType.asInstanceOf[RTypeRef[e]]).asInstanceOf[Expr[(JsonConfig, JsonParser) => Either[ParseError, e]]] + val subFn = refFn[e](t.unwrappedType.asInstanceOf[RTypeRef[e]], isMapKey).asInstanceOf[Expr[(JsonConfig, JsonParser) => Either[ParseError, e]]] '{ (j: JsonConfig, p: JsonParser) => $subFn(j, p).asInstanceOf[Either[co.blocke.scalajack.json.ParseError, T]] } case t: SelfRefRef[T] => + if isMapKey then throw new JsonError("Class or trait types cannot be map keys.") t.refType match case '[s] => val className = Expr(t.typedName.toString) @@ -188,10 +272,8 @@ object JsonReader: // * Java Collections // * Java Enums // * Non-case Scala classes - // * Map // * Scala2Ref // * SealedTraitRef - // * SelfRefRef // * TraitRef // * TryRef // * TupleRef diff --git a/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala b/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala index 995ef84b..3e5f1397 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala @@ -25,7 +25,7 @@ object JsonWriter: val refCache = new ConcurrentHashMap[TypedName, (?, StringBuilder, JsonConfig) => StringBuilder] - def writeJsonFn[T](rtRef: RTypeRef[T])(using tt: Type[T], q: Quotes): Expr[(T, StringBuilder, JsonConfig) => StringBuilder] = + def writeJsonFn[T](rtRef: RTypeRef[T], isMapKey: Boolean = false)(using tt: Type[T], q: Quotes): Expr[(T, StringBuilder, JsonConfig) => StringBuilder] = import quotes.reflect.* rtRef match @@ -36,13 +36,20 @@ object JsonWriter: sb.append('"') } case rt: PrimitiveRef[?] => - '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => sb.append(a.toString) } + if isMapKey then + '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => + sb.append('"') + sb.append(a.toString) + sb.append('"') + } + else '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => sb.append(a.toString) } case rt: AliasRef[?] => - val fn = writeJsonFn[rt.T](rt.unwrappedType.asInstanceOf[RTypeRef[rt.T]])(using Type.of[rt.T]) + val fn = writeJsonFn[rt.T](rt.unwrappedType.asInstanceOf[RTypeRef[rt.T]], isMapKey)(using Type.of[rt.T]) '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => $fn(a, sb, cfg) } case rt: ArrayRef[?] => + if isMapKey then throw new JsonError("Arrays cannot be map keys.") rt.elementRef.refType match case '[t] => val elementFn = writeJsonFn[t](rt.elementRef.asInstanceOf[RTypeRef[t]]) @@ -59,6 +66,7 @@ object JsonWriter: } case rt: ClassRef[?] => + if isMapKey then throw new JsonError("Classes cannot be map keys.") val fieldFns = rt.fields.map { f => f.fieldRef.refType match case '[t] => (writeJsonFn[t](f.fieldRef.asInstanceOf[RTypeRef[t]]), f) @@ -89,7 +97,6 @@ object JsonWriter: case _ => '{ () } ) // .asInstanceOf[Expr[Unit]] val stmts = hintStmt :: fieldFns.map { case (fn, field) => - // val stmts = fieldFns.map { case (fn, field) => '{ val fieldValue = ${ Select.unique('{ a }.asTerm, field.name).asExpr @@ -99,7 +106,9 @@ object JsonWriter: field.fieldRef.refType match case '[t] => '{ + sb.append('"') sb.append(${ Expr(field.name) }) + sb.append('"') sb.append(':') val fn2 = $fn.asInstanceOf[(t, StringBuilder, JsonConfig) => StringBuilder] fn2(fieldValue.asInstanceOf[t], sb, cfg) @@ -117,6 +126,7 @@ object JsonWriter: } case rt: TraitRef[?] => + if isMapKey then throw new JsonError("Traits cannot be map keys.") val typedName = Expr(rt.typedName) val traitType = rt.expr '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => @@ -131,6 +141,7 @@ object JsonWriter: } case rt: SeqRef[?] => + if isMapKey then throw new JsonError("Seq instances cannot be map keys.") rt.elementRef.refType match case '[t] => val elementFn = writeJsonFn[t](rt.elementRef.asInstanceOf[RTypeRef[t]]) @@ -147,6 +158,7 @@ object JsonWriter: } case rt: OptionRef[?] => + if isMapKey then throw new JsonError("Options cannot be map keys.") rt.optionParamType.refType match case '[t] => val fn = writeJsonFn[t](rt.optionParamType.asInstanceOf[RTypeRef[t]]) @@ -157,6 +169,7 @@ object JsonWriter: } case rt: TryRef[?] => + if isMapKey then throw new JsonError("Try cannot be map keys.") rt.tryRef.refType match case '[t] => val fn = writeJsonFn[t](rt.tryRef.asInstanceOf[RTypeRef[t]]) @@ -171,12 +184,13 @@ object JsonWriter: } case rt: MapRef[?] => + if isMapKey then throw new JsonError("Maps cannot be map keys.") rt.elementRef.refType match case '[k] => rt.elementRef2.refType match case '[v] => - val keyFn = writeJsonFn[k](rt.elementRef.asInstanceOf[RTypeRef[k]]) - val valueFn = writeJsonFn[v](rt.elementRef.asInstanceOf[RTypeRef[v]]) + val keyFn = writeJsonFn[k](rt.elementRef.asInstanceOf[RTypeRef[k]], true) + val valueFn = writeJsonFn[v](rt.elementRef2.asInstanceOf[RTypeRef[v]]) '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => sb.append('{') val sbLen = sb.length @@ -192,6 +206,7 @@ object JsonWriter: } case rt: LeftRightRef[?] => + if isMapKey then throw new JsonError("Union, intersection, or Either cannot be map keys.") rt.leftRef.refType match case '[lt] => val leftFn = writeJsonFn[lt](rt.leftRef.asInstanceOf[RTypeRef[lt]]) @@ -215,6 +230,7 @@ object JsonWriter: case rt: EnumRef[?] => val expr = rt.expr + val isMapKeyExpr = Expr(isMapKey) '{ val rtype = $expr.asInstanceOf[EnumRType[?]] (a: T, sb: StringBuilder, cfg: JsonConfig) => @@ -222,13 +238,20 @@ object JsonWriter: case '*' => true case aList: List[String] if aList.contains(rtype.name) => true case _ => false - if enumAsId then sb.append(rtype.ordinal(a.toString).getOrElse(throw new JsonError(s"Value $a is not a valid enum value for ${rtype.name}"))) + if enumAsId then + val enumVal = rtype.ordinal(a.toString).getOrElse(throw new JsonError(s"Value $a is not a valid enum value for ${rtype.name}")) + if $isMapKeyExpr then + sb.append('"') + sb.append(enumVal.toString) + sb.append('"') + else sb.append(enumVal.toString) else sb.append('"') sb.append(a.toString) sb.append('"') } + // TODO: Not sure this is right! case rt: SealedTraitRef[?] => '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => sb.append('"') @@ -237,6 +260,7 @@ object JsonWriter: } case rt: TupleRef[?] => + if isMapKey then throw new JsonError("Tuples cannot be map keys.") val elementFns = rt.tupleRefs.map { f => f.refType match case '[t] => (writeJsonFn[t](f.asInstanceOf[RTypeRef[t]]), f) @@ -269,6 +293,7 @@ object JsonWriter: } case rt: SelfRefRef[?] => + if isMapKey then throw new JsonError("Classes or traits cannot be map keys.") val e = rt.expr '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => val fn = refCache.get($e.typedName).asInstanceOf[(T, StringBuilder, JsonConfig) => StringBuilder] @@ -276,6 +301,7 @@ object JsonWriter: } case rt: JavaCollectionRef[?] => + if isMapKey then throw new JsonError("Collections cannot be map keys.") rt.elementRef.refType match case '[t] => val elementFn = writeJsonFn[t](rt.elementRef.asInstanceOf[RTypeRef[t]]) @@ -292,11 +318,12 @@ object JsonWriter: } case rt: JavaMapRef[?] => + if isMapKey then throw new JsonError("Maps cannot be map keys.") rt.elementRef.refType match case '[k] => rt.elementRef2.refType match case '[v] => - val keyFn = writeJsonFn[k](rt.elementRef.asInstanceOf[RTypeRef[k]]) + val keyFn = writeJsonFn[k](rt.elementRef.asInstanceOf[RTypeRef[k]], true) val valueFn = writeJsonFn[v](rt.elementRef.asInstanceOf[RTypeRef[v]]) '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => sb.append('{') diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index b705008d..5909e858 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -18,7 +18,7 @@ object RunMe extends App: given json.JsonConfig = json .JsonConfig() - .copy(enumsAsIds = '*') + // .copy(enumsAsIds = '*') try // Works! @@ -29,12 +29,25 @@ object RunMe extends App: // println("Worked? " + c.valueOf("Blue")) // println("Color: " + c.getClass.getName) - println("RESULT: " + ScalaJack.read[Blah]("""{"msg":"Greg\nZoller", "stuff": {"a":-100, "b":false, "c":{"a":2,"b":true}}}""")) + // println("RESULT: " + ScalaJack.read[Blah]("""{"msg":"Greg\nZoller", "stuff": {"a":-100, "b":false, "c":{"a":2,"b":true}}}""")) // println("RESULT: " + ScalaJack.read[json.Blah]("""{"msg":"Greg","isOk":true,"age":57}""")) + + // case class M1( v: Map[Int, Int], v2: Map[Colors, Int], v3: Map[co.blocke.scala_reflection.TypedName, Int]) + val x = M1( + Map(1L -> 2, 3L -> 4), + scala.collection.immutable.HashMap(Colors.Red -> 5, Colors.Blue -> 6), + Map("a".asInstanceOf[TypedName] -> 7, "b".asInstanceOf[TypedName] -> 8) + ) + val js = ScalaJack.write(x) + println(js) + val inst = ScalaJack.read[M1](js) + println(inst) + catch { case t: Throwable => println(s"BOOM ($t): " + t.getMessage) + println(t.printStackTrace) println(t.getClass.getName) } diff --git a/src/main/scala/co.blocke.scalajack/run/Sample.scala b/src/main/scala/co.blocke.scalajack/run/Sample.scala index 20dd6833..576679ff 100644 --- a/src/main/scala/co.blocke.scalajack/run/Sample.scala +++ b/src/main/scala/co.blocke.scalajack/run/Sample.scala @@ -29,3 +29,5 @@ case class Blah(msg: String, stuff: Simple) object Talk: def say(s: String): String = s"Say $s!" + +case class M1(v: Map[Long, Int], v2: HashMap[Colors, Int], v3: Map[co.blocke.scala_reflection.TypedName, Int]) From 3fde56a83f66d3f18bc362c47e12751e2a17f46c Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Fri, 27 Oct 2023 12:16:17 -0500 Subject: [PATCH 13/65] modularize reader --- .../scala/co.blocke.scalajack/ScalaJack.scala | 42 +-- .../co.blocke.scalajack/json/JsonParser.scala | 41 ++- .../co.blocke.scalajack/json/JsonReader.scala | 330 +++--------------- .../json/JsonReaderUtil.scala | 70 ++++ .../json/ReaderModule.scala | 24 ++ .../json/readers/ClassReader.scala | 58 +++ .../json/readers/CollectionReader.scala | 83 +++++ .../json/readers/EnumReader.scala | 58 +++ .../json/readers/MiscReader.scala | 76 ++++ .../json/readers/PrimitiveReader.scala | 107 ++++++ .../scala/co.blocke.scalajack/run/Play.scala | 90 +---- .../co.blocke.scalajack/run/Sample.scala | 2 +- 12 files changed, 570 insertions(+), 411 deletions(-) create mode 100644 src/main/scala/co.blocke.scalajack/json/JsonReaderUtil.scala create mode 100644 src/main/scala/co.blocke.scalajack/json/ReaderModule.scala create mode 100644 src/main/scala/co.blocke.scalajack/json/readers/ClassReader.scala create mode 100644 src/main/scala/co.blocke.scalajack/json/readers/CollectionReader.scala create mode 100644 src/main/scala/co.blocke.scalajack/json/readers/EnumReader.scala create mode 100644 src/main/scala/co.blocke.scalajack/json/readers/MiscReader.scala create mode 100644 src/main/scala/co.blocke.scalajack/json/readers/PrimitiveReader.scala diff --git a/src/main/scala/co.blocke.scalajack/ScalaJack.scala b/src/main/scala/co.blocke.scalajack/ScalaJack.scala index 590b4721..56f43fe9 100644 --- a/src/main/scala/co.blocke.scalajack/ScalaJack.scala +++ b/src/main/scala/co.blocke.scalajack/ScalaJack.scala @@ -22,6 +22,8 @@ object ScalaJack: $fn($t, sb, $cfg).toString } + // --------------------------------------------------------------------- + inline def read[T](js: String)(using cfg: JsonConfig = JsonConfig()): T = ${ readImpl[T]('js, 'cfg) } def readImpl[T: Type](js: Expr[String], cfg: Expr[JsonConfig])(using q: Quotes): Expr[T] = @@ -32,8 +34,7 @@ object ScalaJack: // Used to trap SelfRef's from going into endless loops and causing Stack Overflow. val seenBeforeFnCache = HashMap.empty[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]] - // def refFn[T: Type](ref: RTypeRef[T])(using q: Quotes)(using cache: HashMap[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]]): Expr[(JsonConfig, JsonParser) => Either[ParseError, T]] = - val fn = JsonReader.refFn[T](classRef)(using quotes, Type.of[T])(using seenBeforeFnCache) + val fn = JsonReader().readerFn[T](classRef)(using quotes, Type.of[T])(using seenBeforeFnCache) val listifiedCache = Expr.ofList(seenBeforeFnCache.toList.map(t => Expr.ofTuple(t))) '{ // run-time @@ -41,27 +42,22 @@ object ScalaJack: $fn($cfg, parser) match case Right(v) => v case Left(t) => throw t + } - // val root = RootEnvelope($js, $fn) - // root.run[T]($cfg, root.parser) match - // case Right(v) => v - // case Left(t) => throw t + /* + inline def foo[T](str: String)(using cfg: Config): T = ${ fooImpl[T]('str, 'cfg) } + + def fooImpl[T: Type](str: Expr[String], cfg: Expr[Config])(using q: Quotes): Expr[T] = + import quotes.reflect.* + + // How can I get some configuration here??? + + '{ // run-time + doSomething($str, $cfg) // can use str and cfg here } -// abstract class Envelope: -// val rootEnvelope: Option[RootEnvelope] -// val innerFn: (JsonConfig, JsonParser) => Either[ParseError, ?] -// def run[T](j: JsonConfig, p: JsonParser): Either[ParseError, T] - -// case class RootEnvelope(js: String, fn: (JsonConfig, JsonParser) => Either[ParseError, ?]) extends Envelope: -// val rootEnvelope: Option[RootEnvelope] = None -// val seenBefore: scala.collection.mutable.HashMap[TypedName, (JsonConfig, JsonParser) => Either[ParseError, ?]] = -// scala.collection.mutable.HashMap.empty[TypedName, (JsonConfig, JsonParser) => Either[ParseError, ?]] -// val parser = JsonParser(js) -// val innerFn: (JsonConfig, JsonParser) => Either[ParseError, ?] = fn -// def run[T](j: JsonConfig, p: JsonParser): Either[ParseError, T] = innerFn(j, p).map(_.asInstanceOf[T]) - -// case class SimpleEnvelope(fn: (JsonConfig, JsonParser) => Either[ParseError, ?], root: RootEnvelope) extends Envelope: -// val rootEnvelope = Some(root) -// val innerFn = fn -// def run[T](j: JsonConfig, p: JsonParser): Either[ParseError, T] = innerFn(j, p).map(_.asInstanceOf[T]) + Parameters may only be: + * Quoted parameters or fields + * Literal values of primitive types + * References to `inline val`s + */ diff --git a/src/main/scala/co.blocke.scalajack/json/JsonParser.scala b/src/main/scala/co.blocke.scalajack/json/JsonParser.scala index d8572800..6df54c55 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonParser.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonParser.scala @@ -3,6 +3,7 @@ package json import scala.util.* import co.blocke.scala_reflection.TypedName +import scala.collection.mutable.ListBuffer case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) => Either[ParseError, ?]]): @@ -127,7 +128,7 @@ case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) else i += 1 eatWhitespace - val acc = scala.collection.mutable.ListBuffer.empty[T] + val acc = ListBuffer.empty[T] var done: Option[Either[ParseError, List[T]]] = None while done.isEmpty do (for { @@ -144,6 +145,33 @@ case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) case Right(_) => done.get + def expectTuple( + cfg: JsonConfig, + tupleFns: List[(JsonConfig, JsonParser) => Either[ParseError, ?]] + ): Either[ParseError, List[?]] = + if jsChars(i) != '[' then Left(JsonParseError(s"Beginning of tuple expected at position [$i]")) + else + i += 1 + eatWhitespace + val buf = ListBuffer.empty[Any] + tupleFns + .foldLeft(Right(buf).asInstanceOf[Either[ParseError, ListBuffer[Any]]]) { (acc, fn) => + acc.flatMap(accumulator => + for { + el <- fn(cfg, this) + newAcc = accumulator.addOne(el) + _ <- expectComma + } yield newAcc + ) + } match + case Left(CommaExpected(_)) if buf.size == tupleFns.size && jsChars(i) == ']' => + i += 1 + eatWhitespace + Right(buf.toList) + case Left(CommaExpected(_)) if jsChars(i) == ']' => Left(ParseError(s"Missing required elements in tuple at position [$i]")) + case Left(e: ParseError) => Left(e) + case Right(_) => Left(ParseError(s"Extra/unexpected tuple fields at position [$i]")) + def expectObject[K, V]( cfg: JsonConfig, keyElement: (JsonConfig, JsonParser) => Either[ParseError, K], @@ -173,17 +201,16 @@ case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) done.get // Special case of JSON object where each entry is a field of a class - def expectClass[T]( + def expectClass( cfg: JsonConfig, fieldMap: Map[String, (JsonConfig, JsonParser) => Either[ParseError, ?]], - instantiator: Map[String, ?] => T, fieldValues: scala.collection.mutable.HashMap[String, Any] // pre-set values (Option:None, default values) - ): Either[ParseError, T] = + ): Either[ParseError, Map[String, ?]] = if jsChars(i) != '{' then Left(JsonParseError(s"Beginning of class object expected at position [$i]")) else i += 1 eatWhitespace - var done: Option[Either[ParseError, T]] = None + var done: Option[Either[ParseError, Map[String, ?]]] = None while done.isEmpty do (for { fieldLabel <- expectLabel @@ -195,9 +222,7 @@ case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) case Left(CommaExpected(_)) if jsChars(i) == '}' => i += 1 eatWhitespace - done = Try(instantiator(fieldValues.toMap)) match // instantiate the class here!!! - case Success(v) => Some(Right(v)) - case Failure(e) => Some(Left(ParseError(s"Unable to instantiate class at position [${i - 1}] with message ${e.getMessage}"))) + done = Some(Right(fieldValues.toMap)) case Left(e) => done = Some(Left(e)) case Right(_) => diff --git a/src/main/scala/co.blocke.scalajack/json/JsonReader.scala b/src/main/scala/co.blocke.scalajack/json/JsonReader.scala index 280a4209..1641bc6e 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonReader.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonReader.scala @@ -1,300 +1,48 @@ package co.blocke.scalajack package json -import co.blocke.scala_reflection.reflect.rtypeRefs.* -import co.blocke.scala_reflection.reflect.* -import co.blocke.scala_reflection.{Clazzes, RTypeRef, TypedName} -import co.blocke.scala_reflection.rtypes.* -import co.blocke.scala_reflection.Liftables.TypedNameToExpr +import co.blocke.scala_reflection.{RTypeRef, TypedName} import scala.quoted.* -import scala.collection.Factory -import co.blocke.scala_reflection.RType -import scala.jdk.CollectionConverters.* import scala.collection.mutable.HashMap import scala.util.{Failure, Success, Try} -object JsonReader: - - def classInstantiator[T: Type](ref: ClassRef[T])(using Quotes): Expr[Map[String, ?] => T] = - import quotes.reflect.* - val sym = TypeRepr.of[T].classSymbol.get - '{ (fieldMap: Map[String, ?]) => - ${ - val tree = Apply( - Select.unique(New(TypeIdent(sym)), ""), - ref.fields.map { f => - f.fieldRef.refType match - case '[t] => - '{ fieldMap(${ Expr(f.name) }).asInstanceOf[t] }.asTerm - } - ) - tree.asExpr.asExprOf[T] - } - } - - def refFn[T](ref: RTypeRef[T], isMapKey: Boolean = false)(using q: Quotes, tt: Type[T])(using cache: HashMap[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]]): Expr[(JsonConfig, JsonParser) => Either[ParseError, T]] = - import Clazzes.* - import quotes.reflect.* - - ref match - case t: PrimitiveRef[?] if t.name == BOOLEAN_CLASS => - if isMapKey then - '{ (j: JsonConfig, p: JsonParser) => - (for { - _ <- p.expectQuote - v <- p.expectBoolean(j, p).map(_.asInstanceOf[T]) - _ <- p.expectQuote - } yield v) - } - else '{ (j: JsonConfig, p: JsonParser) => p.expectBoolean(j, p).map(_.asInstanceOf[T]) } - case t: PrimitiveRef[?] if t.name == BYTE_CLASS => - if isMapKey then - '{ (j: JsonConfig, p: JsonParser) => - (for { - _ <- p.expectQuote - v <- p.expectLong(j, p).map(_.toByte.asInstanceOf[T]) - _ <- p.expectQuote - } yield v) - } - else '{ (j: JsonConfig, p: JsonParser) => p.expectLong(j, p).map(_.toByte.asInstanceOf[T]) } - case t: PrimitiveRef[?] if t.name == CHAR_CLASS => - '{ (j: JsonConfig, p: JsonParser) => - p.expectString(j, p) - .flatMap(s => - s.toArray.headOption match - case Some(c) => Right(c.asInstanceOf[T]) - case None => Left(JsonParseError(s"Cannot convert value '$s' into a Char.")) - ) - } - case t: PrimitiveRef[?] if t.name == DOUBLE_CLASS => - if isMapKey then - '{ (j: JsonConfig, p: JsonParser) => - (for { - _ <- p.expectQuote - v <- p.expectDouble(j, p).map(_.asInstanceOf[T]) - _ <- p.expectQuote - } yield v) - } - else '{ (j: JsonConfig, p: JsonParser) => p.expectDouble(j, p).map(_.asInstanceOf[T]) } - case t: PrimitiveRef[?] if t.name == FLOAT_CLASS => - if isMapKey then - '{ (j: JsonConfig, p: JsonParser) => - (for { - _ <- p.expectQuote - v <- p.expectDouble(j, p).map(_.toFloat.asInstanceOf[T]) - _ <- p.expectQuote - } yield v) - } - else '{ (j: JsonConfig, p: JsonParser) => p.expectDouble(j, p).map(_.toFloat.asInstanceOf[T]) } - case t: PrimitiveRef[?] if t.name == INT_CLASS => - if isMapKey then - '{ (j: JsonConfig, p: JsonParser) => - (for { - _ <- p.expectQuote - v <- p.expectLong(j, p).map(_.toInt.asInstanceOf[T]) - _ <- p.expectQuote - } yield v) - } - else '{ (j: JsonConfig, p: JsonParser) => p.expectLong(j, p).map(_.toInt.asInstanceOf[T]) } - case t: PrimitiveRef[?] if t.name == LONG_CLASS => - if isMapKey then - '{ (j: JsonConfig, p: JsonParser) => - (for { - _ <- p.expectQuote - v <- p.expectLong(j, p).map(_.asInstanceOf[T]) - _ <- p.expectQuote - } yield v) - } - else '{ (j: JsonConfig, p: JsonParser) => p.expectLong(j, p).map(_.asInstanceOf[T]) } - case t: PrimitiveRef[?] if t.name == SHORT_CLASS => - if isMapKey then - '{ (j: JsonConfig, p: JsonParser) => - (for { - _ <- p.expectQuote - v <- p.expectLong(j, p).map(_.toShort.asInstanceOf[T]) - _ <- p.expectQuote - } yield v) - } - else '{ (j: JsonConfig, p: JsonParser) => p.expectLong(j, p).map(_.toShort.asInstanceOf[T]) } - case t: PrimitiveRef[T] if t.name == STRING_CLASS => - '{ (j: JsonConfig, p: JsonParser) => p.expectString(j, p).map(_.asInstanceOf[T]) } - - case t: SeqRef[T] => - if isMapKey then throw new JsonError("Seq types cannot be map keys.") - t.refType match - case '[s] => - t.elementRef.refType match - case '[e] => - val subFn = refFn[e](t.elementRef.asInstanceOf[RTypeRef[e]]).asInstanceOf[Expr[(JsonConfig, JsonParser) => Either[ParseError, e]]] - '{ (j: JsonConfig, p: JsonParser) => - p.expectList[e](j, $subFn).map(_.to(${ Expr.summon[Factory[e, T]].get })) // Convert List to whatever the target type should be - } - case t: MapRef[T] => - if isMapKey then throw new JsonError("Map types cannot be map keys.") - t.refType match - case '[m] => - t.elementRef.refType match - case '[k] => - t.elementRef2.refType match - case '[v] => - val keyFn = refFn[k](t.elementRef.asInstanceOf[RTypeRef[k]], true).asInstanceOf[Expr[(JsonConfig, JsonParser) => Either[ParseError, k]]] - val valFn = refFn[v](t.elementRef2.asInstanceOf[RTypeRef[v]]).asInstanceOf[Expr[(JsonConfig, JsonParser) => Either[ParseError, v]]] - '{ (j: JsonConfig, p: JsonParser) => - val z = p.expectObject[k, v](j, $keyFn, $valFn).map(_.to(${ Expr.summon[Factory[(k, v), T]].get })) // Convert List to whatever the target type should be - println(s"Z ${p.getPos}: " + z) - z - } - case t: ArrayRef[T] => - if isMapKey then throw new JsonError("Arrays cannot be map keys.") - t.refType match - case '[s] => - t.elementRef.refType match - case '[e] => - val subFn = refFn[e](t.elementRef.asInstanceOf[RTypeRef[e]]).asInstanceOf[Expr[(JsonConfig, JsonParser) => Either[ParseError, e]]] - '{ (j: JsonConfig, p: JsonParser) => - p.expectList[e](j, $subFn).map(_.to(${ Expr.summon[Factory[e, T]].get })) // Convert List to whatever the target type should be - } - - case t: ScalaOptionRef[T] => - if isMapKey then throw new JsonError("Options cannot be map keys.") - t.refType match - case '[s] => - t.optionParamType.refType match - case '[e] => - val subFn = refFn[e](t.optionParamType.asInstanceOf[RTypeRef[e]]).asInstanceOf[Expr[(JsonConfig, JsonParser) => Either[ParseError, e]]] - val isNullable = Expr(t.optionParamType.isNullable) - '{ (j: JsonConfig, p: JsonParser) => - p.nullCheck match { - case true if j.noneAsNull => Right(None.asInstanceOf[T]) - case true if j.forbidNullsInInput => Left(JsonParseError(s"Forbidden 'null' value received at position [${p.getPos}]")) - case true if ! $isNullable => Left(JsonParseError(s"Null value given for non-nullable value type at position [${p.getPos}]")) - case true => Right(Some(null).asInstanceOf[T]) - case false => $subFn(j, p).map(v => Some(v).asInstanceOf[T]) - } - } - - case t: ScalaEnumRef[T] => - t.refType match - case '[s] => - val rtypeExpr = t.expr - val wrappedMapKey = Expr(isMapKey) - '{ (j: JsonConfig, p: JsonParser) => - val rtype = $rtypeExpr.asInstanceOf[ScalaEnumRType[T]] - def readNumericEnum = - if $wrappedMapKey then - (for { - _ <- p.expectQuote - enumVal <- p.expectLong(j, p) - _ <- p.expectQuote - } yield enumVal).flatMap { v => - val fromOrdinalMethod = Class.forName(rtype.name).getMethod("fromOrdinal", classOf[Int]) - scala.util.Try(fromOrdinalMethod.invoke(null, v.toInt).asInstanceOf[T]) match - case Success(v2) => Right(v2) - case Failure(e) => Left(JsonParseError(s"No enum value in ${rtype.name} for ordinal value '$v'")) - } - else - p.expectLong(j, p).flatMap { v => - val fromOrdinalMethod = Class.forName(rtype.name).getMethod("fromOrdinal", classOf[Int]) - scala.util.Try(fromOrdinalMethod.invoke(null, v.toInt).asInstanceOf[T]) match - case Success(v2) => Right(v2) - case Failure(e) => Left(JsonParseError(s"No enum value in ${rtype.name} for ordinal value '$v'")) - } - j.enumsAsIds match - case '*' => readNumericEnum - case enumList: List[String] if enumList.contains(rtype.name) => readNumericEnum - case _ => - p.expectString(j, p).flatMap { v => - val valueOfMethod = Class.forName(rtype.name).getMethod("valueOf", classOf[String]) - scala.util.Try(valueOfMethod.invoke(null, v).asInstanceOf[T]) match - case Success(v2) => Right(v2) - case Failure(e) => Left(JsonParseError(s"No enum value in ${rtype.name} for value '$v'")) - } - } - - case t: ScalaClassRef[T] => - if isMapKey then throw new JsonError("Class types cannot be map keys.") - t.refType match - case '[s] => - // IDEA: Somewhere at this level (globally tho) create a seenBefore cache. Pass this cache to classParseMap() and don't - // descend on any SelfRef's found. Do something else (TBD) with these that, when run, looks up the right fn from cache. - val parseTable = classParseMap[T](t) - val instantiator = classInstantiator[T](t) - val classFn = '{ (j: JsonConfig, p: JsonParser) => - val rtype = ${ t.expr }.asInstanceOf[ScalaClassRType[T]] - val presetFieldValues = scala.collection.mutable.HashMap.empty[String, Any] - rtype.fields.foreach(f => - if f.fieldType.clazz == Clazzes.OptionClazz then presetFieldValues.put(f.name, None) - else if f.asInstanceOf[ScalaFieldInfo].defaultValueAccessorName.isDefined then - val (companion, accessor) = f.asInstanceOf[ScalaFieldInfo].defaultValueAccessorName.get - - // Have to use Java reflection here to get default value--Scala compiler won't have access to companion - // or accessor if we do a ${} block, and using compiler staging would murder performance. - val defaultValue = { - val c = Class.forName(companion) - val cons = c.getDeclaredConstructor() - cons.setAccessible(true) - val m = c.getMethod(accessor) - m.setAccessible(true) - m.invoke(cons.newInstance()) - } - presetFieldValues.put(f.name, defaultValue) - ) - val classFieldMap = $parseTable(p) // Map[String, JsonConfig => Either[ParseError, ?]] - p.expectClass[T](j, classFieldMap, $instantiator, presetFieldValues) - } - cache.put(Expr(t.typedName), classFn) - classFn - - case t: AliasRef[T] => - t.refType match - case '[s] => - t.unwrappedType.refType match - case '[e] => - val subFn = refFn[e](t.unwrappedType.asInstanceOf[RTypeRef[e]], isMapKey).asInstanceOf[Expr[(JsonConfig, JsonParser) => Either[ParseError, e]]] - '{ (j: JsonConfig, p: JsonParser) => $subFn(j, p).asInstanceOf[Either[co.blocke.scalajack.json.ParseError, T]] } - - case t: SelfRefRef[T] => - if isMapKey then throw new JsonError("Class or trait types cannot be map keys.") - t.refType match - case '[s] => - val className = Expr(t.typedName.toString) - '{ (j: JsonConfig, p: JsonParser) => - val cname = $className - p.cache.get(cname.asInstanceOf[TypedName]) match - case Some(fn) => fn(j, p).asInstanceOf[Either[co.blocke.scalajack.json.ParseError, T]] - case None => Left(ParseError(s"Expected self-ref class $cname but none found in cache at position [${p.getPos}]")) - } - - // TODO: - // * Enumeration - // * Java Primitives - // * Java Classes - // * Java Collections - // * Java Enums - // * Non-case Scala classes - // * Scala2Ref - // * SealedTraitRef - // * TraitRef - // * TryRef - // * TupleRef +case class JsonReader() extends ReaderModule: + + val root: ReaderModule = null // Should never be accessed--we're the root! + + // Did the user supply an extension module? + val extension = Try(Class.forName("co.blocke.scalajack.json.ReaderExtension")) match + case Success(c) => Some(c.getDeclaredConstructor().newInstance.asInstanceOf[ReaderModule]) + case Failure(_) => None + + val modules = readers.PrimitiveReader( + readers.ColletionReader( + readers.ClassReader( + readers.EnumReader( + readers.MiscReader( + TerminusReaderModule(extension, root), + root + ), + root + ), + this + ), + this + ), + this + ) + + def readerFn[T](ref: RTypeRef[T], isMapKey: Boolean = false)(using q: Quotes, tt: Type[T])(using cache: HashMap[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]]): Expr[(JsonConfig, JsonParser) => Either[ParseError, T]] = + modules.readerFn[T](ref) + + // TODO: + // * Enumeration + // * Java Primitives + // * Java Classes + // * Java Collections + // * Java Enums + // * Non-case Scala classes + // * SealedTraitRef + // * TraitRef // ----------------------------------- - - def classParseMap[T: Type](ref: ClassRef[T])(using q: Quotes)(using - cache: scala.collection.mutable.HashMap[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]] - ): Expr[JsonParser => Map[String, (JsonConfig, JsonParser) => Either[ParseError, ?]]] = - import Clazzes.* - '{ (parser: JsonParser) => - val daList = ${ - val fieldList = ref.fields.map(f => - f.fieldRef.refType match - case '[m] => - val fn = refFn[m](f.fieldRef.asInstanceOf[RTypeRef[m]]) - '{ - ${ Expr(f.name) } -> $fn - } - ) - Expr.ofList(fieldList) - } - daList.toMap - } diff --git a/src/main/scala/co.blocke.scalajack/json/JsonReaderUtil.scala b/src/main/scala/co.blocke.scalajack/json/JsonReaderUtil.scala new file mode 100644 index 00000000..b53c8bd8 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/JsonReaderUtil.scala @@ -0,0 +1,70 @@ +package co.blocke.scalajack +package json + +import scala.quoted.* +import co.blocke.scala_reflection.reflect.rtypeRefs.* +import co.blocke.scala_reflection.{Clazzes, RTypeRef, TypedName} + +object JsonReaderUtil: + + def classInstantiator[T: Type](ref: ClassRef[T])(using Quotes): Expr[Map[String, ?] => T] = + import quotes.reflect.* + val sym = TypeRepr.of[T].classSymbol.get + '{ (fieldMap: Map[String, ?]) => + ${ + val tree = Apply( + Select.unique(New(TypeIdent(sym)), ""), + ref.fields.map { f => + f.fieldRef.refType match + case '[t] => + '{ fieldMap(${ Expr(f.name) }).asInstanceOf[t] }.asTerm + } + ) + tree.asExpr.asExprOf[T] + } + } + + def tupleInstantiator[T: Type](ref: TupleRef[T])(using Quotes): Expr[List[?] => T] = + import quotes.reflect.* + val sym = TypeRepr.of[T].classSymbol.get + '{ (untyped: List[?]) => + ${ + val tree = Apply( + // Must correctly type the tuple, both the class type params and the individual values + TypeApply( + Select.unique(New(TypeIdent(sym)), ""), + ref.tupleRefs.map{ r => + r.refType match + case '[s] => + TypeTree.of[s] + } + ), + ref.tupleRefs.zipWithIndex.map { (f,j) => + f.refType match + case '[t] => + val jExpr = Expr(j) + '{ untyped($jExpr).asInstanceOf[t] }.asTerm + } + ) + tree.asExpr.asExprOf[T] + } + } + + def classParseMap[T: Type](ref: ClassRef[T], reader: ReaderModule)(using q: Quotes)(using + cache: scala.collection.mutable.HashMap[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]] + ): Expr[JsonParser => Map[String, (JsonConfig, JsonParser) => Either[ParseError, ?]]] = + import Clazzes.* + '{ (parser: JsonParser) => + val daList = ${ + val fieldList = ref.fields.map(f => + f.fieldRef.refType match + case '[m] => + val fn = reader.readerFn[m](f.fieldRef.asInstanceOf[RTypeRef[m]]) + '{ + ${ Expr(f.name) } -> $fn + } + ) + Expr.ofList(fieldList) + } + daList.toMap + } diff --git a/src/main/scala/co.blocke.scalajack/json/ReaderModule.scala b/src/main/scala/co.blocke.scalajack/json/ReaderModule.scala new file mode 100644 index 00000000..0050afd7 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/ReaderModule.scala @@ -0,0 +1,24 @@ +package co.blocke.scalajack +package json + +import co.blocke.scala_reflection.{TypedName, RTypeRef} +import scala.quoted.* +import scala.collection.mutable.HashMap +import scala.util.{Try, Success, Failure} + +trait ReaderModule: + val root: ReaderModule + def readerFn[T](ref: RTypeRef[T], isMapKey: Boolean = false)(using q: Quotes, tt: Type[T])(using cache: HashMap[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]]): Expr[(JsonConfig, JsonParser) => Either[ParseError, T]] + +case class TerminusReaderModule(extension: Option[ReaderModule], root: ReaderModule) extends ReaderModule: + def readerFn[T](ref: RTypeRef[T], isMapKey: Boolean = false)(using q: Quotes, tt: Type[T])(using cache: HashMap[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]]): Expr[(JsonConfig, JsonParser) => Either[ParseError, T]] = + ref match + case t => + val extResult = extension match + case None => Failure(ParseError("???")) + case Some(ext) => Try(ext.readerFn[T](t)) + extResult match + case Success(v) => v + case Failure(_) => + val className = Expr(t.name) + '{ (j: JsonConfig, p: JsonParser) => Left(ParseError("Unknown (or unsupported) RTypeRef class " + $className)) } diff --git a/src/main/scala/co.blocke.scalajack/json/readers/ClassReader.scala b/src/main/scala/co.blocke.scalajack/json/readers/ClassReader.scala new file mode 100644 index 00000000..1fc05220 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/readers/ClassReader.scala @@ -0,0 +1,58 @@ +package co.blocke.scalajack +package json +package readers + +import co.blocke.scala_reflection.reflect.rtypeRefs.* +import co.blocke.scala_reflection.rtypes.* +import co.blocke.scala_reflection.{Clazzes, TypedName, RTypeRef} +import co.blocke.scala_reflection.Liftables.TypedNameToExpr +import scala.quoted.* +import scala.collection.mutable.HashMap +import scala.util.{Try, Success, Failure} + +case class ClassReader(next: ReaderModule, root: ReaderModule) extends ReaderModule: + + def readerFn[T](ref: RTypeRef[T], isMapKey: Boolean = false)(using q: Quotes, tt: Type[T])(using cache: HashMap[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]]): Expr[(JsonConfig, JsonParser) => Either[ParseError, T]] = + import quotes.reflect.* + import Clazzes.* + + ref match + case t: ScalaClassRef[T] => + if isMapKey then throw new JsonError("Class types cannot be map keys.") + t.refType match + case '[s] => + val parseTable = JsonReaderUtil.classParseMap[T](t, root) + val instantiator = JsonReaderUtil.classInstantiator[T](t) + val classFn = '{ (j: JsonConfig, p: JsonParser) => + val rtype = ${ t.expr }.asInstanceOf[ScalaClassRType[T]] + val presetFieldValues = scala.collection.mutable.HashMap.empty[String, Any] + rtype.fields.foreach(f => + if f.fieldType.clazz == OptionClazz then presetFieldValues.put(f.name, None) + else if f.asInstanceOf[ScalaFieldInfo].defaultValueAccessorName.isDefined then + val (companion, accessor) = f.asInstanceOf[ScalaFieldInfo].defaultValueAccessorName.get + + // Have to use Java reflection here to get default value--Scala compiler won't have access to companion + // or accessor if we do a ${} block, and using compiler staging would murder performance. + val defaultValue = { + val c = Class.forName(companion) + val cons = c.getDeclaredConstructor() + cons.setAccessible(true) + val m = c.getMethod(accessor) + m.setAccessible(true) + m.invoke(cons.newInstance()) + } + presetFieldValues.put(f.name, defaultValue) + ) + val classFieldMap = $parseTable(p) // Map[String, JsonConfig => Either[ParseError, ?]] + p.expectClass(j, classFieldMap, presetFieldValues) + .flatMap(fieldValues => + scala.util.Try($instantiator(fieldValues.toMap)) match // instantiate the class here!!! + case Success(v) => Right(v) + case Failure(e) => Left(ParseError(s"Unable to instantiate class at position [${p.getPos - 1}] with message ${e.getMessage}")) + ) + } + cache.put(Expr(t.typedName), classFn) + classFn + + case t => + next.readerFn[T](t) \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json/readers/CollectionReader.scala b/src/main/scala/co.blocke.scalajack/json/readers/CollectionReader.scala new file mode 100644 index 00000000..5c629091 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/readers/CollectionReader.scala @@ -0,0 +1,83 @@ +package co.blocke.scalajack +package json +package readers + +import co.blocke.scala_reflection.reflect.rtypeRefs.* +import co.blocke.scala_reflection.reflect.* +import co.blocke.scala_reflection.{Clazzes, RTypeRef, TypedName} +import co.blocke.scala_reflection.rtypes.* +import co.blocke.scala_reflection.Liftables.TypedNameToExpr +import scala.quoted.* +import scala.collection.Factory +import co.blocke.scala_reflection.RType +import scala.jdk.CollectionConverters.* +import scala.collection.mutable.HashMap +import scala.util.{Failure, Success, Try} + +case class ColletionReader(next: ReaderModule, root: ReaderModule) extends ReaderModule: + + def readerFn[T](ref: RTypeRef[T], isMapKey: Boolean = false)(using q: Quotes, tt: Type[T])(using cache: HashMap[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]]): Expr[(JsonConfig, JsonParser) => Either[ParseError, T]] = + import quotes.reflect.* + + ref match + case t: SeqRef[T] => + if isMapKey then throw new JsonError("Seq types cannot be map keys.") + t.refType match + case '[s] => + t.elementRef.refType match + case '[e] => + val subFn = root.readerFn[e](t.elementRef.asInstanceOf[RTypeRef[e]]).asInstanceOf[Expr[(JsonConfig, JsonParser) => Either[ParseError, e]]] + '{ (j: JsonConfig, p: JsonParser) => + p.expectList[e](j, $subFn).map(_.to(${ Expr.summon[Factory[e, T]].get })) // Convert List to whatever the target type should be + } + + case t: MapRef[T] => + if isMapKey then throw new JsonError("Map types cannot be map keys.") + t.refType match + case '[m] => + t.elementRef.refType match + case '[k] => + t.elementRef2.refType match + case '[v] => + val keyFn = root.readerFn[k](t.elementRef.asInstanceOf[RTypeRef[k]], true).asInstanceOf[Expr[(JsonConfig, JsonParser) => Either[ParseError, k]]] + val valFn = root.readerFn[v](t.elementRef2.asInstanceOf[RTypeRef[v]]).asInstanceOf[Expr[(JsonConfig, JsonParser) => Either[ParseError, v]]] + '{ (j: JsonConfig, p: JsonParser) => + val z = p.expectObject[k, v](j, $keyFn, $valFn).map(_.to(${ Expr.summon[Factory[(k, v), T]].get })) // Convert List to whatever the target type should be + println(s"Z ${p.getPos}: " + z) + z + } + + case t: ArrayRef[T] => + if isMapKey then throw new JsonError("Arrays cannot be map keys.") + t.refType match + case '[s] => + t.elementRef.refType match + case '[e] => + val subFn = root.readerFn[e](t.elementRef.asInstanceOf[RTypeRef[e]]).asInstanceOf[Expr[(JsonConfig, JsonParser) => Either[ParseError, e]]] + '{ (j: JsonConfig, p: JsonParser) => + p.expectList[e](j, $subFn).map(_.to(${ Expr.summon[Factory[e, T]].get })) // Convert List to whatever the target type should be + } + + case t: TupleRef[T] => + if isMapKey then throw new JsonError("Tuple types cannot be map keys.") + t.refType match + case '[s] => + val tupleFns = Expr.ofList( + t.tupleRefs.map(tr => + tr.refType match + case '[e] => + root.readerFn[e](tr.asInstanceOf[RTypeRef[e]]).asInstanceOf[Expr[(JsonConfig, JsonParser) => Either[ParseError, e]]] + ) + ) + val instantiator = JsonReaderUtil.tupleInstantiator[T](t) + '{ (j: JsonConfig, p: JsonParser) => + p.expectTuple(j, $tupleFns) + .flatMap(results => + scala.util.Try($instantiator(results)) match // instantiate the tuple here!!! + case Success(v) => Right(v) + case Failure(e) => Left(ParseError(s"Unable to instantiate tuple at position [${p.getPos - 1}] with message ${e.getMessage}")) + ) + } + + case t => + next.readerFn[T](t) \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json/readers/EnumReader.scala b/src/main/scala/co.blocke.scalajack/json/readers/EnumReader.scala new file mode 100644 index 00000000..d8e8b9b1 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/readers/EnumReader.scala @@ -0,0 +1,58 @@ +package co.blocke.scalajack +package json +package readers + +import co.blocke.scala_reflection.reflect.rtypeRefs.* +import co.blocke.scala_reflection.rtypes.* +import co.blocke.scala_reflection.{Clazzes, TypedName, RTypeRef} +import scala.quoted.* +import scala.collection.mutable.HashMap +import scala.util.{Try, Success, Failure} + +case class EnumReader(next: ReaderModule, root: ReaderModule) extends ReaderModule: + + def readerFn[T](ref: RTypeRef[T], isMapKey: Boolean = false)(using q: Quotes, tt: Type[T])(using cache: HashMap[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]]): Expr[(JsonConfig, JsonParser) => Either[ParseError, T]] = + import quotes.reflect.* + import Clazzes.* + + ref match + case t: ScalaEnumRef[T] => + t.refType match + case '[s] => + val rtypeExpr = t.expr + val wrappedMapKey = Expr(isMapKey) + '{ (j: JsonConfig, p: JsonParser) => + val rtype = $rtypeExpr.asInstanceOf[ScalaEnumRType[T]] + def readNumericEnum = + if $wrappedMapKey then + (for { + _ <- p.expectQuote + enumVal <- p.expectLong(j, p) + _ <- p.expectQuote + } yield enumVal).flatMap { v => + val fromOrdinalMethod = Class.forName(rtype.name).getMethod("fromOrdinal", classOf[Int]) + scala.util.Try(fromOrdinalMethod.invoke(null, v.toInt).asInstanceOf[T]) match + case Success(v2) => Right(v2) + case Failure(e) => Left(JsonParseError(s"No enum value in ${rtype.name} for ordinal value '$v'")) + } + else + p.expectLong(j, p).flatMap { v => + val fromOrdinalMethod = Class.forName(rtype.name).getMethod("fromOrdinal", classOf[Int]) + scala.util.Try(fromOrdinalMethod.invoke(null, v.toInt).asInstanceOf[T]) match + case Success(v2) => Right(v2) + case Failure(e) => Left(JsonParseError(s"No enum value in ${rtype.name} for ordinal value '$v'")) + } + j.enumsAsIds match + case '*' => readNumericEnum + case enumList: List[String] if enumList.contains(rtype.name) => readNumericEnum + case _ => + p.expectString(j, p).flatMap { v => + val valueOfMethod = Class.forName(rtype.name).getMethod("valueOf", classOf[String]) + scala.util.Try(valueOfMethod.invoke(null, v).asInstanceOf[T]) match + case Success(v2) => Right(v2) + case Failure(e) => Left(JsonParseError(s"No enum value in ${rtype.name} for value '$v'")) + } + } + + case t => + next.readerFn[T](t) \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json/readers/MiscReader.scala b/src/main/scala/co.blocke.scalajack/json/readers/MiscReader.scala new file mode 100644 index 00000000..cdb5e11d --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/readers/MiscReader.scala @@ -0,0 +1,76 @@ +package co.blocke.scalajack +package json +package readers + +import co.blocke.scala_reflection.reflect.rtypeRefs.* +import co.blocke.scala_reflection.rtypes.* +import co.blocke.scala_reflection.{Clazzes, TypedName, RTypeRef} +import scala.quoted.* +import scala.collection.mutable.HashMap +import scala.util.{Try, Success, Failure} + +case class MiscReader(next: ReaderModule, root: ReaderModule) extends ReaderModule: + + def readerFn[T](ref: RTypeRef[T], isMapKey: Boolean = false)(using q: Quotes, tt: Type[T])(using cache: HashMap[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]]): Expr[(JsonConfig, JsonParser) => Either[ParseError, T]] = + import quotes.reflect.* + import Clazzes.* + + ref match + case t: ScalaOptionRef[T] => + if isMapKey then throw new JsonError("Options cannot be map keys.") + t.refType match + case '[s] => + t.optionParamType.refType match + case '[e] => + val subFn = readerFn[e](t.optionParamType.asInstanceOf[RTypeRef[e]]).asInstanceOf[Expr[(JsonConfig, JsonParser) => Either[ParseError, e]]] + val isNullable = Expr(t.optionParamType.isNullable) + '{ (j: JsonConfig, p: JsonParser) => + p.nullCheck match { + case true if j.forbidNullsInInput => Left(JsonParseError(s"Forbidden 'null' value received at position [${p.getPos}]")) + case true if j.noneAsNull => Right(None.asInstanceOf[T]) + case true if ! $isNullable => Left(JsonParseError(s"Null value given for non-nullable value type at position [${p.getPos}]")) + case true => Right(Some(null).asInstanceOf[T]) + case false => $subFn(j, p).map(v => Some(v).asInstanceOf[T]) + } + } + + case t: AliasRef[T] => + t.refType match + case '[s] => + t.unwrappedType.refType match + case '[e] => + val subFn = readerFn[e](t.unwrappedType.asInstanceOf[RTypeRef[e]], isMapKey).asInstanceOf[Expr[(JsonConfig, JsonParser) => Either[ParseError, e]]] + '{ (j: JsonConfig, p: JsonParser) => $subFn(j, p).asInstanceOf[Either[co.blocke.scalajack.json.ParseError, T]] } + + case t: SelfRefRef[T] => + if isMapKey then throw new JsonError("Class or trait types cannot be map keys.") + t.refType match + case '[s] => + val className = Expr(t.typedName.toString) + '{ (j: JsonConfig, p: JsonParser) => + val cname = $className + p.cache.get(cname.asInstanceOf[TypedName]) match + case Some(fn) => fn(j, p).asInstanceOf[Either[co.blocke.scalajack.json.ParseError, T]] + case None => Left(ParseError(s"Expected self-ref class $cname but none found in cache at position [${p.getPos}]")) + } + + case t: TryRef[T] => + if isMapKey then throw new JsonError("Try values cannot be map keys.") + t.refType match + case '[s] => + t.tryRef.refType match + case '[e] => + val subFn = readerFn[e](t.tryRef.asInstanceOf[RTypeRef[e]]).asInstanceOf[Expr[(JsonConfig, JsonParser) => Either[ParseError, e]]] + val isNullable = Expr(t.tryRef.isNullable) + '{ (j: JsonConfig, p: JsonParser) => + p.nullCheck match { + case true if j.forbidNullsInInput => Left(JsonParseError(s"Forbidden 'null' value received at position [${p.getPos}]")) + case true if j.tryFailureHandling == TryOption.AS_NULL => Right(Failure(ParseError("null value received indicating Try failure")).asInstanceOf[T]) + case true if ! $isNullable => Left(JsonParseError(s"Null value given for non-nullable value type at position [${p.getPos}]")) + case true => Right(Success(null).asInstanceOf[T]) + case false => $subFn(j, p).map(v => Success(v).asInstanceOf[T]) + } + } + + case t => + next.readerFn[T](t) \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json/readers/PrimitiveReader.scala b/src/main/scala/co.blocke.scalajack/json/readers/PrimitiveReader.scala new file mode 100644 index 00000000..0963f1c6 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/readers/PrimitiveReader.scala @@ -0,0 +1,107 @@ +package co.blocke.scalajack +package json +package readers + +import co.blocke.scala_reflection.reflect.rtypeRefs.* +import co.blocke.scala_reflection.reflect.* +import co.blocke.scala_reflection.{Clazzes, RTypeRef, TypedName} +import co.blocke.scala_reflection.rtypes.* +import co.blocke.scala_reflection.Liftables.TypedNameToExpr +import scala.quoted.* +import scala.collection.Factory +import co.blocke.scala_reflection.RType +import scala.jdk.CollectionConverters.* +import scala.collection.mutable.HashMap +import scala.util.{Failure, Success, Try} + +case class PrimitiveReader(next: ReaderModule, root: ReaderModule) extends ReaderModule: + + def readerFn[T](ref: RTypeRef[T], isMapKey: Boolean = false)(using q: Quotes, tt: Type[T])(using cache: HashMap[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]]): Expr[(JsonConfig, JsonParser) => Either[ParseError, T]] = + import Clazzes.* + import quotes.reflect.* + + ref match + case t: PrimitiveRef[?] if t.name == BOOLEAN_CLASS => + if isMapKey then + '{ (j: JsonConfig, p: JsonParser) => + (for { + _ <- p.expectQuote + v <- p.expectBoolean(j, p).map(_.asInstanceOf[T]) + _ <- p.expectQuote + } yield v) + } + else '{ (j: JsonConfig, p: JsonParser) => p.expectBoolean(j, p).map(_.asInstanceOf[T]) } + case t: PrimitiveRef[?] if t.name == BYTE_CLASS => + if isMapKey then + '{ (j: JsonConfig, p: JsonParser) => + (for { + _ <- p.expectQuote + v <- p.expectLong(j, p).map(_.toByte.asInstanceOf[T]) + _ <- p.expectQuote + } yield v) + } + else '{ (j: JsonConfig, p: JsonParser) => p.expectLong(j, p).map(_.toByte.asInstanceOf[T]) } + case t: PrimitiveRef[?] if t.name == CHAR_CLASS => + '{ (j: JsonConfig, p: JsonParser) => + p.expectString(j, p) + .flatMap(s => + s.toArray.headOption match + case Some(c) => Right(c.asInstanceOf[T]) + case None => Left(JsonParseError(s"Cannot convert value '$s' into a Char.")) + ) + } + case t: PrimitiveRef[?] if t.name == DOUBLE_CLASS => + if isMapKey then + '{ (j: JsonConfig, p: JsonParser) => + (for { + _ <- p.expectQuote + v <- p.expectDouble(j, p).map(_.asInstanceOf[T]) + _ <- p.expectQuote + } yield v) + } + else '{ (j: JsonConfig, p: JsonParser) => p.expectDouble(j, p).map(_.asInstanceOf[T]) } + case t: PrimitiveRef[?] if t.name == FLOAT_CLASS => + if isMapKey then + '{ (j: JsonConfig, p: JsonParser) => + (for { + _ <- p.expectQuote + v <- p.expectDouble(j, p).map(_.toFloat.asInstanceOf[T]) + _ <- p.expectQuote + } yield v) + } + else '{ (j: JsonConfig, p: JsonParser) => p.expectDouble(j, p).map(_.toFloat.asInstanceOf[T]) } + case t: PrimitiveRef[?] if t.name == INT_CLASS => + if isMapKey then + '{ (j: JsonConfig, p: JsonParser) => + (for { + _ <- p.expectQuote + v <- p.expectLong(j, p).map(_.toInt.asInstanceOf[T]) + _ <- p.expectQuote + } yield v) + } + else '{ (j: JsonConfig, p: JsonParser) => p.expectLong(j, p).map(_.toInt.asInstanceOf[T]) } + case t: PrimitiveRef[?] if t.name == LONG_CLASS => + if isMapKey then + '{ (j: JsonConfig, p: JsonParser) => + (for { + _ <- p.expectQuote + v <- p.expectLong(j, p).map(_.asInstanceOf[T]) + _ <- p.expectQuote + } yield v) + } + else '{ (j: JsonConfig, p: JsonParser) => p.expectLong(j, p).map(_.asInstanceOf[T]) } + case t: PrimitiveRef[?] if t.name == SHORT_CLASS => + if isMapKey then + '{ (j: JsonConfig, p: JsonParser) => + (for { + _ <- p.expectQuote + v <- p.expectLong(j, p).map(_.toShort.asInstanceOf[T]) + _ <- p.expectQuote + } yield v) + } + else '{ (j: JsonConfig, p: JsonParser) => p.expectLong(j, p).map(_.toShort.asInstanceOf[T]) } + case t: PrimitiveRef[T] if t.name == STRING_CLASS => + '{ (j: JsonConfig, p: JsonParser) => p.expectString(j, p).map(_.asInstanceOf[T]) } + + case t => + next.readerFn[T](t) \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index 5909e858..e85ace0f 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -21,27 +21,10 @@ object RunMe extends App: // .copy(enumsAsIds = '*') try - // Works! - // val c = Class.forName("co.blocke.scalajack.run.Color") - // val valueOfMethod = c.getMethod("valueOf", classOf[String]) - // println(valueOfMethod.invoke(null, "Boom")) - - // println("Worked? " + c.valueOf("Blue")) - // println("Color: " + c.getClass.getName) - - // println("RESULT: " + ScalaJack.read[Blah]("""{"msg":"Greg\nZoller", "stuff": {"a":-100, "b":false, "c":{"a":2,"b":true}}}""")) - - // println("RESULT: " + ScalaJack.read[json.Blah]("""{"msg":"Greg","isOk":true,"age":57}""")) - - // case class M1( v: Map[Int, Int], v2: Map[Colors, Int], v3: Map[co.blocke.scala_reflection.TypedName, Int]) - val x = M1( - Map(1L -> 2, 3L -> 4), - scala.collection.immutable.HashMap(Colors.Red -> 5, Colors.Blue -> 6), - Map("a".asInstanceOf[TypedName] -> 7, "b".asInstanceOf[TypedName] -> 8) - ) + val x = Blah("foo", (5, true, "wow")) val js = ScalaJack.write(x) println(js) - val inst = ScalaJack.read[M1](js) + val inst = ScalaJack.read[Blah](js) println(inst) catch { @@ -50,72 +33,3 @@ object RunMe extends App: println(t.printStackTrace) println(t.getClass.getName) } - - // val t0 = System.currentTimeMillis() - // for i <- (0 to 10000) do ScalaJack.read[json.Blah]("""{"msg":"Greg","isOk":true,"age":57}""") - // val t1 = System.currentTimeMillis() - // println("TIME: " + (t1 - t0)) - - // inline def read[T](js: String)(using cfg: JsonConfig = JsonConfig()): T = ${ readImpl[T]('js, 'cfg) } - - // def expectList[T]( expectElement: ()=>Either[ParseError,T]): Either[ParseError,List[T]] = - -/* - val p = Person("Greg", 57, List(false, true, true), Colors.Blue, "Fred".asInstanceOf[BigName]) - - val d = Dog("Fido", 4, 2, Some(Dog("Mindy", 4, 0, None))) - val d2 = Dog("Spot", 4, 3, Some(Dog("Floppy", 3, 1, None))) - - val mapper = (a: Any) => - a.getClass.getPackage.getName match - case p if p.startsWith("co.blocke") => "Blocke" - case x => "Something " + x.getClass.getName - - given json.JsonConfig = json - .JsonConfig() - .copy( - typeHintDefaultTransformer = (s: String) => s.split("\\.").last, - typeHintLabelByTrait = Map("co.blocke.scalajack.run.Animal" -> "kind"), - typeHintTransformer = Map("co.blocke.scalajack.run.Dog" -> mapper) - ) - - println(ScalaJack.write(d)) - - val t0 = System.currentTimeMillis() - for i <- 0 to 10000 do - ScalaJack.write(d) - // if i % 100 == 0 then println(i) - // if i == 10000 then println(i) - - // println(Codec.write(d)(using cfg)) - // println("") - // println(Codec.write(d2)(using cfg)) - // println("") - // println(Codec.write(d)(using cfg)) - - val t1 = System.currentTimeMillis() - println("TIME: " + (t1 - t0)) - */ - -/* - - case SomeRef => - - // Compile-Time - // * Quotes - // * Class details - - // Build field parse map: - Map( - "name" -> ()=>expectString() - "lists" -> ()=>expectList(()=>expectString()) - ) - - '{ - // Runtime - // * json - // * cfg - } - - - */ diff --git a/src/main/scala/co.blocke.scalajack/run/Sample.scala b/src/main/scala/co.blocke.scalajack/run/Sample.scala index 576679ff..2c819ef8 100644 --- a/src/main/scala/co.blocke.scalajack/run/Sample.scala +++ b/src/main/scala/co.blocke.scalajack/run/Sample.scala @@ -25,7 +25,7 @@ enum Vehicle: case class Simple(a: Int, b: Boolean, c: Option[Simple], z: Int = 5) -case class Blah(msg: String, stuff: Simple) +case class Blah(msg: String, stuff: (Int, Boolean, String)) object Talk: def say(s: String): String = s"Say $s!" From 826a30d52e8742f7f9992d15b653083cbf915816 Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Sun, 29 Oct 2023 00:35:16 -0500 Subject: [PATCH 14/65] tests started --- build.sbt | 7 +- .../co.blocke.scalajack/json/JsonError.scala | 2 +- .../co.blocke.scalajack/json/JsonParser.scala | 127 +++++-- .../co.blocke.scalajack/json/JsonReader.scala | 6 +- .../json/JsonReaderUtil.scala | 18 +- .../co.blocke.scalajack/json/JsonWriter.scala | 45 ++- .../json/ReaderModule.scala | 16 +- .../json/readers/ClassReader.scala | 10 +- .../json/readers/CollectionReader.scala | 37 +- .../json/readers/EnumReader.scala | 55 ++- .../json/readers/MiscReader.scala | 22 +- .../json/readers/PrimitiveReader.scala | 158 ++++++++- .../scala/co.blocke.scalajack/run/Play.scala | 9 +- .../co.blocke.scalajack/run/Sample.scala | 8 +- .../scala/co.blocke.scalajack/JsonDiff.scala | 58 +++ .../co.blocke.scalajack/JsonMatcher.scala | 61 ++++ .../scala/co.blocke.scalajack/TestUtil.scala | 42 +++ .../json/primitives/Model.scala | 197 +++++++++++ .../json/primitives/ScalaPrim.scala | 329 ++++++++++++++++++ 19 files changed, 1083 insertions(+), 124 deletions(-) create mode 100644 src/test/scala/co.blocke.scalajack/JsonDiff.scala create mode 100644 src/test/scala/co.blocke.scalajack/JsonMatcher.scala create mode 100644 src/test/scala/co.blocke.scalajack/TestUtil.scala create mode 100644 src/test/scala/co.blocke.scalajack/json/primitives/Model.scala create mode 100644 src/test/scala/co.blocke.scalajack/json/primitives/ScalaPrim.scala diff --git a/build.sbt b/build.sbt index 74bcfb46..ceeca868 100644 --- a/build.sbt +++ b/build.sbt @@ -35,8 +35,11 @@ lazy val root = project Test / parallelExecution := false, scalafmtOnCompile := !isCI, libraryDependencies ++= Seq( - "co.blocke" %% "scala-reflection" % "sj_fixes_58a385", - "org.scalameta" %% "munit" % "1.0.0-M9" % Test + "co.blocke" %% "scala-reflection" % "sj_fixes_58a385", + "org.apache.commons" % "commons-text" % "1.10.0", + "org.scalameta" %% "munit" % "1.0.0-M9" % Test, + "org.json4s" %% "json4s-core" % "4.0.6" % Test, + "org.json4s" %% "json4s-native" % "4.0.6" % Test ) ) diff --git a/src/main/scala/co.blocke.scalajack/json/JsonError.scala b/src/main/scala/co.blocke.scalajack/json/JsonError.scala index 8b07a73e..076a7388 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonError.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonError.scala @@ -3,6 +3,6 @@ package json class JsonError(msg: String) extends Throwable -class ParseError(message: String) extends Throwable(message) +abstract class ParseError(message: String) extends Throwable(message) case class JsonParseError(message: String) extends ParseError(message) case class CommaExpected(message: String = "") extends ParseError(message) diff --git a/src/main/scala/co.blocke.scalajack/json/JsonParser.scala b/src/main/scala/co.blocke.scalajack/json/JsonParser.scala index 6df54c55..928df8d5 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonParser.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonParser.scala @@ -26,7 +26,7 @@ case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) if jsChars(i) == '\"' then i += 1 Right(()) - else Left(ParseError(s"Quote expected at position [$i]")) + else Left(JsonParseError(showError(s"Quote expected at position [$i]"))) @inline def expectComma: Either[ParseError, Unit] = // Note: this consumes whitespace before/after the ',' for { _ <- eatWhitespace @@ -35,7 +35,7 @@ case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) i += 1 eatWhitespace Right(()) - else Left(CommaExpected()) + else Left(CommaExpected(showError(s"Comma expected at position [$i]"))) } yield r @inline def expectColon: Either[ParseError, Unit] = // Note: this consumes whitespace before/after the ':' for { @@ -45,7 +45,7 @@ case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) i += 1 eatWhitespace Right(()) - else Left(JsonParseError(s"Expected colon at position [$i]")) + else Left(JsonParseError(showError(s"Expected colon at position [$i]"))) } yield r // JSON label (i.e. object key, which is always a simple string, ie no escaped/special chars) @@ -57,7 +57,7 @@ case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) while i < max && jsChars(i) != '"' do i += 1 i += 1 Right(js.substring(mark, i - 1)) - case x => Left(JsonParseError(s"Unexpected character '$x' where beginning of label expected at position [$i]")) + case x => Left(JsonParseError(showError(s"Unexpected character '$x' where beginning of label expected at position [$i]"))) // Data Types // ------------------------------ @@ -69,7 +69,7 @@ case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) case 'f' if i + 4 < max && jsChars(i + 1) == 'a' && jsChars(i + 2) == 'l' && jsChars(i + 3) == 's' && jsChars(i + 4) == 'e' => i += 5 Right(false) - case x => Left(JsonParseError(s"Unexpected character '$x' where beginning of boolean value expected at position [$i]")) + case x => Left(JsonParseError(showError(s"Unexpected character '$x' where beginning of boolean value expected at position [$i]"))) def expectLong(cfg: JsonConfig, p: JsonParser): Either[ParseError, Long] = val mark = i @@ -85,7 +85,27 @@ case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) if mark == i then s"""Int/Long expected but couldn't parse from "${jsChars(i)}" at position [$i]""" else s"""Int/Long expected but couldn't parse from "${js.substring(mark, i)}" at position [$i]""" i = mark - Left(JsonParseError(msg)) + Left(JsonParseError(showError(msg))) + + def expectBigLong(cfg: JsonConfig, p: JsonParser): Either[ParseError, BigInt] = + nullCheck match + case true if cfg.forbidNullsInInput => Left(JsonParseError(showError(s"Forbidden 'null' value received at position [$i]"))) + case true => Right(null.asInstanceOf[BigInt]) + case false => + val mark = i + var done = false + while !done do + jsChars(i) match + case c if (c >= '0' && c <= '9') || c == '-' => i += 1 + case _ => done = true + Try(BigInt(js.substring(mark, i))) match + case Success(g) => Right(g) + case Failure(f) => + val msg = + if mark == i then s"""Int/Long expected but couldn't parse from "${jsChars(i)}" at position [$i]""" + else s"""Int/Long expected but couldn't parse from "${js.substring(mark, i)}" at position [$i]""" + i = mark + Left(JsonParseError(showError(msg))) def expectDouble(cfg: JsonConfig, p: JsonParser): Either[ParseError, Double] = val mark = i @@ -94,18 +114,38 @@ case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) jsChars(i) match case c if (c >= '0' && c <= '9') || c == '-' || c == '.' || c == 'e' || c == 'E' || c == '+' => i += 1 case _ => done = true - Try(js.substring(mark, i - 1).toDouble) match + Try(js.substring(mark, i).toDouble) match case Success(g) => Right(g) case Failure(_) => val msg = if mark == i then s"Float/Double expected but couldn't parse from \"${jsChars(i)}\" at position [$i]" - else s"Float/Double expected but couldn't parse from \"${js.substring(mark, i - 1)}\" at position [$i]" + else s"Float/Double expected but couldn't parse from \"${js.substring(mark, i)}\" at position [$i]" i = mark - Left(JsonParseError(msg)) + Left(JsonParseError(showError(msg))) + + def expectBigDouble(cfg: JsonConfig, p: JsonParser): Either[ParseError, BigDecimal] = + nullCheck match + case true if cfg.forbidNullsInInput => Left(JsonParseError(showError(s"Forbidden 'null' value received at position [$i]"))) + case true => Right(null.asInstanceOf[BigDecimal]) + case false => + val mark = i + var done = false + while !done do + jsChars(i) match + case c if (c >= '0' && c <= '9') || c == '-' || c == '.' || c == 'e' || c == 'E' || c == '+' => i += 1 + case _ => done = true + Try(BigDecimal(js.substring(mark, i))) match + case Success(g) => Right(g) + case Failure(_) => + val msg = + if mark == i then s"Float/Double expected but couldn't parse from \"${jsChars(i)}\" at position [$i]" + else s"Float/Double expected but couldn't parse from \"${js.substring(mark, i)}\" at position [$i]" + i = mark + Left(JsonParseError(showError(msg))) def expectString(cfg: JsonConfig, p: JsonParser): Either[ParseError, String] = nullCheck match - case true if cfg.forbidNullsInInput => Left(JsonParseError(s"Forbidden 'null' value received at position [$i]")) + case true if cfg.forbidNullsInInput => Left(JsonParseError(showError(s"Forbidden 'null' value received at position [$i]"))) case true => Right(null.asInstanceOf[String]) case false => jsChars(i) match @@ -121,35 +161,39 @@ case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) case _ => i += 1 i += 1 Right(captured.getOrElse(js.substring(mark, i - 1))) - case x => Left(JsonParseError(s"Unexpected character '$x' where beginning of label expected at position [$i]")) + case x => Left(JsonParseError(showError(s"Unexpected character '$x' where beginning of label expected at position [$i]"))) def expectList[T](cfg: JsonConfig, expectElement: (JsonConfig, JsonParser) => Either[ParseError, T]): Either[ParseError, List[T]] = - if jsChars(i) != '[' then Left(JsonParseError(s"Beginning of list expected at position [$i]")) - else - i += 1 - eatWhitespace - val acc = ListBuffer.empty[T] - var done: Option[Either[ParseError, List[T]]] = None - while done.isEmpty do - (for { - el <- expectElement(cfg, this) - _ = acc.addOne(el) - _ <- expectComma - } yield el) match - case Left(CommaExpected(_)) if jsChars(i) == ']' => - i += 1 - eatWhitespace - done = Some(Right(acc.toList)) - case Left(e) => - done = Some(Left(e)) - case Right(_) => - done.get + nullCheck match + case true if cfg.forbidNullsInInput => Left(JsonParseError(showError(s"Forbidden 'null' value received at position [$i]"))) + case true => Right(null.asInstanceOf[List[T]]) + case false => + if jsChars(i) != '[' then Left(JsonParseError(showError(s"Beginning of list expected at position [$i]"))) + else + i += 1 + eatWhitespace + val acc = ListBuffer.empty[T] + var done: Option[Either[ParseError, List[T]]] = None + while done.isEmpty do + (for { + el <- expectElement(cfg, this) + _ = acc.addOne(el) + _ <- expectComma + } yield el) match + case Left(CommaExpected(_)) if jsChars(i) == ']' => + i += 1 + eatWhitespace + done = Some(Right(acc.toList)) + case Left(e) => + done = Some(Left(e)) + case Right(_) => + done.get def expectTuple( cfg: JsonConfig, tupleFns: List[(JsonConfig, JsonParser) => Either[ParseError, ?]] ): Either[ParseError, List[?]] = - if jsChars(i) != '[' then Left(JsonParseError(s"Beginning of tuple expected at position [$i]")) + if jsChars(i) != '[' then Left(JsonParseError(showError(s"Beginning of tuple expected at position [$i]"))) else i += 1 eatWhitespace @@ -168,16 +212,16 @@ case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) i += 1 eatWhitespace Right(buf.toList) - case Left(CommaExpected(_)) if jsChars(i) == ']' => Left(ParseError(s"Missing required elements in tuple at position [$i]")) + case Left(CommaExpected(_)) if jsChars(i) == ']' => Left(JsonParseError(showError(s"Missing required elements in tuple at position [$i]"))) case Left(e: ParseError) => Left(e) - case Right(_) => Left(ParseError(s"Extra/unexpected tuple fields at position [$i]")) + case Right(_) => Left(JsonParseError(showError(s"Extra/unexpected tuple fields at position [$i]"))) def expectObject[K, V]( cfg: JsonConfig, keyElement: (JsonConfig, JsonParser) => Either[ParseError, K], valueElement: (JsonConfig, JsonParser) => Either[ParseError, V] ): Either[ParseError, Map[K, V]] = - if jsChars(i) != '{' then Left(JsonParseError(s"Beginning of object expected at position [$i]")) + if jsChars(i) != '{' then Left(JsonParseError(showError(s"Beginning of object expected at position [$i]"))) else i += 1 eatWhitespace @@ -206,7 +250,7 @@ case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) fieldMap: Map[String, (JsonConfig, JsonParser) => Either[ParseError, ?]], fieldValues: scala.collection.mutable.HashMap[String, Any] // pre-set values (Option:None, default values) ): Either[ParseError, Map[String, ?]] = - if jsChars(i) != '{' then Left(JsonParseError(s"Beginning of class object expected at position [$i]")) + if jsChars(i) != '{' then Left(JsonParseError(showError(s"Beginning of class object expected at position [$i]"))) else i += 1 eatWhitespace @@ -277,3 +321,14 @@ case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) i += 1 } builder.toString + + def showError(msg: String): String = { + val (clip, dashes) = i match { + case ep if ep <= 50 && max < 80 => (js, ep) + case ep if ep <= 50 => (js.substring(0, 77) + "...", ep) + case ep if ep > 50 && ep + 30 >= max => + ("..." + js.substring(i - 49), 52) + case ep => ("..." + js.substring(ep - 49, ep + 27) + "...", 52) + } + msg + "\n" + clip.replaceAll("[\n\t]", "~") + "\n" + ("-" * dashes) + "^" + } diff --git a/src/main/scala/co.blocke.scalajack/json/JsonReader.scala b/src/main/scala/co.blocke.scalajack/json/JsonReader.scala index 1641bc6e..2b392b1b 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonReader.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonReader.scala @@ -36,13 +36,11 @@ case class JsonReader() extends ReaderModule: modules.readerFn[T](ref) // TODO: - // * Enumeration - // * Java Primitives - // * Java Classes - // * Java Collections // * Java Enums + // * Java Classes // * Non-case Scala classes // * SealedTraitRef // * TraitRef + // * Primitive: Any // ----------------------------------- diff --git a/src/main/scala/co.blocke.scalajack/json/JsonReaderUtil.scala b/src/main/scala/co.blocke.scalajack/json/JsonReaderUtil.scala index b53c8bd8..195fd384 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonReaderUtil.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonReaderUtil.scala @@ -32,14 +32,14 @@ object JsonReaderUtil: val tree = Apply( // Must correctly type the tuple, both the class type params and the individual values TypeApply( - Select.unique(New(TypeIdent(sym)), ""), - ref.tupleRefs.map{ r => - r.refType match - case '[s] => - TypeTree.of[s] - } + Select.unique(New(TypeIdent(sym)), ""), + ref.tupleRefs.map { r => + r.refType match + case '[s] => + TypeTree.of[s] + } ), - ref.tupleRefs.zipWithIndex.map { (f,j) => + ref.tupleRefs.zipWithIndex.map { (f, j) => f.refType match case '[t] => val jExpr = Expr(j) @@ -50,7 +50,7 @@ object JsonReaderUtil: } } - def classParseMap[T: Type](ref: ClassRef[T], reader: ReaderModule)(using q: Quotes)(using + def classParseMap[T: Type](ref: ClassRef[T], root: ReaderModule)(using q: Quotes)(using cache: scala.collection.mutable.HashMap[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]] ): Expr[JsonParser => Map[String, (JsonConfig, JsonParser) => Either[ParseError, ?]]] = import Clazzes.* @@ -59,7 +59,7 @@ object JsonReaderUtil: val fieldList = ref.fields.map(f => f.fieldRef.refType match case '[m] => - val fn = reader.readerFn[m](f.fieldRef.asInstanceOf[RTypeRef[m]]) + val fn = root.readerFn[m](f.fieldRef.asInstanceOf[RTypeRef[m]]) '{ ${ Expr(f.name) } -> $fn } diff --git a/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala b/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala index 3e5f1397..fc6952cc 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala @@ -11,6 +11,7 @@ import co.blocke.scala_reflection.RType import scala.jdk.CollectionConverters.* import java.util.concurrent.ConcurrentHashMap import scala.util.{Failure, Success} +import org.apache.commons.text.StringEscapeUtils object JsonWriter: @@ -30,19 +31,29 @@ object JsonWriter: rtRef match case rt: PrimitiveRef[?] if rt.family == PrimFamily.Stringish => + val nullable = Expr(rt.isNullable) '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - sb.append('"') - sb.append(a.toString) - sb.append('"') + if $nullable && a == null then sb.append("null") + else + sb.append('"') + sb.append(StringEscapeUtils.escapeJson(a.toString)) + sb.append('"') } case rt: PrimitiveRef[?] => + val nullable = Expr(rt.isNullable) if isMapKey then '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - sb.append('"') - sb.append(a.toString) - sb.append('"') + if $nullable && a == null then sb.append("null") + else + sb.append('"') + sb.append(a.toString) + sb.append('"') + } + else + '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => + if $nullable && a == null then sb.append("null") + else sb.append(a.toString) } - else '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => sb.append(a.toString) } case rt: AliasRef[?] => val fn = writeJsonFn[rt.T](rt.unwrappedType.asInstanceOf[RTypeRef[rt.T]], isMapKey)(using Type.of[rt.T]) @@ -54,15 +65,17 @@ object JsonWriter: case '[t] => val elementFn = writeJsonFn[t](rt.elementRef.asInstanceOf[RTypeRef[t]]) '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - sb.append('[') - val sbLen = sb.length - a.asInstanceOf[Array[t]].foreach { e => - if isOkToWrite(e, cfg) then - $elementFn(e.asInstanceOf[t], sb, cfg) - sb.append(',') - } - if sbLen == sb.length then sb.append(']') - else sb.setCharAt(sb.length() - 1, ']') + if a == null then sb.append("null") + else + sb.append('[') + val sbLen = sb.length + a.asInstanceOf[Array[t]].foreach { e => + if isOkToWrite(e, cfg) then + $elementFn(e.asInstanceOf[t], sb, cfg) + sb.append(',') + } + if sbLen == sb.length then sb.append(']') + else sb.setCharAt(sb.length() - 1, ']') } case rt: ClassRef[?] => diff --git a/src/main/scala/co.blocke.scalajack/json/ReaderModule.scala b/src/main/scala/co.blocke.scalajack/json/ReaderModule.scala index 0050afd7..7372b34c 100644 --- a/src/main/scala/co.blocke.scalajack/json/ReaderModule.scala +++ b/src/main/scala/co.blocke.scalajack/json/ReaderModule.scala @@ -1,10 +1,10 @@ package co.blocke.scalajack package json -import co.blocke.scala_reflection.{TypedName, RTypeRef} +import co.blocke.scala_reflection.{RTypeRef, TypedName} import scala.quoted.* import scala.collection.mutable.HashMap -import scala.util.{Try, Success, Failure} +import scala.util.{Failure, Success, Try} trait ReaderModule: val root: ReaderModule @@ -15,10 +15,10 @@ case class TerminusReaderModule(extension: Option[ReaderModule], root: ReaderMod ref match case t => val extResult = extension match - case None => Failure(ParseError("???")) - case Some(ext) => Try(ext.readerFn[T](t)) + case None => Failure(JsonParseError("???")) // Should Never Happen(tm) + case Some(ext) => Try(ext.readerFn[T](t)) extResult match - case Success(v) => v - case Failure(_) => - val className = Expr(t.name) - '{ (j: JsonConfig, p: JsonParser) => Left(ParseError("Unknown (or unsupported) RTypeRef class " + $className)) } + case Success(v) => v + case Failure(_) => + val className = Expr(t.name) + '{ (j: JsonConfig, p: JsonParser) => Left(JsonParseError("Unknown (or unsupported) RTypeRef class " + $className)) } diff --git a/src/main/scala/co.blocke.scalajack/json/readers/ClassReader.scala b/src/main/scala/co.blocke.scalajack/json/readers/ClassReader.scala index 1fc05220..9ccbbe3c 100644 --- a/src/main/scala/co.blocke.scalajack/json/readers/ClassReader.scala +++ b/src/main/scala/co.blocke.scalajack/json/readers/ClassReader.scala @@ -4,11 +4,11 @@ package readers import co.blocke.scala_reflection.reflect.rtypeRefs.* import co.blocke.scala_reflection.rtypes.* -import co.blocke.scala_reflection.{Clazzes, TypedName, RTypeRef} +import co.blocke.scala_reflection.{Clazzes, RTypeRef, TypedName} import co.blocke.scala_reflection.Liftables.TypedNameToExpr import scala.quoted.* import scala.collection.mutable.HashMap -import scala.util.{Try, Success, Failure} +import scala.util.{Failure, Success, Try} case class ClassReader(next: ReaderModule, root: ReaderModule) extends ReaderModule: @@ -48,11 +48,11 @@ case class ClassReader(next: ReaderModule, root: ReaderModule) extends ReaderMod .flatMap(fieldValues => scala.util.Try($instantiator(fieldValues.toMap)) match // instantiate the class here!!! case Success(v) => Right(v) - case Failure(e) => Left(ParseError(s"Unable to instantiate class at position [${p.getPos - 1}] with message ${e.getMessage}")) + case Failure(e) => Left(JsonParseError(p.showError(s"Unable to instantiate class at position [${p.getPos - 1}] with message ${e.getMessage}"))) ) } cache.put(Expr(t.typedName), classFn) classFn - case t => - next.readerFn[T](t) \ No newline at end of file + case t => + next.readerFn[T](t) diff --git a/src/main/scala/co.blocke.scalajack/json/readers/CollectionReader.scala b/src/main/scala/co.blocke.scalajack/json/readers/CollectionReader.scala index 5c629091..3831e4fe 100644 --- a/src/main/scala/co.blocke.scalajack/json/readers/CollectionReader.scala +++ b/src/main/scala/co.blocke.scalajack/json/readers/CollectionReader.scala @@ -42,9 +42,7 @@ case class ColletionReader(next: ReaderModule, root: ReaderModule) extends Reade val keyFn = root.readerFn[k](t.elementRef.asInstanceOf[RTypeRef[k]], true).asInstanceOf[Expr[(JsonConfig, JsonParser) => Either[ParseError, k]]] val valFn = root.readerFn[v](t.elementRef2.asInstanceOf[RTypeRef[v]]).asInstanceOf[Expr[(JsonConfig, JsonParser) => Either[ParseError, v]]] '{ (j: JsonConfig, p: JsonParser) => - val z = p.expectObject[k, v](j, $keyFn, $valFn).map(_.to(${ Expr.summon[Factory[(k, v), T]].get })) // Convert List to whatever the target type should be - println(s"Z ${p.getPos}: " + z) - z + p.expectObject[k, v](j, $keyFn, $valFn).map(_.to(${ Expr.summon[Factory[(k, v), T]].get })) // Convert List to whatever the target type should be } case t: ArrayRef[T] => @@ -55,9 +53,13 @@ case class ColletionReader(next: ReaderModule, root: ReaderModule) extends Reade case '[e] => val subFn = root.readerFn[e](t.elementRef.asInstanceOf[RTypeRef[e]]).asInstanceOf[Expr[(JsonConfig, JsonParser) => Either[ParseError, e]]] '{ (j: JsonConfig, p: JsonParser) => - p.expectList[e](j, $subFn).map(_.to(${ Expr.summon[Factory[e, T]].get })) // Convert List to whatever the target type should be + p.expectList[e](j, $subFn) + .map(_ match + case v if v == null => null.asInstanceOf[T] + case v => v.to(${ Expr.summon[Factory[e, T]].get }) + ) // Convert List to whatever the target type should be } - + case t: TupleRef[T] => if isMapKey then throw new JsonError("Tuple types cannot be map keys.") t.refType match @@ -75,9 +77,28 @@ case class ColletionReader(next: ReaderModule, root: ReaderModule) extends Reade .flatMap(results => scala.util.Try($instantiator(results)) match // instantiate the tuple here!!! case Success(v) => Right(v) - case Failure(e) => Left(ParseError(s"Unable to instantiate tuple at position [${p.getPos - 1}] with message ${e.getMessage}")) + case Failure(e) => Left(JsonParseError(p.showError(s"Unable to instantiate tuple at position [${p.getPos - 1}] with message ${e.getMessage}"))) ) } - case t => - next.readerFn[T](t) \ No newline at end of file + // Java + case t: JavaCollectionRef[T] => + if isMapKey then throw new JsonError("Java collections cannot be map keys.") + t.refType match + case '[s] => + t.elementRef.refType match + case '[e] => + val subFn = root.readerFn[e](t.elementRef.asInstanceOf[RTypeRef[e]]).asInstanceOf[Expr[(JsonConfig, JsonParser) => Either[ParseError, e]]] + val className = Expr(t.name) + '{ (j: JsonConfig, p: JsonParser) => + val cname = $className + p.expectList[e](j, $subFn) + .flatMap(result => + scala.util.Try(Class.forName(cname).getDeclaredConstructor(Class.forName("java.util.Collection")).newInstance(result.asJava)) match + case Success(ok) => Right(ok.asInstanceOf[T]) + case Failure(e) => Left(JsonParseError(p.showError(s"Could not instantiate a $cname, with error: " + e))) + ) + } + + case t => + next.readerFn[T](t) diff --git a/src/main/scala/co.blocke.scalajack/json/readers/EnumReader.scala b/src/main/scala/co.blocke.scalajack/json/readers/EnumReader.scala index d8e8b9b1..7ee73f25 100644 --- a/src/main/scala/co.blocke.scalajack/json/readers/EnumReader.scala +++ b/src/main/scala/co.blocke.scalajack/json/readers/EnumReader.scala @@ -4,10 +4,10 @@ package readers import co.blocke.scala_reflection.reflect.rtypeRefs.* import co.blocke.scala_reflection.rtypes.* -import co.blocke.scala_reflection.{Clazzes, TypedName, RTypeRef} +import co.blocke.scala_reflection.{Clazzes, RTypeRef, TypedName} import scala.quoted.* import scala.collection.mutable.HashMap -import scala.util.{Try, Success, Failure} +import scala.util.{Failure, Success, Try} case class EnumReader(next: ReaderModule, root: ReaderModule) extends ReaderModule: @@ -33,14 +33,14 @@ case class EnumReader(next: ReaderModule, root: ReaderModule) extends ReaderModu val fromOrdinalMethod = Class.forName(rtype.name).getMethod("fromOrdinal", classOf[Int]) scala.util.Try(fromOrdinalMethod.invoke(null, v.toInt).asInstanceOf[T]) match case Success(v2) => Right(v2) - case Failure(e) => Left(JsonParseError(s"No enum value in ${rtype.name} for ordinal value '$v'")) + case Failure(e) => Left(JsonParseError(p.showError(s"No enum value in ${rtype.name} for ordinal value '$v'"))) } else p.expectLong(j, p).flatMap { v => val fromOrdinalMethod = Class.forName(rtype.name).getMethod("fromOrdinal", classOf[Int]) scala.util.Try(fromOrdinalMethod.invoke(null, v.toInt).asInstanceOf[T]) match case Success(v2) => Right(v2) - case Failure(e) => Left(JsonParseError(s"No enum value in ${rtype.name} for ordinal value '$v'")) + case Failure(e) => Left(JsonParseError(p.showError(s"No enum value in ${rtype.name} for ordinal value '$v'"))) } j.enumsAsIds match case '*' => readNumericEnum @@ -50,9 +50,50 @@ case class EnumReader(next: ReaderModule, root: ReaderModule) extends ReaderModu val valueOfMethod = Class.forName(rtype.name).getMethod("valueOf", classOf[String]) scala.util.Try(valueOfMethod.invoke(null, v).asInstanceOf[T]) match case Success(v2) => Right(v2) - case Failure(e) => Left(JsonParseError(s"No enum value in ${rtype.name} for value '$v'")) + case Failure(e) => Left(JsonParseError(p.showError(s"No enum value in ${rtype.name} for value '$v'"))) } } - case t => - next.readerFn[T](t) \ No newline at end of file + case t: ScalaEnumerationRef[T] => + t.refType match + case '[s] => + val rtypeExpr = t.expr + val wrappedMapKey = Expr(isMapKey) + '{ (j: JsonConfig, p: JsonParser) => + val rtype = $rtypeExpr.asInstanceOf[ScalaEnumerationRType[T]] + def readNumericEnum = + val eClazz = Class.forName(rtype.name) + val fromOrdinalMethod = eClazz.getMethod("apply", classOf[Int]) + if $wrappedMapKey then + (for { + _ <- p.expectQuote + enumVal <- p.expectLong(j, p) + _ <- p.expectQuote + } yield enumVal).flatMap { v => + scala.util.Try(fromOrdinalMethod.invoke(null, v.toInt).asInstanceOf[T]) match + case Success(v2) => Right(v2) + case Failure(e) => Left(JsonParseError(p.showError(s"No enumeration value in ${rtype.name} for ordinal value '$v'"))) + } + else + p.expectLong(j, p).flatMap { v => + scala.util.Try(fromOrdinalMethod.invoke(null, v.toInt).asInstanceOf[T]) match + case Success(v2) => Right(v2) + case Failure(e) => Left(JsonParseError(p.showError(s"No enumeration value in ${rtype.name} for ordinal value '$v'"))) + } + j.enumsAsIds match + case '*' => readNumericEnum + case enumList: List[String] if enumList.contains(rtype.name) => readNumericEnum + case _ => + p.expectString(j, p).flatMap { v => + val eClazz = Class.forName(rtype.name) + val valueOfMethod = eClazz.getMethod("withName", classOf[String]) + scala.util.Try(valueOfMethod.invoke(null, v).asInstanceOf[T]) match + case Success(v2) => Right(v2) + case Failure(e) => Left(JsonParseError(p.showError(s"No enumeration value in ${rtype.name} for value '$v'"))) + } + } + + // TODO: JavaEnumRef + + case t => + next.readerFn[T](t) diff --git a/src/main/scala/co.blocke.scalajack/json/readers/MiscReader.scala b/src/main/scala/co.blocke.scalajack/json/readers/MiscReader.scala index cdb5e11d..7e9b51bc 100644 --- a/src/main/scala/co.blocke.scalajack/json/readers/MiscReader.scala +++ b/src/main/scala/co.blocke.scalajack/json/readers/MiscReader.scala @@ -4,10 +4,10 @@ package readers import co.blocke.scala_reflection.reflect.rtypeRefs.* import co.blocke.scala_reflection.rtypes.* -import co.blocke.scala_reflection.{Clazzes, TypedName, RTypeRef} +import co.blocke.scala_reflection.{Clazzes, RTypeRef, TypedName} import scala.quoted.* import scala.collection.mutable.HashMap -import scala.util.{Try, Success, Failure} +import scala.util.{Failure, Success, Try} case class MiscReader(next: ReaderModule, root: ReaderModule) extends ReaderModule: @@ -26,9 +26,9 @@ case class MiscReader(next: ReaderModule, root: ReaderModule) extends ReaderModu val isNullable = Expr(t.optionParamType.isNullable) '{ (j: JsonConfig, p: JsonParser) => p.nullCheck match { - case true if j.forbidNullsInInput => Left(JsonParseError(s"Forbidden 'null' value received at position [${p.getPos}]")) + case true if j.forbidNullsInInput => Left(JsonParseError(p.showError(s"Forbidden 'null' value received at position [${p.getPos}]"))) case true if j.noneAsNull => Right(None.asInstanceOf[T]) - case true if ! $isNullable => Left(JsonParseError(s"Null value given for non-nullable value type at position [${p.getPos}]")) + case true if ! $isNullable => Left(JsonParseError(p.showError(s"Null value given for non-nullable value type at position [${p.getPos}]"))) case true => Right(Some(null).asInstanceOf[T]) case false => $subFn(j, p).map(v => Some(v).asInstanceOf[T]) } @@ -51,7 +51,7 @@ case class MiscReader(next: ReaderModule, root: ReaderModule) extends ReaderModu val cname = $className p.cache.get(cname.asInstanceOf[TypedName]) match case Some(fn) => fn(j, p).asInstanceOf[Either[co.blocke.scalajack.json.ParseError, T]] - case None => Left(ParseError(s"Expected self-ref class $cname but none found in cache at position [${p.getPos}]")) + case None => Left(JsonParseError(p.showError(s"Expected self-ref class $cname but none found in cache at position [${p.getPos}]"))) } case t: TryRef[T] => @@ -64,13 +64,13 @@ case class MiscReader(next: ReaderModule, root: ReaderModule) extends ReaderModu val isNullable = Expr(t.tryRef.isNullable) '{ (j: JsonConfig, p: JsonParser) => p.nullCheck match { - case true if j.forbidNullsInInput => Left(JsonParseError(s"Forbidden 'null' value received at position [${p.getPos}]")) - case true if j.tryFailureHandling == TryOption.AS_NULL => Right(Failure(ParseError("null value received indicating Try failure")).asInstanceOf[T]) - case true if ! $isNullable => Left(JsonParseError(s"Null value given for non-nullable value type at position [${p.getPos}]")) + case true if j.forbidNullsInInput => Left(JsonParseError(p.showError(s"Forbidden 'null' value received at position [${p.getPos}]"))) + case true if j.tryFailureHandling == TryOption.AS_NULL => Right(Failure(JsonParseError(p.showError("null value received indicating Try failure"))).asInstanceOf[T]) + case true if ! $isNullable => Left(JsonParseError(p.showError(s"Null value given for non-nullable value type at position [${p.getPos}]"))) case true => Right(Success(null).asInstanceOf[T]) case false => $subFn(j, p).map(v => Success(v).asInstanceOf[T]) } } - - case t => - next.readerFn[T](t) \ No newline at end of file + + case t => + next.readerFn[T](t) diff --git a/src/main/scala/co.blocke.scalajack/json/readers/PrimitiveReader.scala b/src/main/scala/co.blocke.scalajack/json/readers/PrimitiveReader.scala index 0963f1c6..a00cc8d2 100644 --- a/src/main/scala/co.blocke.scalajack/json/readers/PrimitiveReader.scala +++ b/src/main/scala/co.blocke.scalajack/json/readers/PrimitiveReader.scala @@ -21,7 +21,33 @@ case class PrimitiveReader(next: ReaderModule, root: ReaderModule) extends Reade import quotes.reflect.* ref match - case t: PrimitiveRef[?] if t.name == BOOLEAN_CLASS => + + // + // Scala Primitives + // + case t: PrimitiveRef[T] if t.name == BIG_DECIMAL_CLASS => + if isMapKey then + '{ (j: JsonConfig, p: JsonParser) => + (for { + _ <- p.expectQuote + v <- p.expectBigDouble(j, p).map(_.asInstanceOf[T]) + _ <- p.expectQuote + } yield v) + } + else '{ (j: JsonConfig, p: JsonParser) => p.expectBigDouble(j, p).map(_.asInstanceOf[T]) } + + case t: PrimitiveRef[T] if t.name == BIG_INT_CLASS => + if isMapKey then + '{ (j: JsonConfig, p: JsonParser) => + (for { + _ <- p.expectQuote + v <- p.expectBigLong(j, p).map(_.asInstanceOf[T]) + _ <- p.expectQuote + } yield v) + } + else '{ (j: JsonConfig, p: JsonParser) => p.expectBigLong(j, p).map(_.asInstanceOf[T]) } + + case t: PrimitiveRef[T] if t.name == BOOLEAN_CLASS => if isMapKey then '{ (j: JsonConfig, p: JsonParser) => (for { @@ -31,7 +57,8 @@ case class PrimitiveReader(next: ReaderModule, root: ReaderModule) extends Reade } yield v) } else '{ (j: JsonConfig, p: JsonParser) => p.expectBoolean(j, p).map(_.asInstanceOf[T]) } - case t: PrimitiveRef[?] if t.name == BYTE_CLASS => + + case t: PrimitiveRef[T] if t.name == BYTE_CLASS => if isMapKey then '{ (j: JsonConfig, p: JsonParser) => (for { @@ -41,16 +68,18 @@ case class PrimitiveReader(next: ReaderModule, root: ReaderModule) extends Reade } yield v) } else '{ (j: JsonConfig, p: JsonParser) => p.expectLong(j, p).map(_.toByte.asInstanceOf[T]) } - case t: PrimitiveRef[?] if t.name == CHAR_CLASS => + + case t: PrimitiveRef[T] if t.name == CHAR_CLASS => '{ (j: JsonConfig, p: JsonParser) => p.expectString(j, p) .flatMap(s => s.toArray.headOption match case Some(c) => Right(c.asInstanceOf[T]) - case None => Left(JsonParseError(s"Cannot convert value '$s' into a Char.")) + case None => Left(JsonParseError(p.showError(s"Cannot convert value '$s' into a Char."))) ) } - case t: PrimitiveRef[?] if t.name == DOUBLE_CLASS => + + case t: PrimitiveRef[T] if t.name == DOUBLE_CLASS => if isMapKey then '{ (j: JsonConfig, p: JsonParser) => (for { @@ -60,7 +89,8 @@ case class PrimitiveReader(next: ReaderModule, root: ReaderModule) extends Reade } yield v) } else '{ (j: JsonConfig, p: JsonParser) => p.expectDouble(j, p).map(_.asInstanceOf[T]) } - case t: PrimitiveRef[?] if t.name == FLOAT_CLASS => + + case t: PrimitiveRef[T] if t.name == FLOAT_CLASS => if isMapKey then '{ (j: JsonConfig, p: JsonParser) => (for { @@ -70,7 +100,8 @@ case class PrimitiveReader(next: ReaderModule, root: ReaderModule) extends Reade } yield v) } else '{ (j: JsonConfig, p: JsonParser) => p.expectDouble(j, p).map(_.toFloat.asInstanceOf[T]) } - case t: PrimitiveRef[?] if t.name == INT_CLASS => + + case t: PrimitiveRef[T] if t.name == INT_CLASS => if isMapKey then '{ (j: JsonConfig, p: JsonParser) => (for { @@ -80,7 +111,8 @@ case class PrimitiveReader(next: ReaderModule, root: ReaderModule) extends Reade } yield v) } else '{ (j: JsonConfig, p: JsonParser) => p.expectLong(j, p).map(_.toInt.asInstanceOf[T]) } - case t: PrimitiveRef[?] if t.name == LONG_CLASS => + + case t: PrimitiveRef[T] if t.name == LONG_CLASS => if isMapKey then '{ (j: JsonConfig, p: JsonParser) => (for { @@ -90,7 +122,8 @@ case class PrimitiveReader(next: ReaderModule, root: ReaderModule) extends Reade } yield v) } else '{ (j: JsonConfig, p: JsonParser) => p.expectLong(j, p).map(_.asInstanceOf[T]) } - case t: PrimitiveRef[?] if t.name == SHORT_CLASS => + + case t: PrimitiveRef[T] if t.name == SHORT_CLASS => if isMapKey then '{ (j: JsonConfig, p: JsonParser) => (for { @@ -100,8 +133,111 @@ case class PrimitiveReader(next: ReaderModule, root: ReaderModule) extends Reade } yield v) } else '{ (j: JsonConfig, p: JsonParser) => p.expectLong(j, p).map(_.toShort.asInstanceOf[T]) } + case t: PrimitiveRef[T] if t.name == STRING_CLASS => '{ (j: JsonConfig, p: JsonParser) => p.expectString(j, p).map(_.asInstanceOf[T]) } - case t => - next.readerFn[T](t) \ No newline at end of file + // + // Java Primitives + // + case t: PrimitiveRef[T] if t.name == JBOOLEAN_CLASS => + if isMapKey then + '{ (j: JsonConfig, p: JsonParser) => + (for { + _ <- p.expectQuote + v <- p.expectBoolean(j, p).map(b => java.lang.Boolean(b).asInstanceOf[T]) + _ <- p.expectQuote + } yield v) + } + else '{ (j: JsonConfig, p: JsonParser) => p.expectBoolean(j, p).map(_.asInstanceOf[T]) } + + case t: PrimitiveRef[T] if t.name == JBYTE_CLASS => + if isMapKey then + '{ (j: JsonConfig, p: JsonParser) => + (for { + _ <- p.expectQuote + v <- p.expectLong(j, p).map(b => java.lang.Byte(b.asInstanceOf[Byte]).asInstanceOf[T]) + _ <- p.expectQuote + } yield v) + } + else '{ (j: JsonConfig, p: JsonParser) => p.expectLong(j, p).map(_.toByte.asInstanceOf[T]) } + + case t: PrimitiveRef[T] if t.name == JCHARACTER_CLASS => + '{ (j: JsonConfig, p: JsonParser) => + p.expectString(j, p) + .flatMap(s => + s.toArray.headOption match + case Some(c) => Right(java.lang.Character(c).asInstanceOf[T]) + case None => Left(JsonParseError(p.showError(s"Cannot convert value '$s' into a Char."))) + ) + } + + case t: PrimitiveRef[T] if t.name == JDOUBLE_CLASS => + if isMapKey then + '{ (j: JsonConfig, p: JsonParser) => + (for { + _ <- p.expectQuote + v <- p.expectDouble(j, p).map(b => java.lang.Double(b).asInstanceOf[T]) + _ <- p.expectQuote + } yield v) + } + else '{ (j: JsonConfig, p: JsonParser) => p.expectDouble(j, p).map(_.asInstanceOf[T]) } + + case t: PrimitiveRef[T] if t.name == JFLOAT_CLASS => + if isMapKey then + '{ (j: JsonConfig, p: JsonParser) => + (for { + _ <- p.expectQuote + v <- p.expectDouble(j, p).map(b => java.lang.Float(b).asInstanceOf[T]) + _ <- p.expectQuote + } yield v) + } + else '{ (j: JsonConfig, p: JsonParser) => p.expectDouble(j, p).map(_.toFloat.asInstanceOf[T]) } + + case t: PrimitiveRef[T] if t.name == JINTEGER_CLASS => + if isMapKey then + '{ (j: JsonConfig, p: JsonParser) => + (for { + _ <- p.expectQuote + v <- p.expectLong(j, p).map(b => java.lang.Integer(b.toInt).asInstanceOf[T]) + _ <- p.expectQuote + } yield v) + } + else '{ (j: JsonConfig, p: JsonParser) => p.expectLong(j, p).map(_.toInt.asInstanceOf[T]) } + + case t: PrimitiveRef[T] if t.name == JLONG_CLASS => + if isMapKey then + '{ (j: JsonConfig, p: JsonParser) => + (for { + _ <- p.expectQuote + v <- p.expectLong(j, p).map(b => java.lang.Long(b).asInstanceOf[T]) + _ <- p.expectQuote + } yield v) + } + else '{ (j: JsonConfig, p: JsonParser) => p.expectLong(j, p).map(_.asInstanceOf[T]) } + + case t: PrimitiveRef[T] if t.name == JSHORT_CLASS => + if isMapKey then + '{ (j: JsonConfig, p: JsonParser) => + (for { + _ <- p.expectQuote + v <- p.expectLong(j, p).map(b => java.lang.Short(b.toShort).asInstanceOf[T]) + _ <- p.expectQuote + } yield v) + } + else '{ (j: JsonConfig, p: JsonParser) => p.expectLong(j, p).map(_.toShort.asInstanceOf[T]) } + + case t: PrimitiveRef[T] if t.name == UUID_CLASS => + '{ (j: JsonConfig, p: JsonParser) => + p.expectString(j, p) + .flatMap(u => + if u == null then Right(null.asInstanceOf[T]) + else + scala.util.Try(java.util.UUID.fromString(u)) match + case Success(uuid) => Right(uuid.asInstanceOf[T]) + case Failure(_) => Left(JsonParseError(p.showError(s"Unable to marshal UUID from value '$u'."))) + ) + } + + case t => + next.readerFn[T](t) diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index e85ace0f..4a459d71 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -2,6 +2,7 @@ package co.blocke.scalajack package run import co.blocke.scala_reflection.* +import scala.jdk.CollectionConverters.* enum Color: case Red, Green, Blue @@ -21,15 +22,13 @@ object RunMe extends App: // .copy(enumsAsIds = '*') try - val x = Blah("foo", (5, true, "wow")) + val x = Blah("foo", WeekDay.Fri) val js = ScalaJack.write(x) println(js) + val inst = ScalaJack.read[Blah](js) println(inst) catch { - case t: Throwable => - println(s"BOOM ($t): " + t.getMessage) - println(t.printStackTrace) - println(t.getClass.getName) + case t: Throwable => println(s"BOOM ($t): " + t.getMessage) } diff --git a/src/main/scala/co.blocke.scalajack/run/Sample.scala b/src/main/scala/co.blocke.scalajack/run/Sample.scala index 2c819ef8..b0519080 100644 --- a/src/main/scala/co.blocke.scalajack/run/Sample.scala +++ b/src/main/scala/co.blocke.scalajack/run/Sample.scala @@ -23,9 +23,15 @@ import scala.collection.immutable.* enum Vehicle: case Car, Bus, Train +object WeekDay extends Enumeration { + type WeekDay = Value + val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value +} +import WeekDay.* + case class Simple(a: Int, b: Boolean, c: Option[Simple], z: Int = 5) -case class Blah(msg: String, stuff: (Int, Boolean, String)) +case class Blah(msg: String, stuff: WeekDay) object Talk: def say(s: String): String = s"Say $s!" diff --git a/src/test/scala/co.blocke.scalajack/JsonDiff.scala b/src/test/scala/co.blocke.scalajack/JsonDiff.scala new file mode 100644 index 00000000..acafcefa --- /dev/null +++ b/src/test/scala/co.blocke.scalajack/JsonDiff.scala @@ -0,0 +1,58 @@ +package co.blocke.scalajack +package json + +import org.json4s.JsonAST.{ JNothing, JObject, JValue } + +object JsonDiff { + + def compare( + left: JValue, + right: JValue, + leftLabel: String = "left", + rightLabel: String = "right"): Seq[JsonDiff] = { + (left, right) match { + case (JObject(leftFields), JObject(rightFields)) => + val allFieldNames = + (leftFields.map(_._1) ++ rightFields.map(_._1)).distinct + allFieldNames.sorted flatMap { fieldName => + val leftFieldValue = leftFields + .collectFirst({ case (`fieldName`, fieldValue) => fieldValue }) + .getOrElse(JNothing) + val rightFieldValue = rightFields + .collectFirst({ case (`fieldName`, fieldValue) => fieldValue }) + .getOrElse(JNothing) + compare(leftFieldValue, rightFieldValue, leftLabel, rightLabel) + } + + // ---- Not used/needed at present, and I have questions about the correct behavior here. Exactly how do you + // "diff" two arrays (not necessarily homogeneous typed)? + // + // case (JArray(leftElements), JArray(rightElements)) => + // (0 until (leftElements.size max rightElements.size)) flatMap { elementIndex => + // val leftElement = leftElements.applyOrElse(elementIndex, (_: Int) => JNothing) + // val rightElement = rightElements.applyOrElse(elementIndex, (_: Int) => JNothing) + // compare(path \ elementIndex, leftElement, rightElement, leftLabel, rightLabel) + // } + + case _ => + if (left == right) { + Seq.empty + } else { + val outerLeft = left + val outerRight = right + Seq(new JsonDiff { + override val left: JValue = outerLeft + override val right: JValue = outerRight + override def toString: String = + s"JsonDiff($leftLabel: $left, $rightLabel: $right)" + }) + } + } + } + +} + +trait JsonDiff { + val left: JValue + val right: JValue +} diff --git a/src/test/scala/co.blocke.scalajack/JsonMatcher.scala b/src/test/scala/co.blocke.scalajack/JsonMatcher.scala new file mode 100644 index 00000000..acc73607 --- /dev/null +++ b/src/test/scala/co.blocke.scalajack/JsonMatcher.scala @@ -0,0 +1,61 @@ +package co.blocke.scalajack +package json + +import org.json4s.JsonAST.JValue +import org.json4s.native.JsonMethods._ + +import munit.internal.MacroCompat +import munit.{Location, Compare} +import munit.internal.console.StackTraces +import munit.internal.difflib.ComparisonFailExceptionHandler + +// object JsonMatcher { + +// def jsonMatches( expected: String, actual: String ): Boolean = +// val diffs = JsonDiff.compare( +// parseJValue(expected), +// parseJValue(actual), +// "expected", +// "actual" +// ) +// diffs.isEmpty + +// implicit def parseJValue(string: String): JValue = parse(string) +// } + + +object BlockeUtil extends BlockeUtil +trait BlockeUtil extends MacroCompat.CompileErrorMacro with munit.Assertions: + + // val munitLines = new Lines + + // def munitAnsiColors: Boolean = true + + implicit def parseJValue(string: String): JValue = parse(string) + + /** + * Asserts that two JSON strings are equal according to the `Compare[A, B]` type-class. + * + * By default, uses `==` to compare values. + * + * JSON is unorderd, so two JSON strings are equal if they contain the same elements, in any order + */ + def jsonMatches( + obtained: String, + expected: String, + clue: => Any = "values are not the same" + )(implicit loc: Location, compare: Compare[String, String]): Unit = { + StackTraces.dropInside { + val areEqual = + val diffs = JsonDiff.compare( + parseJValue(obtained), + parseJValue(expected), + "obtained", + "expected" + ) + diffs.isEmpty + + if (!areEqual) + compare.failEqualsComparison(obtained, expected, clue, loc, this) + } + } \ No newline at end of file diff --git a/src/test/scala/co.blocke.scalajack/TestUtil.scala b/src/test/scala/co.blocke.scalajack/TestUtil.scala new file mode 100644 index 00000000..c8365bdf --- /dev/null +++ b/src/test/scala/co.blocke.scalajack/TestUtil.scala @@ -0,0 +1,42 @@ +package co.blocke.scalajack + +import munit.internal.console + +object TestUtil { + + inline def describe(message: String, color: String = Console.MAGENTA): Unit = println(s"$color$message${Console.RESET}") + inline def pending = describe(" << Test Pending (below) >>", Console.YELLOW) + + def hexStringToByteArray(s: String): Array[Byte] = { + val len = s.length + val data = new Array[Byte](len / 2) + var i = 0 + while ({ + i < len + }) { + data(i / 2) = ((Character.digit(s.charAt(i), 16) << 4) + Character.digit( + s.charAt(i + 1), + 16 + )).toByte + + i += 2 + } + data + } + + // Utility to generate test code quickly + def showException(label: String, fnStr: String, fn: () => Any) = + try { + fn() + } catch { + case x: IndexOutOfBoundsException => throw x + case t: Throwable => + if (!t.getMessage.contains("\n")) + throw t + val msg = "\"\"\"" + t.getMessage().replace("\n", "\n |") + "\"\"\"" + println( + label + " >> " + t.getClass.getName + "\n-----------------------\n" + + s"val msg = $msg.stripMargin\nthe[${t.getClass.getName}] thrownBy $fnStr should have message msg\n" + ) + } +} \ No newline at end of file diff --git a/src/test/scala/co.blocke.scalajack/json/primitives/Model.scala b/src/test/scala/co.blocke.scalajack/json/primitives/Model.scala new file mode 100644 index 00000000..451c9854 --- /dev/null +++ b/src/test/scala/co.blocke.scalajack/json/primitives/Model.scala @@ -0,0 +1,197 @@ +package co.blocke.scalajack +package json.primitives + +import java.util.UUID +import java.lang.{ + Boolean => JBoolean, + Byte => JByte, + Character => JChar, + Double => JDouble, + Float => JFloat, + Integer => JInt, + Long => JLong, + Number => JNumber, + Short => JShort +} +import java.math.{ BigDecimal => JBigDecimal, BigInteger => JBigInteger } +import java.time._ +import scala.math._ + +// === Scala +case class SampleBigDecimal( + bd1: BigDecimal, + bd2: BigDecimal, + bd3: BigDecimal, + bd4: BigDecimal, + bd5: BigDecimal, + bd6: BigDecimal) +case class SampleBigInt(bi1: BigInt, bi2: BigInt, bi3: BigInt, bi4: BigInt) +case class SampleBinary(b1: Array[Byte], b2: Array[Byte]) +case class SampleBoolean(bool1: Boolean, bool2: Boolean) +case class SampleByte(b1: Byte, b2: Byte, b3: Byte, b4: Byte) +case class SampleChar(c1: Char, c2: Char, c3: Char) +case class SampleDouble(d1: Double, d2: Double, d3: Double, d4: Double) + +object Size extends Enumeration { + val Small, Medium, Large = Value +} +object SizeWithType extends Enumeration { + type SizeWithType = Value + val Little, Grand = Value +} +import SizeWithType._ +case class SampleEnum( + e1: Size.Value, + e2: Size.Value, + e3: Size.Value, + e4: Size.Value, + e5: Size.Value, + e6: SizeWithType) + +enum Color { + case Red, Blue, Green +} +case class TVColors( color1: Color, color2: Color ) + +sealed trait Flavor +case object Vanilla extends Flavor +case object Chocolate extends Flavor +case object Bourbon extends Flavor + +sealed trait Vehicle +case class Truck(numberOfWheels: Int) extends Vehicle +case class Car(numberOfWheels: Int, color: String) extends Vehicle +case class Plane(numberOfEngines: Int) extends Vehicle + +case class Ride( wheels: Vehicle ) +case class Favorite( flavor: Flavor ) + +case class SampleFloat(f1: Float, f2: Float, f3: Float, f4: Float) +case class SampleInt(i1: Int, i2: Int, i3: Int, i4: Int) +case class SampleLong(l1: Long, l2: Long, l3: Long, l4: Long) +case class SampleShort(s1: Short, s2: Short, s3: Short, s4: Short) +case class SampleString(s1: String, s2: String, s3: String) + +// === Java +case class SampleJBigDecimal( + bd1: JBigDecimal, + bd2: JBigDecimal, + bd3: JBigDecimal, + bd4: JBigDecimal, + bd5: JBigDecimal) +case class SampleJBigInteger( + bi1: JBigInteger, + bi2: JBigInteger, + bi3: JBigInteger, + bi4: JBigInteger, + bi5: JBigInteger, + bi6: JBigInteger, + bi7: JBigInteger) +case class SampleJBoolean( + bool1: JBoolean, + bool2: JBoolean, + bool3: JBoolean, + bool4: JBoolean, + bool5: JBoolean) +case class SampleJByte(b1: JByte, b2: JByte, b3: JByte, b4: JByte, b5: JByte) +case class SampleJChar(c1: JChar, c2: JChar, c3: JChar) +case class SampleJDouble( + d1: JDouble, + d2: JDouble, + d3: JDouble, + d4: JDouble, + d5: JDouble) +case class SampleJFloat( + f1: JFloat, + f2: JFloat, + f3: JFloat, + f4: JFloat, + f5: JFloat) +case class SampleJInt(i1: JInt, i2: JInt, i3: JInt, i4: JInt, i5: JInt) +case class SampleJLong(l1: JLong, l2: JLong, l3: JLong, l4: JLong, l5: JLong) +case class SampleJNumber( + n1: JNumber, + n2: JNumber, + n3: JNumber, + n4: JNumber, + n5: JNumber, + n6: JNumber, + n7: JNumber, + n8: JNumber, + n9: JNumber, + n10: JNumber, + n11: JNumber, + n12: JNumber, + n13: JNumber, + n14: JNumber, + n15: JNumber, + n16: JNumber, + n17: JNumber) +case class SampleJShort( + s1: JShort, + s2: JShort, + s3: JShort, + s4: JShort, + s5: JShort) +case class SampleUUID(u1: UUID, u2: UUID) + +// === Java Time +case class SampleDuration(d1: Duration, d2: Duration, d3: Duration) +case class SampleInstant( + i1: Instant, + i2: Instant, + i3: Instant, + i4: Instant, + i5: Instant) +case class SampleLocalDateTime( + d1: LocalDateTime, + d2: LocalDateTime, + d3: LocalDateTime, + d4: LocalDateTime) +case class SampleLocalDate( + d1: LocalDate, + d2: LocalDate, + d3: LocalDate, + d4: LocalDate) +case class SampleLocalTime( + d1: LocalTime, + d2: LocalTime, + d3: LocalTime, + d4: LocalTime, + d5: LocalTime, + d6: LocalTime) +case class SampleOffsetDateTime( + o1: OffsetDateTime, + o2: OffsetDateTime, + o3: OffsetDateTime, + o4: OffsetDateTime) +case class SampleOffsetTime( + o1: OffsetTime, + o2: OffsetTime, + o3: OffsetTime, + o4: OffsetTime) +case class SamplePeriod(p1: Period, p2: Period, p3: Period) +case class SampleZonedDateTime(o1: ZonedDateTime, o2: ZonedDateTime) + +// === Any primitives +case class AnyShell(a: Any) + +// === Value Classes +case class VCBigDecimal(vc: BigDecimal) extends AnyVal +case class VCBigInt(vc: BigInt) extends AnyVal +case class VCBoolean(vc: Boolean) extends AnyVal +case class VCByte(vc: Byte) extends AnyVal +case class VCChar(vc: Char) extends AnyVal +case class VCDouble(vc: Double) extends AnyVal +case class VCEnum(vc: Color) extends AnyVal +case class VCEnumeration(vc: Size.Value) extends AnyVal +case class VCFloat(vc: Float) extends AnyVal +case class VCInt(vc: Int) extends AnyVal +case class VCLong(vc: Long) extends AnyVal +case class VCShort(vc: Short) extends AnyVal +case class VCString(vc: String) extends AnyVal +case class VCUUID(vc: UUID) extends AnyVal +case class VCNumber(vc: Number) extends AnyVal + +// === Permissives test +case class Holder[T](value: T) \ No newline at end of file diff --git a/src/test/scala/co.blocke.scalajack/json/primitives/ScalaPrim.scala b/src/test/scala/co.blocke.scalajack/json/primitives/ScalaPrim.scala new file mode 100644 index 00000000..b33705c0 --- /dev/null +++ b/src/test/scala/co.blocke.scalajack/json/primitives/ScalaPrim.scala @@ -0,0 +1,329 @@ +package co.blocke.scalajack +package json +package primitives + +import co.blocke.scala_reflection._ +import scala.math.BigDecimal +import java.util.UUID +import TestUtil._ +import munit._ +import munit.internal.console + +class ScalaPrim() extends FunSuite with BlockeUtil: + + test("Introduction") { + describe("---------------------------\n: Scala Primitive Tests :\n---------------------------", Console.YELLOW) + describe("+++ Positive Tests +++") + } + + test("BigDecimal must work") { + val inst = SampleBigDecimal( + BigDecimal(123L), + BigDecimal(1.23), + BigDecimal(0), + BigDecimal("123.456"), + BigDecimal( + "0.1499999999999999944488848768742172978818416595458984375" + ), + null + ) + + val js = ScalaJack.write(inst) + assertEquals(js, + """{"bd1":123,"bd2":1.23,"bd3":0,"bd4":123.456,"bd5":0.1499999999999999944488848768742172978818416595458984375,"bd6":null}""" + ) + assertEquals(inst, ScalaJack.read[SampleBigDecimal](js)) + } + + test("BigInt must work") { + val inst = SampleBigInt( + BigInt("-90182736451928374653345"), + BigInt("90182736451928374653345"), + BigInt(0), + null + ) + val js = ScalaJack.write(inst) + assertEquals(js, + """{"bi1":-90182736451928374653345,"bi2":90182736451928374653345,"bi3":0,"bi4":null}""" + ) + assertEquals(inst, ScalaJack.read[SampleBigInt](js)) + } + + test("Binary must work") { + val inst = SampleBinary( + null, + hexStringToByteArray("e04fd020ea3a6910a2d808002b30309d") + ) + val js = ScalaJack.write(inst) + val inst2 = ScalaJack.read[SampleBinary](js) + assert(null == inst2.b1) + assertEquals(inst.b2.toList, inst2.b2.toList) + } + + test("Boolean must work (not nullable)") { + val inst = SampleBoolean(bool1 = true, bool2 = false) + val js = ScalaJack.write(inst) + jsonMatches(js, """{"bool1":true,"bool2":false}""") + assertEquals(inst, ScalaJack.read[SampleBoolean](js)) + } + + test("Byte must work (not nullable)") { + val inst = SampleByte(Byte.MaxValue, Byte.MinValue, 0, 64) + val js = ScalaJack.write(inst) + jsonMatches(js, """{"b1":127,"b2":-128,"b3":0,"b4":64}""") + assertEquals(inst, ScalaJack.read[SampleByte](js)) + } + + test("Char must work (not nullable)") { + val inst = SampleChar(Char.MaxValue, 'Z', '\u20A0') + val js = ScalaJack.write(inst) + jsonMatches(js,"""{"c1":"\""" + """uffff","c2":"Z","c3":"\""" + """u20a0"}""") + assertEquals(inst, ScalaJack.read[SampleChar](js)) + } + + test("Double must work (not nullable)") { + val inst = + SampleDouble(Double.MaxValue, Double.MinValue, 0.0, -123.4567) + val js = ScalaJack.write(inst) + jsonMatches(js, + """{"d1":1.7976931348623157E308,"d2":-1.7976931348623157E308,"d3":0.0,"d4":-123.4567}""") + assertEquals(inst, ScalaJack.read[SampleDouble](js)) + } + + test("Float must work") { + val inst = SampleFloat(Float.MaxValue, Float.MinValue, 0.0F, -123.4567F) + val js = ScalaJack.write(inst) + jsonMatches(js, + """{"f1":3.4028235E38,"f2":-3.4028235E38,"f3":0.0,"f4":-123.4567}""") + assertEquals(inst, ScalaJack.read[SampleFloat](js)) + } + + test("Int must work (not nullable)") { + val inst = SampleInt(Int.MaxValue, Int.MinValue, 0, 123) + val js = ScalaJack.write(inst) + jsonMatches(js, """{"i1":2147483647,"i2":-2147483648,"i3":0,"i4":123}""") + assertEquals(inst, ScalaJack.read[SampleInt](js)) + } + + test("Long must work (not nullable)") { + val inst = SampleLong(Long.MaxValue, Long.MinValue, 0L, 123L) + val js = ScalaJack.write(inst) + jsonMatches(js, + """{"l1":9223372036854775807,"l2":-9223372036854775808,"l3":0,"l4":123}""") + assertEquals(inst, ScalaJack.read[SampleLong](js)) + } + + test("Short must work (not nullable)") { + val inst = SampleShort(Short.MaxValue, Short.MinValue, 0, 123) + val js = ScalaJack.write(inst) + jsonMatches(js,"""{"s1":32767,"s2":-32768,"s3":0,"s4":123}""") + assertEquals(inst, ScalaJack.read[SampleShort](js)) + } + + test("String must work") { + val inst = SampleString("something\b\n\f\r\t☆", "", null) + val js = ScalaJack.write(inst) + // The weird '+' here is to break up the unicode so it won't be interpreted and wreck the test. + jsonMatches(js, + """{"s1":"something\b\n\f\r\t\""" + """u2606","s2":"","s3":null}""") + assertEquals(inst, ScalaJack.read[SampleString](js)) + } + + test("UUID must work") { + val inst = SampleUUID( + null, + UUID.fromString("580afe0d-81c0-458f-9e09-4486c7af0fe9") + ) + val js = ScalaJack.write(inst) + jsonMatches(js, + """{"u1":null,"u2":"580afe0d-81c0-458f-9e09-4486c7af0fe9"}""") + assertEquals(inst, ScalaJack.read[SampleUUID](js)) + } + + + //-------------------------------------------------------- + +/* + test("BigDecimal must break") { + describe("--- Negative Tests ---") + val js = + """{"bd1":123,"bd2":1.23,"bd3":0,"bd4":123.456,"bd5":"0.1499999999999999944488848768742172978818416595458984375","bd6":null}""" + val msg = + """Expected a Number here + |{"bd1":123,"bd2":1.23,"bd3":0,"bd4":123.456,"bd5":"0.149999999999999994448884... + |--------------------------------------------------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ + ScalaJack.read[SampleBigDecimal](js) + } + } + + test("BigInt must break") { + val js = + """{"bi1":"-90182736451928374653345","bi2":90182736451928374653345,"bi3":0,"bi4":null}""" + val msg = + """Expected a Number here + |{"bi1":"-90182736451928374653345","bi2":90182736451928374653345,"bi3":0,"bi4"... + |-------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ + ScalaJack.read[SampleBigInt](js) + } + } + + test("Boolean must break") { + val js = """{"bool1":true,"bool2":"false"}""" + val msg = """Expected a Boolean here + |{"bool1":true,"bool2":"false"} + |----------------------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ + ScalaJack.read[SampleBoolean](js) + } + val js2 = """{"bool1":true,"bool2":123}""" + val msg2 = """Expected a Boolean here + |{"bool1":true,"bool2":123} + |----------------------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ + ScalaJack.read[SampleBoolean](js2) + } + val js3 = """{"bool1":true,"bool2":null}""" + val msg3 = """Expected a Boolean here + |{"bool1":true,"bool2":null} + |----------------------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg3){ + ScalaJack.read[SampleBoolean](js3) + } + } + + test("Byte must break") { + val js = """{"b1":true,"b2":-128,"b3":0,"b4":64}""" + val msg = """Expected a Number here + |{"b1":true,"b2":-128,"b3":0,"b4":64} + |------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ + ScalaJack.read[SampleByte](js) + } + val js2 = """{"b1":12,"b2":-128,"b3":0,"b4":null}""" + val msg2 = """Expected a Number here + |{"b1":12,"b2":-128,"b3":0,"b4":null} + |-------------------------------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ + ScalaJack.read[SampleByte](js2) + } + } + + test("Char must break") { + val js = """{"c1":null,"c2":"Y","c3":"Z"}""" + val msg = """A Char typed value cannot be null + |{"c1":null,"c2":"Y","c3":"Z"} + |---------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ + ScalaJack.read[SampleChar](js) + } + val js2 = """{"c1":"","c2":"Y","c3":"Z"}""" + val msg2 = """Tried to read a Char but empty string found + |{"c1":"","c2":"Y","c3":"Z"} + |-------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ + ScalaJack.read[SampleChar](js2) + } + } + + test("Double must break") { + val js = + """{"d1":1.79769313486E23157E308,"d2":-1.7976931348623157E308,"d3":0.0,"d4":-123.4567}""" + val msg = + """Cannot parse an Double from value + |{"d1":1.79769313486E23157E308,"d2":-1.7976931348623157E308,"d3":0.0,"d4":-123... + |----------------------------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ + ScalaJack.read[SampleDouble](js) + } + } + + test("Float must break") { + val js = + """{"f1":3.4028235E38,"f2":"-3.4028235E38","f3":0.0,"f4":-123.4567}""" + val msg = + """Expected a Number here + |{"f1":3.4028235E38,"f2":"-3.4028235E38","f3":0.0,"f4":-123.4567} + |------------------------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ + ScalaJack.read[SampleFloat](js) + } + } + + test("Int must break") { + val js = """{"i1":2147483647,"i2":-2147483648,"i3":"0","i4":123}""" + val msg = """Expected a Number here + |{"i1":2147483647,"i2":-2147483648,"i3":"0","i4":123} + |---------------------------------------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ + ScalaJack.read[SampleInt](js) + } + val js2 = """{"i1":2147483647,"i2":-2147483648,"i3":2.3,"i4":123}""" + val msg2 = """Cannot parse an Int from value + |{"i1":2147483647,"i2":-2147483648,"i3":2.3,"i4":123} + |-----------------------------------------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ + ScalaJack.read[SampleInt](js2) + } + } + + test("Long must break") { + val js = + """{"l1":9223372036854775807,"l2":-9223372036854775808,"l3":true,"l4":123}""" + val msg = + """Expected a Number here + |...23372036854775807,"l2":-9223372036854775808,"l3":true,"l4":123} + |----------------------------------------------------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ + ScalaJack.read[SampleLong](js) + } + val js2 = + """{"l1":9223372036854775807,"l2":-9223372036854775808,"l3":0.3,"l4":123}""" + val msg2 = + """Cannot parse an Long from value + |...372036854775807,"l2":-9223372036854775808,"l3":0.3,"l4":123} + |----------------------------------------------------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ + ScalaJack.read[SampleLong](js2) + } + } + + test("Short must break") { + val js = """{"s1":32767,"s2":true,"s3":0,"s4":123}""" + val msg = """Expected a Number here + |{"s1":32767,"s2":true,"s3":0,"s4":123} + |-----------------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ + ScalaJack.read[SampleShort](js) + } + val js2 = """{"s1":32767,"s2":3.4,"s3":0,"s4":123}""" + val msg2 = """Cannot parse an Short from value + |{"s1":32767,"s2":3.4,"s3":0,"s4":123} + |-------------------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ + ScalaJack.read[SampleShort](js2) + } + } + + test("String must break") { + val js = """{"s1":"something","s2":-19,"s3":null}""" + val msg = """Expected a String here + |{"s1":"something","s2":-19,"s3":null} + |-----------------------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ + ScalaJack.read[SampleString](js) + } + } + + test("UUID must break") { + val js = + """{"u1":"bogus","u2":"580afe0d-81c0-458f-9e09-4486c7af0fe9"}""" + val msg = """Failed to create UUID value from parsed text bogus + |{"u1":"bogus","u2":"580afe0d-81c0-458f-9e09-4486c7af0fe9"} + |------------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ + ScalaJack.read[SampleUUID](js) + } + } +*/ \ No newline at end of file From ee2cd1867d9176a412375d0d86f7a2c0eae2709f Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Sun, 5 Nov 2023 10:20:58 -0600 Subject: [PATCH 15/65] New faster writer --- benchmark/README.md | 46 ++ benchmark/build.sbt | 59 ++ benchmark/project/build.properties | 1 + benchmark/project/plugins.sbt | 1 + .../src/main/scala/co.blocke/Argonaut.scala | 26 + .../src/main/scala/co.blocke/Benchmark.scala | 96 +++ .../src/main/scala/co.blocke/PlayJson.scala | 46 ++ .../src/main/scala/co.blocke/Record.scala | 86 +++ benchmark/src/main/scala/co.blocke/Run.scala | 8 + .../src/main/scala/co.blocke/ZIOJson.scala | 20 + build.sbt | 14 +- .../scala/co.blocke.scalajack/ScalaJack.scala | 39 +- .../co.blocke.scalajack/json/JsonConfig.scala | 1 + .../co.blocke.scalajack/json/JsonParser.scala | 79 ++- .../co.blocke.scalajack/json/JsonWriter.scala | 484 +++++---------- .../json/JsonWriter.scalax | 394 ++++++++++++ .../json/JsonWriter2.scalax | 251 ++++++++ .../json/JsonWriterRT.scala | 160 +++++ .../json/ReflectUtil.scala | 52 -- .../json/ReflectUtil.scalax | 59 ++ .../json/readers/PrimitiveReader.scala | 10 +- .../scala/co.blocke.scalajack/run/Play.scala | 41 +- .../co.blocke.scalajack/run/Sample.scala | 73 ++- .../scala/co.blocke.scalajack/JsonDiff.scala | 15 +- .../co.blocke.scalajack/JsonMatcher.scala | 79 +-- .../scala/co.blocke.scalajack/TestUtil.scala | 23 +- .../json/primitives/Model.scala | 148 +---- .../json/primitives/ScalaPrim.scala | 559 +++++++++--------- 28 files changed, 1915 insertions(+), 955 deletions(-) create mode 100644 benchmark/README.md create mode 100644 benchmark/build.sbt create mode 100644 benchmark/project/build.properties create mode 100644 benchmark/project/plugins.sbt create mode 100644 benchmark/src/main/scala/co.blocke/Argonaut.scala create mode 100644 benchmark/src/main/scala/co.blocke/Benchmark.scala create mode 100644 benchmark/src/main/scala/co.blocke/PlayJson.scala create mode 100644 benchmark/src/main/scala/co.blocke/Record.scala create mode 100644 benchmark/src/main/scala/co.blocke/Run.scala create mode 100644 benchmark/src/main/scala/co.blocke/ZIOJson.scala create mode 100644 src/main/scala/co.blocke.scalajack/json/JsonWriter.scalax create mode 100644 src/main/scala/co.blocke.scalajack/json/JsonWriter2.scalax create mode 100644 src/main/scala/co.blocke.scalajack/json/JsonWriterRT.scala delete mode 100644 src/main/scala/co.blocke.scalajack/json/ReflectUtil.scala create mode 100644 src/main/scala/co.blocke.scalajack/json/ReflectUtil.scalax diff --git a/benchmark/README.md b/benchmark/README.md new file mode 100644 index 00000000..d8cb4251 --- /dev/null +++ b/benchmark/README.md @@ -0,0 +1,46 @@ +# Performance + +JSON serialization enchmarks I found in github often measured (IMO) silly things like how fast a parser +could handle a small list of Int. For this benchmark I used a more substantial model + JSON. It's still +not large by any measure, but it does have some nested objects and collections that make it a more +realistic test. + +The test is run via jmh, a popular benchmarking tool. The JVM is stock--not tuned to within an inch +of its life, again to be a more realistic use case. + +Run benchmark from the ScalaJack/benchmark directory (not the main ScalaJack project directory): +``` +sbt "jmh:run -i 10 -wi 10 -f 2 -t 1 co.blocke.WritingBenchmark" +``` + +## Writing Performance: + +| Benchmark | Mode | Cnt | Score | Error | Units | +|------------------|-------|-----|-------------|--------------|-------| +| Hand-Tooled | thrpt | 20 | 2575393.513 | ± 178731.952 | ops/s | +| Circe | thrpt | 20 | 1939339.085 | ± 6279.547 | ops/s | +| ScalaJack 8 | thrpt | 20 | 1703256.521 | ± 12260.518 | ops/s | +| ZIO JSON | thrpt | 20 | 818228.736 | ± 3070.298 | ops/s | +| Argonaut | thrpt | 20 | 716228.404 | ± 6241.145 | ops/s | +| Play JSON | thrpt | 20 | 438538.475 | ± 16319.198 | ops/s | +| ScalaJack 7 | thrpt | 20 | 106292.338 | ± 330.111 | ops/s | + +### Writing Interpretation + +The Hand-Tooled case is straight, manual JSON creation in code. I included it to show a likely +upper-bound on achievable performance. No parser, with whatever logic it must do, can be faster +than hand-tooled code that is hard-wired in its output and requires zero logic. + +We see that both Circe and ScalaJack are very close in performance--close to each other and +shockingly close to hand-tooled code. + +Circe is the gold standard for JSON serializers due to its many features, excellent performance, and +widespread adoption. The one cost Circe imposes is the same one virtually all other serializers +require: that boilerplate be provided to define encoders/decoders to aid the serializion. Circe's +boilerplate is actually not terrible. Others require a fair bit of extra code per class serialized. + +ScalaJack's focus is first and foremost to be frictionless--no drama to the user. The very slight +difference in maximal performance is a worthy expense--its still blazing fast, and positioned well +vs the pack. ScalaJack requires zero boilerplate--you can throw any Scala object at it with no +pre-preparation and it will serialize it. You'll notice the order-of-magnitude improvement ScalaJack 8 +has over ScalaJack 7, due to moving everything possible into compile-time macros for speed. diff --git a/benchmark/build.sbt b/benchmark/build.sbt new file mode 100644 index 00000000..2b1ad05b --- /dev/null +++ b/benchmark/build.sbt @@ -0,0 +1,59 @@ +ThisBuild / organization := "co.blocke" + +val compilerOptions = Seq( + "-deprecation", + "-encoding", + "UTF-8", + "-feature", + "-language:existentials", + "-language:higherKinds", + "-unchecked", + "-Ywarn-dead-code", + "-Ywarn-numeric-widen", + "-Xfuture" +) + +val circeVersion = "0.15.0-M1" +val scalaTestVersion = "3.2.11" +ThisBuild / scalaVersion := "3.3.0" + +def priorTo2_13(scalaVersion: String): Boolean = + CrossVersion.partialVersion(scalaVersion) match { + case Some((2, minor)) if minor < 13 => true + case _ => false + } + +val baseSettings = Seq( + scalacOptions ++= compilerOptions +) + +lazy val benchmark = project + .in(file(".")) + .settings(baseSettings ++ noPublishSettings) + .settings( + libraryDependencies ++= Seq( + "io.circe" %% "circe-core", + "io.circe" %% "circe-generic", + "io.circe" %% "circe-parser" + ).map(_ % circeVersion), + libraryDependencies ++= Seq( + "org.playframework" %% "play-json" % "3.0.1", + "io.argonaut" %% "argonaut" % "6.3.9", + "co.blocke" %% "scalajack" % "826a30_unknown", + "co.blocke" %% "scala-reflection" % "sj_fixes_edbef8", + "dev.zio" %% "zio-json" % "0.6.1", + // "io.circe" %% "circe-derivation" % "0.15.0-M1", + // "io.circe" %% "circe-jackson29" % "0.14.0", + // "org.json4s" %% "json4s-jackson" % "4.0.4", + // "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core" % "2.13.17", + // "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-macros" % "2.13.17", + "org.scalatest" %% "scalatest" % scalaTestVersion % Test + ) + ) + .enablePlugins(JmhPlugin) + +lazy val noPublishSettings = Seq( + publish := {}, + publishLocal := {}, + publishArtifact := false +) diff --git a/benchmark/project/build.properties b/benchmark/project/build.properties new file mode 100644 index 00000000..27430827 --- /dev/null +++ b/benchmark/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.9.6 diff --git a/benchmark/project/plugins.sbt b/benchmark/project/plugins.sbt new file mode 100644 index 00000000..514aeb2e --- /dev/null +++ b/benchmark/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.6") diff --git a/benchmark/src/main/scala/co.blocke/Argonaut.scala b/benchmark/src/main/scala/co.blocke/Argonaut.scala new file mode 100644 index 00000000..99424827 --- /dev/null +++ b/benchmark/src/main/scala/co.blocke/Argonaut.scala @@ -0,0 +1,26 @@ +package co.blocke + +import argonaut._, Argonaut._ +import org.openjdk.jmh.annotations._ + + +implicit val CodecPet: CodecJson[Pet] = + casecodec3(Pet.apply, (a: Pet) => Option((a.name, a.species, a.age)))("name","species","age") + +implicit val CodecFriend: CodecJson[Friend] = + casecodec3(Friend.apply, (a: Friend) => Option((a.name, a.age, a.email)))("name","age","email") + +implicit val CodecAddress: CodecJson[Address] = + casecodec4(Address.apply, (a: Address) => Option((a.street, a.city, a.state, a.postal_code)))("street","city","state","postal_code") + +implicit val CodecPerson: CodecJson[Person] = + casecodec6(Person.apply, (a: Person) => Option((a.name, a.age, a.address, a.email, a.phone_numbers, a.is_employed)))("name", "age","address","email","phone_numbers","is_employed") + +implicit val CodecRecord: CodecJson[Record] = + casecodec4(Record.apply, (a: Record) => Option((a.person, a.hobbies, a.friends, a.pets)))("person", "hobbies", "friends", "pets") + + +trait ArgonautWritingBenchmark { + @Benchmark + def writeRecordArgonaut = record.asJson +} diff --git a/benchmark/src/main/scala/co.blocke/Benchmark.scala b/benchmark/src/main/scala/co.blocke/Benchmark.scala new file mode 100644 index 00000000..a5210215 --- /dev/null +++ b/benchmark/src/main/scala/co.blocke/Benchmark.scala @@ -0,0 +1,96 @@ +package co.blocke + +import org.openjdk.jmh.annotations._ + +import java.util.concurrent.TimeUnit + +import co.blocke.scalajack.* + +import io.circe.syntax.* +import io.circe.* +import io.circe.generic.semiauto.* + +val record = ScalaJack.read[Record](jsData) + +implicit val recordDecoder: Decoder[Record] = deriveDecoder[Record] +implicit val recordEncoder: Encoder[Record] = deriveEncoder[Record] + +implicit val personDecoder: Decoder[Person] = deriveDecoder[Person] +implicit val personEncoder: Encoder[Person] = deriveEncoder[Person] + +implicit val addressDecoder: Decoder[Address] = deriveDecoder[Address] +implicit val addressEncoder: Encoder[Address] = deriveEncoder[Address] + +implicit val friendDecoder: Decoder[Friend] = deriveDecoder[Friend] +implicit val friendEncoder: Encoder[Friend] = deriveEncoder[Friend] + +implicit val petDecoder: Decoder[Pet] = deriveDecoder[Pet] +implicit val petEncoder: Encoder[Pet] = deriveEncoder[Pet] + + + +trait CirceReadingBenchmark{ + val circeJson = record.asJson + def readRecordCirce = circeJson.as[Record] +} + +// trait ScalaJackReadingBenchmark{ +// def readRecordScalaJack = ScalaJack.read[Record](jsData) +// } + +trait CirceWritingBenchmark { + @Benchmark + def writeRecordCirce = record.asJson +} + +trait ScalaJackWritingBenchmark { + @Benchmark + def writeRecordScalaJack = ScalaJack.write(record) +} + +trait HandTooledWritingBenchmark { + @Benchmark + def writeRecordHandTooled = + val sb = new StringBuilder() + sb.append("{") + sb.append("\"person\":{") + sb.append("\"name:\":\""+record.person.name+"\",") + sb.append("\"age:\":"+record.person.age+",") + sb.append("\"address:\":{\"street\":"+record.person.address.street+"\",") + sb.append("\"city\":\""+record.person.address.city+"\",") + sb.append("\"state\":\""+record.person.address.state+"\",") + sb.append("\"postal_code\":\""+record.person.address.postal_code+"\"},") + sb.append("\"email:\":\""+record.person.email+"\",") + sb.append("\"phone_numbers:\":[") + record.person.phone_numbers.map(p => sb.append("\""+p+"\",")) + sb.append("],") + sb.append("\"is_employed:\":"+record.person.is_employed+"},") + sb.append("\"hobbies:\":[") + record.hobbies.map(p => sb.append("\""+p+"\",")) + sb.append("],") + sb.append("\"friends:\":[") + record.friends.map(f=>sb.append(s"""{"name":"${f.name},"age":${f.age},"email":"${f.email}"},""")) + sb.append("],") + sb.append("\"pets:\":[") + record.pets.map(f=>sb.append(s"""{"name":"${f.name},"species":"${f.species}"","age":${f.age}},""")) + sb.append("]}") + sb.toString + } + +// @State(Scope.Thread) +// @BenchmarkMode(Array(Mode.Throughput)) +// @OutputTimeUnit(TimeUnit.SECONDS) +// class ReadingBenchmark +// extends CirceReadingBenchmark +// with ScalaJackReadingBenchmark + +@State(Scope.Thread) +@BenchmarkMode(Array(Mode.Throughput)) +@OutputTimeUnit(TimeUnit.SECONDS) +class WritingBenchmark + extends CirceWritingBenchmark + with ScalaJackWritingBenchmark + with HandTooledWritingBenchmark + with ArgonautWritingBenchmark + with PlayWritingBenchmark + with ZIOJsonWritingBenchmark diff --git a/benchmark/src/main/scala/co.blocke/PlayJson.scala b/benchmark/src/main/scala/co.blocke/PlayJson.scala new file mode 100644 index 00000000..9e1ccd27 --- /dev/null +++ b/benchmark/src/main/scala/co.blocke/PlayJson.scala @@ -0,0 +1,46 @@ +package co.blocke + +import play.api.libs.json._ +import play.api.libs.json.Reads._ +import play.api.libs.functional.syntax._ +import org.openjdk.jmh.annotations._ + +implicit val friendWrites: Writes[Friend] = ( + (JsPath \ "name").write[String] and + (JsPath \ "age").write[Int] and + (JsPath \ "email").write[String] +)(unlift((a: Friend) => Option((a.name, a.age, a.email)))) + +implicit val petWrites: Writes[Pet] = ( + (JsPath \ "name").write[String] and + (JsPath \ "species").write[String] and + (JsPath \ "age").write[Int] +)(unlift((a: Pet) => Option((a.name, a.species, a.age)))) + +implicit val addressWrites: Writes[Address] = ( + (JsPath \ "street").write[String] and + (JsPath \ "city").write[String] and + (JsPath \ "state").write[String] and + (JsPath \ "postal_code").write[String] +)(unlift((a: Address) => Option((a.street, a.city, a.state, a.postal_code)))) + +implicit val personWrites: Writes[Person] = ( + (JsPath \ "namet").write[String] and + (JsPath \ "age").write[Int] and + (JsPath \ "address").write[Address] and + (JsPath \ "email").write[String] and + (JsPath \ "phone_numbers").write[List[String]] and + (JsPath \ "is_employed").write[Boolean] +)(unlift((a: Person) => Option((a.name, a.age, a.address, a.email, a.phone_numbers, a.is_employed)))) + +implicit val recordWrites: Writes[Record] = ( + (JsPath \ "person").write[Person] and + (JsPath \ "hobbies").write[List[String]] and + (JsPath \ "friends").write[List[Friend]] and + (JsPath \ "pets").write[List[Pet]] +)(unlift((a: Record) => Option((a.person, a.hobbies, a.friends, a.pets)))) + +trait PlayWritingBenchmark { + @Benchmark + def writeRecordPlay = Json.toJson(record) +} diff --git a/benchmark/src/main/scala/co.blocke/Record.scala b/benchmark/src/main/scala/co.blocke/Record.scala new file mode 100644 index 00000000..dc8a9e60 --- /dev/null +++ b/benchmark/src/main/scala/co.blocke/Record.scala @@ -0,0 +1,86 @@ +package co.blocke + +case class Person( + name: String, + age: Int, + address: Address, + email: String, + phone_numbers: List[String], + is_employed: Boolean +) + +case class Address( + street: String, + city: String, + state: String, + postal_code: String +) + +case class Friend( + name: String, + age: Int, + email: String +) + +case class Pet( + name: String, + species: String, + age: Int +) + +case class Record( + person: Person, + hobbies: List[String], + friends: List[Friend], + pets: List[Pet] +) + + +val jsData = + """{ + "person": { + "name": "John Doe", + "age": 30, + "address": { + "street": "123 Main Street", + "city": "Anytown", + "state": "CA", + "postal_code": "12345" + }, + "email": "john.doe@example.com", + "phone_numbers": [ + "555-555-5555", + "555-123-4567" + ], + "is_employed": true + }, + "hobbies": [ + "reading", + "swimming", + "traveling" + ], + "friends": [ + { + "name": "Jane Smith", + "age": 28, + "email": "jane.smith@example.com" + }, + { + "name": "Bob Johnson", + "age": 32, + "email": "bob.johnson@example.com" + } + ], + "pets": [ + { + "name": "Fido", + "species": "Dog", + "age": 5 + }, + { + "name": "Whiskers", + "species": "Cat", + "age": 3 + } + ] + }""" \ No newline at end of file diff --git a/benchmark/src/main/scala/co.blocke/Run.scala b/benchmark/src/main/scala/co.blocke/Run.scala new file mode 100644 index 00000000..5a1d8a31 --- /dev/null +++ b/benchmark/src/main/scala/co.blocke/Run.scala @@ -0,0 +1,8 @@ +package co.blocke + +case class Foo() extends ZIOJsonWritingBenchmark + +object RunMe extends App: + + val f = Foo() + println(f.writeRecordZIOJson) \ No newline at end of file diff --git a/benchmark/src/main/scala/co.blocke/ZIOJson.scala b/benchmark/src/main/scala/co.blocke/ZIOJson.scala new file mode 100644 index 00000000..d0462744 --- /dev/null +++ b/benchmark/src/main/scala/co.blocke/ZIOJson.scala @@ -0,0 +1,20 @@ +package co.blocke + +import zio.json._ +import org.openjdk.jmh.annotations._ + +implicit val decoder1: JsonDecoder[Address] = DeriveJsonDecoder.gen[Address] +implicit val decoder2: JsonDecoder[Pet] = DeriveJsonDecoder.gen[Pet] +implicit val decoder3: JsonDecoder[Friend] = DeriveJsonDecoder.gen[Friend] +implicit val decoder4: JsonDecoder[Person] = DeriveJsonDecoder.gen[Person] +implicit val decoder5: JsonDecoder[Record] = DeriveJsonDecoder.gen[Record] +implicit val encoder1: JsonEncoder[Address] = DeriveJsonEncoder.gen[Address] +implicit val encoder2: JsonEncoder[Pet] = DeriveJsonEncoder.gen[Pet] +implicit val encoder3: JsonEncoder[Friend] = DeriveJsonEncoder.gen[Friend] +implicit val encoder4: JsonEncoder[Person] = DeriveJsonEncoder.gen[Person] +implicit val encoder5: JsonEncoder[Record] = DeriveJsonEncoder.gen[Record] + +trait ZIOJsonWritingBenchmark { + @Benchmark + def writeRecordZIOJson = record.toJson +} \ No newline at end of file diff --git a/build.sbt b/build.sbt index ceeca868..98a371dc 100644 --- a/build.sbt +++ b/build.sbt @@ -35,11 +35,12 @@ lazy val root = project Test / parallelExecution := false, scalafmtOnCompile := !isCI, libraryDependencies ++= Seq( - "co.blocke" %% "scala-reflection" % "sj_fixes_58a385", - "org.apache.commons" % "commons-text" % "1.10.0", - "org.scalameta" %% "munit" % "1.0.0-M9" % Test, - "org.json4s" %% "json4s-core" % "4.0.6" % Test, - "org.json4s" %% "json4s-native" % "4.0.6" % Test + "co.blocke" %% "scala-reflection" % "sj_fixes_edbef8", + "org.apache.commons" % "commons-text" % "1.10.0", + "io.github.kitlangton" %% "neotype" % "0.0.9", + "org.scalatest" %% "scalatest" % "3.2.17" % Test, + "org.json4s" %% "json4s-core" % "4.0.6" % Test, + "org.json4s" %% "json4s-native" % "4.0.6" % Test ) ) @@ -68,8 +69,7 @@ ThisBuild / githubWorkflowPublish := Seq( //========================== lazy val settings = Seq( javacOptions ++= Seq("-source", "1.8", "-target", "1.8"), - scalacOptions ++= compilerOptions, - testFrameworks += new TestFramework("munit.Framework") + scalacOptions ++= compilerOptions ) lazy val compilerOptions = Seq( diff --git a/src/main/scala/co.blocke.scalajack/ScalaJack.scala b/src/main/scala/co.blocke.scalajack/ScalaJack.scala index 56f43fe9..7974fb9a 100644 --- a/src/main/scala/co.blocke.scalajack/ScalaJack.scala +++ b/src/main/scala/co.blocke.scalajack/ScalaJack.scala @@ -1,26 +1,23 @@ package co.blocke.scalajack -import co.blocke.scala_reflection.TypedName +import co.blocke.scala_reflection.{RTypeRef, TypedName} import co.blocke.scala_reflection.reflect.ReflectOnType import co.blocke.scala_reflection.reflect.rtypeRefs.ClassRef -import scala.collection.mutable.HashMap +import scala.collection.mutable.{HashMap, Map} import scala.quoted.* import quoted.Quotes import json.* object ScalaJack: - inline def write[T](t: T)(using cfg: JsonConfig = JsonConfig()): String = ${ writeImpl[T]('t, 'cfg) } + inline def write[T](a: T)(using cfg: JsonConfig = JsonConfig()): String = ${ writeImpl[T]('a, 'cfg) } - def writeImpl[T: Type](t: Expr[T], cfg: Expr[JsonConfig])(using q: Quotes): Expr[String] = - import quotes.reflect.* - - val rtRef = ReflectOnType[T](q)(TypeRepr.of[T])(using scala.collection.mutable.Map.empty[TypedName, Boolean]) - val fn = JsonWriter.writeJsonFn[T](rtRef) - '{ - val sb = new StringBuilder() - $fn($t, sb, $cfg).toString - } + def writeImpl[T](aE: Expr[T], cfg: Expr[JsonConfig])(using q: Quotes, tt: Type[T]): Expr[String] = + import q.reflect.* + val ref = ReflectOnType(q)(TypeRepr.of[T], true)(using Map.empty[TypedName, Boolean]).asInstanceOf[RTypeRef[T]] + val sbE = '{ new StringBuilder() } + val classesSeen = Map.empty[TypedName, RTypeRef[?]] + '{ ${ JsonWriter.refWrite[T](cfg, ref, aE, sbE)(using classesSeen) }.toString } // --------------------------------------------------------------------- @@ -43,21 +40,3 @@ object ScalaJack: case Right(v) => v case Left(t) => throw t } - - /* - inline def foo[T](str: String)(using cfg: Config): T = ${ fooImpl[T]('str, 'cfg) } - - def fooImpl[T: Type](str: Expr[String], cfg: Expr[Config])(using q: Quotes): Expr[T] = - import quotes.reflect.* - - // How can I get some configuration here??? - - '{ // run-time - doSomething($str, $cfg) // can use str and cfg here - } - - Parameters may only be: - * Quoted parameters or fields - * Literal values of primitive types - * References to `inline val`s - */ diff --git a/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala b/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala index 8982166c..5aa07adb 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala @@ -5,6 +5,7 @@ case class JsonConfig( noneAsNull: Boolean = false, forbidNullsInInput: Boolean = false, tryFailureHandling: TryOption = TryOption.NO_WRITE, + permissivePrimitives: Boolean = false, // -------------------------- typeHintLabel: String = "_hint", typeHintLabelByTrait: Map[String, String] = Map.empty[String, String], // Trait name -> type hint label diff --git a/src/main/scala/co.blocke.scalajack/json/JsonParser.scala b/src/main/scala/co.blocke.scalajack/json/JsonParser.scala index 928df8d5..4b558da6 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonParser.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonParser.scala @@ -69,7 +69,14 @@ case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) case 'f' if i + 4 < max && jsChars(i + 1) == 'a' && jsChars(i + 2) == 'l' && jsChars(i + 3) == 's' && jsChars(i + 4) == 'e' => i += 5 Right(false) - case x => Left(JsonParseError(showError(s"Unexpected character '$x' where beginning of boolean value expected at position [$i]"))) + case x => + if cfg.permissivePrimitives && jsChars(i) == '"' then + for { + _ <- expectQuote + result <- expectBoolean(cfg, p) + _ <- expectQuote + } yield result + else Left(JsonParseError(showError(s"Unexpected character '$x' where beginning of boolean value expected at position [$i]"))) def expectLong(cfg: JsonConfig, p: JsonParser): Either[ParseError, Long] = val mark = i @@ -81,11 +88,18 @@ case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) Try(js.substring(mark, i).toLong) match case Success(g) => Right(g) case Failure(f) => - val msg = - if mark == i then s"""Int/Long expected but couldn't parse from "${jsChars(i)}" at position [$i]""" - else s"""Int/Long expected but couldn't parse from "${js.substring(mark, i)}" at position [$i]""" - i = mark - Left(JsonParseError(showError(msg))) + if cfg.permissivePrimitives && jsChars(i) == '"' then + for { + _ <- expectQuote + result <- expectLong(cfg, p) + _ <- expectQuote + } yield result + else + val msg = + if mark == i then s"""Int/Long expected but couldn't parse from "${jsChars(i)}" at position [$i]""" + else s"""Int/Long expected but couldn't parse from "${js.substring(mark, i)}" at position [$i]""" + i = mark + Left(JsonParseError(showError(msg))) def expectBigLong(cfg: JsonConfig, p: JsonParser): Either[ParseError, BigInt] = nullCheck match @@ -101,11 +115,18 @@ case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) Try(BigInt(js.substring(mark, i))) match case Success(g) => Right(g) case Failure(f) => - val msg = - if mark == i then s"""Int/Long expected but couldn't parse from "${jsChars(i)}" at position [$i]""" - else s"""Int/Long expected but couldn't parse from "${js.substring(mark, i)}" at position [$i]""" - i = mark - Left(JsonParseError(showError(msg))) + if cfg.permissivePrimitives && jsChars(i) == '"' then + for { + _ <- expectQuote + result <- expectBigLong(cfg, p) + _ <- expectQuote + } yield result + else + val msg = + if mark == i then s"""Int/Long expected but couldn't parse from "${jsChars(i)}" at position [$i]""" + else s"""Int/Long expected but couldn't parse from "${js.substring(mark, i)}" at position [$i]""" + i = mark + Left(JsonParseError(showError(msg))) def expectDouble(cfg: JsonConfig, p: JsonParser): Either[ParseError, Double] = val mark = i @@ -117,11 +138,18 @@ case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) Try(js.substring(mark, i).toDouble) match case Success(g) => Right(g) case Failure(_) => - val msg = - if mark == i then s"Float/Double expected but couldn't parse from \"${jsChars(i)}\" at position [$i]" - else s"Float/Double expected but couldn't parse from \"${js.substring(mark, i)}\" at position [$i]" - i = mark - Left(JsonParseError(showError(msg))) + if cfg.permissivePrimitives && jsChars(i) == '"' then + for { + _ <- expectQuote + result <- expectDouble(cfg, p) + _ <- expectQuote + } yield result + else + val msg = + if mark == i then s"Float/Double expected but couldn't parse from \"${jsChars(i)}\" at position [$i]" + else s"Float/Double expected but couldn't parse from \"${js.substring(mark, i)}\" at position [$i]" + i = mark + Left(JsonParseError(showError(msg))) def expectBigDouble(cfg: JsonConfig, p: JsonParser): Either[ParseError, BigDecimal] = nullCheck match @@ -137,11 +165,18 @@ case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) Try(BigDecimal(js.substring(mark, i))) match case Success(g) => Right(g) case Failure(_) => - val msg = - if mark == i then s"Float/Double expected but couldn't parse from \"${jsChars(i)}\" at position [$i]" - else s"Float/Double expected but couldn't parse from \"${js.substring(mark, i)}\" at position [$i]" - i = mark - Left(JsonParseError(showError(msg))) + if cfg.permissivePrimitives && jsChars(i) == '"' then + for { + _ <- expectQuote + result <- expectBigDouble(cfg, p) + _ <- expectQuote + } yield result + else + val msg = + if mark == i then s"Float/Double expected but couldn't parse from \"${jsChars(i)}\" at position [$i]" + else s"Float/Double expected but couldn't parse from \"${js.substring(mark, i)}\" at position [$i]" + i = mark + Left(JsonParseError(showError(msg))) def expectString(cfg: JsonConfig, p: JsonParser): Either[ParseError, String] = nullCheck match @@ -161,7 +196,7 @@ case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) case _ => i += 1 i += 1 Right(captured.getOrElse(js.substring(mark, i - 1))) - case x => Left(JsonParseError(showError(s"Unexpected character '$x' where beginning of label expected at position [$i]"))) + case x => Left(JsonParseError(showError(s"Unexpected character '$x' where beginning of a string expected at position [$i]"))) def expectList[T](cfg: JsonConfig, expectElement: (JsonConfig, JsonParser) => Either[ParseError, T]): Either[ParseError, List[T]] = nullCheck match diff --git a/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala b/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala index fc6952cc..d78bc15a 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala @@ -1,17 +1,34 @@ package co.blocke.scalajack package json -import co.blocke.scala_reflection.reflect.rtypeRefs.* -import co.blocke.scala_reflection.reflect.* -import co.blocke.scala_reflection.{RTypeRef, TypedName} -import co.blocke.scala_reflection.rtypes.* -import co.blocke.scala_reflection.Liftables.TypedNameToExpr import scala.quoted.* -import co.blocke.scala_reflection.RType -import scala.jdk.CollectionConverters.* -import java.util.concurrent.ConcurrentHashMap -import scala.util.{Failure, Success} -import org.apache.commons.text.StringEscapeUtils +import co.blocke.scala_reflection.* +import co.blocke.scala_reflection.rtypes.{ScalaClassRType, TraitRType} +import co.blocke.scala_reflection.reflect.{ReflectOnType, TypeSymbolMapper} +import co.blocke.scala_reflection.reflect.rtypeRefs.* +import scala.util.{Failure, Success, Try} +import scala.quoted.staging.* + +/* + TODO: + [ ] - Scala non-case class + [ ] - Java class (Do I still want to support this???) + [ ] - Enum + [ ] - Enumeration + [ ] - Java Enum + [ ] - Java Collections + [ ] - Java Map + [ ] - Intersection + [ ] - Union + [ ] - Either + [ ] - Object (How???) + [ ] - Sealed Trait (How???) + [*] - SelfRef + [ ] - Tuple + [ ] - Unknown (throw exception) + [ ] - Scala 2 (throw exception) + [ ] - TypeSymbol (throw exception) + */ object JsonWriter: @@ -24,353 +41,180 @@ object JsonWriter: case Failure(_) if cfg.tryFailureHandling == TryOption.NO_WRITE => false case _ => true - val refCache = new ConcurrentHashMap[TypedName, (?, StringBuilder, JsonConfig) => StringBuilder] - - def writeJsonFn[T](rtRef: RTypeRef[T], isMapKey: Boolean = false)(using tt: Type[T], q: Quotes): Expr[(T, StringBuilder, JsonConfig) => StringBuilder] = + def refWrite[T]( + cfgE: Expr[JsonConfig], + ref: RTypeRef[T], + aE: Expr[T], + sbE: Expr[StringBuilder], + isMapKey: Boolean = false + )(using classesSeen: scala.collection.mutable.Map[TypedName, RTypeRef[?]])(using Quotes, Type[T]): Expr[StringBuilder] = import quotes.reflect.* - rtRef match - case rt: PrimitiveRef[?] if rt.family == PrimFamily.Stringish => - val nullable = Expr(rt.isNullable) - '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - if $nullable && a == null then sb.append("null") - else - sb.append('"') - sb.append(StringEscapeUtils.escapeJson(a.toString)) - sb.append('"') - } - case rt: PrimitiveRef[?] => - val nullable = Expr(rt.isNullable) + ref match + case t: PrimitiveRef[?] if t.family == PrimFamily.Stringish => '{ if $aE == null then $sbE.append("null") else $sbE.append("\"" + $aE.toString + "\"") } + case t: PrimitiveRef[?] => if isMapKey then - '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - if $nullable && a == null then sb.append("null") + '{ + if $aE == null then $sbE.append("\"null\"") else - sb.append('"') - sb.append(a.toString) - sb.append('"') - } - else - '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - if $nullable && a == null then sb.append("null") - else sb.append(a.toString) + $sbE.append('"') + $sbE.append($aE.toString) + $sbE.append('"') } + else '{ if $aE == null then $sbE.append("null") else $sbE.append($aE.toString) } - case rt: AliasRef[?] => - val fn = writeJsonFn[rt.T](rt.unwrappedType.asInstanceOf[RTypeRef[rt.T]], isMapKey)(using Type.of[rt.T]) - '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => $fn(a, sb, cfg) } + case t: SeqRef[?] => + if isMapKey then throw new JsonError("Seq instances cannot be map keys") - case rt: ArrayRef[?] => - if isMapKey then throw new JsonError("Arrays cannot be map keys.") - rt.elementRef.refType match - case '[t] => - val elementFn = writeJsonFn[t](rt.elementRef.asInstanceOf[RTypeRef[t]]) - '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - if a == null then sb.append("null") + t.elementRef.refType match + case '[e] => + '{ + val sb = $sbE + if $aE == null then sb.append("null") else sb.append('[') val sbLen = sb.length - a.asInstanceOf[Array[t]].foreach { e => - if isOkToWrite(e, cfg) then - $elementFn(e.asInstanceOf[t], sb, cfg) + + $aE.asInstanceOf[Seq[e]].foldLeft(sb) { (acc, one) => + if isOkToWrite(one, $cfgE) then + ${ refWrite[e](cfgE, t.elementRef.asInstanceOf[RTypeRef[e]], '{ one }, '{ acc }) } sb.append(',') + else sb } + if sbLen == sb.length then sb.append(']') else sb.setCharAt(sb.length() - 1, ']') } + case t: ArrayRef[?] => + if isMapKey then throw new JsonError("Arrays cannot be map keys") + + t.elementRef.refType match + case '[e] => + '{ + val sb = $sbE + if $aE == null then sb.append("null") + else + sb.append('[') + val sbLen = sb.length - case rt: ClassRef[?] => - if isMapKey then throw new JsonError("Classes cannot be map keys.") - val fieldFns = rt.fields.map { f => - f.fieldRef.refType match - case '[t] => (writeJsonFn[t](f.fieldRef.asInstanceOf[RTypeRef[t]]), f) - } - val typedName = Expr(rt.typedName) + $aE.asInstanceOf[Array[e]].foldLeft(sb) { (acc, one) => + if isOkToWrite(one, $cfgE) then + ${ refWrite[e](cfgE, t.elementRef.asInstanceOf[RTypeRef[e]], '{ one }, '{ acc }) } + sb.append(',') + else sb + } + + if sbLen == sb.length then sb.append(']') + else sb.setCharAt(sb.length() - 1, ']') + } + + case t: ClassRef[?] => + classesSeen.put(t.typedName, t) '{ - val classFn = (a: T, sb: StringBuilder, cfg: JsonConfig) => + val sb = $sbE + if $aE == null then sb.append("null") + else sb.append('{') val sbLen = sb.length ${ - val hintStmt = (rt match - case s: ScalaClassRef[?] if s.renderTrait.isDefined => - val traitName = Expr(s.renderTrait.get) - '{ - sb.append('"') - sb.append(cfg.typeHintLabelByTrait.getOrElse($traitName, cfg.typeHintLabel)) - sb.append('"') - sb.append(':') - sb.append('"') - val hint = cfg.typeHintTransformer.get(a.getClass.getName) match - case Some(xform) => xform(a) - case None => cfg.typeHintDefaultTransformer(a.getClass.getName) - sb.append(hint) - sb.append('"') - sb.append(',') - () - } - case _ => '{ () } - ) // .asInstanceOf[Expr[Unit]] - val stmts = hintStmt :: fieldFns.map { case (fn, field) => - '{ - val fieldValue = ${ - Select.unique('{ a }.asTerm, field.name).asExpr - } - if isOkToWrite(fieldValue, cfg) then - ${ - field.fieldRef.refType match - case '[t] => - '{ - sb.append('"') - sb.append(${ Expr(field.name) }) - sb.append('"') - sb.append(':') - val fn2 = $fn.asInstanceOf[(t, StringBuilder, JsonConfig) => StringBuilder] - fn2(fieldValue.asInstanceOf[t], sb, cfg) - sb.append(',') - } + t.fields.foldLeft('{ sb }) { (accE, f) => + f.fieldRef.refType match + case '[e] => + val fieldValue = Select.unique(aE.asTerm, f.name).asExprOf[e] + val name = Expr(f.name) + '{ + val acc = $accE + if isOkToWrite($fieldValue, $cfgE) then + acc.append('"') + acc.append($name) + acc.append('"') + acc.append(':') + val b = ${ refWrite[e](cfgE, f.fieldRef.asInstanceOf[RTypeRef[e]], fieldValue, '{ acc }) } + acc.append(',') + else acc } - } } - Expr.ofList(stmts) } if sbLen == sb.length then sb.append('}') else sb.setCharAt(sb.length() - 1, '}') - refCache.put($typedName, classFn) - classFn } - case rt: TraitRef[?] => - if isMapKey then throw new JsonError("Traits cannot be map keys.") - val typedName = Expr(rt.typedName) - val traitType = rt.expr - '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - val comboName = ($traitType.typedName.toString + "::" + a.getClass.getName).asInstanceOf[TypedName] - Option(refCache.get(comboName)) match - case Some(writerFn) => - writerFn.asInstanceOf[(T, StringBuilder, JsonConfig) => StringBuilder](a, sb, cfg) - case None => - val writerFn = ReflectUtil.inTermsOf[T]($traitType, a, sb, cfg) - refCache.put(comboName, writerFn) - writerFn(a, sb, cfg) - } - - case rt: SeqRef[?] => - if isMapKey then throw new JsonError("Seq instances cannot be map keys.") - rt.elementRef.refType match - case '[t] => - val elementFn = writeJsonFn[t](rt.elementRef.asInstanceOf[RTypeRef[t]]) - '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - sb.append('[') - val sbLen = sb.length - a.asInstanceOf[Seq[?]].foreach { e => - if isOkToWrite(e, cfg) then - $elementFn(e.asInstanceOf[t], sb, cfg) - sb.append(',') - } - if sbLen == sb.length then sb.append(']') - else sb.setCharAt(sb.length() - 1, ']') - } - - case rt: OptionRef[?] => - if isMapKey then throw new JsonError("Options cannot be map keys.") - rt.optionParamType.refType match - case '[t] => - val fn = writeJsonFn[t](rt.optionParamType.asInstanceOf[RTypeRef[t]]) - '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - a match - case None => sb.append("null") - case Some(v) => $fn(v.asInstanceOf[t], sb, cfg) - } - - case rt: TryRef[?] => - if isMapKey then throw new JsonError("Try cannot be map keys.") - rt.tryRef.refType match - case '[t] => - val fn = writeJsonFn[t](rt.tryRef.asInstanceOf[RTypeRef[t]]) - '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - a match - case Success(v) => $fn(v.asInstanceOf[t], sb, cfg) - case Failure(_) if cfg.tryFailureHandling == TryOption.AS_NULL => sb.append("null") - case Failure(v) => - sb.append('"') - sb.append(v.getMessage) - sb.append('"') - } - - case rt: MapRef[?] => - if isMapKey then throw new JsonError("Maps cannot be map keys.") - rt.elementRef.refType match - case '[k] => - rt.elementRef2.refType match - case '[v] => - val keyFn = writeJsonFn[k](rt.elementRef.asInstanceOf[RTypeRef[k]], true) - val valueFn = writeJsonFn[v](rt.elementRef2.asInstanceOf[RTypeRef[v]]) - '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - sb.append('{') - val sbLen = sb.length - a.asInstanceOf[Map[?, ?]].foreach { case (key, value) => - if isOkToWrite(value, cfg) then - $keyFn(key.asInstanceOf[k], sb, cfg) - sb.append(':') - $valueFn(value.asInstanceOf[v], sb, cfg) - sb.append(',') - } - if sbLen == sb.length then sb.append('}') - else sb.setCharAt(sb.length() - 1, '}') - } - - case rt: LeftRightRef[?] => - if isMapKey then throw new JsonError("Union, intersection, or Either cannot be map keys.") - rt.leftRef.refType match - case '[lt] => - val leftFn = writeJsonFn[lt](rt.leftRef.asInstanceOf[RTypeRef[lt]]) - rt.rightRef.refType match - case '[rt] => - val rightFn = writeJsonFn[rt](rt.rightRef.asInstanceOf[RTypeRef[rt]]) - val rtypeExpr = rt.expr - '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - $rtypeExpr match - case r if r.clazz == classOf[Either[_, _]] => - a match - case Left(v) => - $leftFn(v.asInstanceOf[lt], sb, cfg) - case Right(v) => - $rightFn(v.asInstanceOf[rt], sb, cfg) - case _: RType[?] => // Intersection & Union types.... take your best shot! It's all we've got. No definitive info here. - val trial = new StringBuilder() - if scala.util.Try($rightFn(a.asInstanceOf[rt], trial, cfg)).isFailure then $leftFn(a.asInstanceOf[lt], sb, cfg) - else sb ++= trial - } - - case rt: EnumRef[?] => - val expr = rt.expr - val isMapKeyExpr = Expr(isMapKey) + case t: TraitRef[?] => + classesSeen.put(t.typedName, t) + val rt = t.expr '{ - val rtype = $expr.asInstanceOf[EnumRType[?]] - (a: T, sb: StringBuilder, cfg: JsonConfig) => - val enumAsId = cfg.enumsAsIds match - case '*' => true - case aList: List[String] if aList.contains(rtype.name) => true - case _ => false - if enumAsId then - val enumVal = rtype.ordinal(a.toString).getOrElse(throw new JsonError(s"Value $a is not a valid enum value for ${rtype.name}")) - if $isMapKeyExpr then - sb.append('"') - sb.append(enumVal.toString) - sb.append('"') - else sb.append(enumVal.toString) - else - sb.append('"') - sb.append(a.toString) - sb.append('"') - } - - // TODO: Not sure this is right! - case rt: SealedTraitRef[?] => - '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - sb.append('"') - a.toString - sb.append('"') - } - - case rt: TupleRef[?] => - if isMapKey then throw new JsonError("Tuples cannot be map keys.") - val elementFns = rt.tupleRefs.map { f => - f.refType match - case '[t] => (writeJsonFn[t](f.asInstanceOf[RTypeRef[t]]), f) - } - val numElementsExpr = Expr(elementFns.size) - '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - sb.append('[') - val sbLen = sb.length - ${ - val stmts = elementFns.zipWithIndex.map { case ((fn, e), i) => - '{ - val fieldValue = ${ - Select.unique('{ a }.asTerm, "_" + (i + 1)).asExpr - } - ${ - e.refType match - case '[t] => - '{ - val fn2 = $fn.asInstanceOf[(t, StringBuilder, JsonConfig) => StringBuilder] - fn2(fieldValue.asInstanceOf[t], sb, cfg) - sb.append(',') - } - } - } - } - Expr.ofList(stmts) + given Compiler = Compiler.make($aE.getClass.getClassLoader) + val fn = (q: Quotes) ?=> { + import q.reflect.* + val sb = $sbE + val classRType = RType.inTermsOf[T]($aE.getClass).asInstanceOf[ScalaClassRType[T]].copy(renderTrait = Some($rt.name)).asInstanceOf[RType[T]] + JsonWriterRT.refWriteRT[classRType.T]($cfgE, classRType, $aE.asInstanceOf[classRType.T], $sbE)(using scala.collection.mutable.Map.empty[TypedName, RType[?]]) + Expr(1) // do-nothing... '{} requires Expr(something) be returned, so... } - if sbLen == sb.length then sb.append(']') - else sb.setCharAt(sb.length() - 1, ']') - } - - case rt: SelfRefRef[?] => - if isMapKey then throw new JsonError("Classes or traits cannot be map keys.") - val e = rt.expr - '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - val fn = refCache.get($e.typedName).asInstanceOf[(T, StringBuilder, JsonConfig) => StringBuilder] - fn(a, sb, cfg) + quoted.staging.run(fn) + $sbE } - case rt: JavaCollectionRef[?] => - if isMapKey then throw new JsonError("Collections cannot be map keys.") - rt.elementRef.refType match - case '[t] => - val elementFn = writeJsonFn[t](rt.elementRef.asInstanceOf[RTypeRef[t]]) - '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - sb.append('[') - val sbLen = sb.length - a.asInstanceOf[java.util.Collection[?]].toArray.foreach { e => - if isOkToWrite(e, cfg) then - $elementFn(e.asInstanceOf[t], sb, cfg) - sb.append(',') - } - if sbLen == sb.length then sb.append(']') - else sb.setCharAt(sb.length() - 1, ']') + case t: OptionRef[?] => + if isMapKey then throw new JsonError("Option valuess cannot be map keys") + t.optionParamType.refType match + case '[e] => + '{ + $aE match + case None => $sbE.append("null") + case Some(v) => + ${ refWrite[e](cfgE, t.optionParamType.asInstanceOf[RTypeRef[e]], '{ v }.asInstanceOf[Expr[e]], sbE) } } - case rt: JavaMapRef[?] => - if isMapKey then throw new JsonError("Maps cannot be map keys.") - rt.elementRef.refType match + case t: MapRef[?] => + if isMapKey then throw new JsonError("Map values cannot be map keys") + t.elementRef.refType match case '[k] => - rt.elementRef2.refType match + t.elementRef2.refType match case '[v] => - val keyFn = writeJsonFn[k](rt.elementRef.asInstanceOf[RTypeRef[k]], true) - val valueFn = writeJsonFn[v](rt.elementRef.asInstanceOf[RTypeRef[v]]) - '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - sb.append('{') - val sbLen = sb.length - a.asInstanceOf[java.util.Map[?, ?]].asScala.foreach { case (key, value) => - if isOkToWrite(value, cfg) then - $keyFn(key.asInstanceOf[k], sb, cfg) - sb.append(':') - $valueFn(value.asInstanceOf[v], sb, cfg) - sb.append(',') - } - if sbLen == sb.length then sb.append('}') - else sb.setCharAt(sb.length() - 1, '}') + '{ + val sb = $sbE + if $aE == null then sb.append("null") + else + sb.append('{') + val sbLen = sb.length + $aE.asInstanceOf[Map[?, ?]].foreach { case (key, value) => + if isOkToWrite(value, $cfgE) then + val b = ${ refWrite[k](cfgE, t.elementRef.asInstanceOf[RTypeRef[k]], '{ key }.asInstanceOf[Expr[k]], sbE, true) } + b.append(':') + val b2 = ${ refWrite[v](cfgE, t.elementRef2.asInstanceOf[RTypeRef[v]], '{ value }.asInstanceOf[Expr[v]], sbE) } + b2.append(',') + } + if sbLen == sb.length then sb.append('}') + else sb.setCharAt(sb.length() - 1, '}') } - case rt: ObjectRef => - val name = Expr(rt.name) - '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - sb.append('"') - sb.append($name) - sb.append('"') - } + case t: TryRef[?] => + if isMapKey then throw new JsonError("Try values (Succeed/Fail) cannot be map keys") + t.tryRef.refType match + case '[e] => + '{ + $aE match + case Success(v) => + ${ refWrite[e](cfgE, t.tryRef.asInstanceOf[RTypeRef[e]], '{ v }.asInstanceOf[Expr[e]], sbE) } + case Failure(_) if $cfgE.tryFailureHandling == TryOption.AS_NULL => $sbE.append("null") + case Failure(v) => + $sbE.append('"') + $sbE.append(v.getMessage) + $sbE.append('"') + } - case rt: Scala2Ref[?] => - val name = Expr(rt.name) - '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - sb.append('"') - sb.append($name) - sb.append('"') - } + case t: AliasRef[?] => + t.unwrappedType.refType match + case '[e] => + refWrite[e](cfgE, t.unwrappedType.asInstanceOf[RTypeRef[e]], aE.asInstanceOf[Expr[e]], sbE) - case rt: UnknownRef[?] => - '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - sb.append('"') - sb.append("unknown") - sb.append('"') + case t: SelfRefRef[?] => + if isMapKey then throw new JsonError("Classes or traits cannot be map keys.") + import quotes.reflect.* + val againE = classesSeen.getOrElse(t.typedName, throw new JsonError("Dangling self-reference: " + t.name)).asInstanceOf[RTypeRef[T]].expr + '{ + val again = $againE.asInstanceOf[RType[T]] + JsonWriterRT.refWriteRT[T]($cfgE, again, $aE.asInstanceOf[T], $sbE)(using scala.collection.mutable.Map.empty[TypedName, RType[?]]) + $sbE } diff --git a/src/main/scala/co.blocke.scalajack/json/JsonWriter.scalax b/src/main/scala/co.blocke.scalajack/json/JsonWriter.scalax new file mode 100644 index 00000000..d33919ef --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/JsonWriter.scalax @@ -0,0 +1,394 @@ +package co.blocke.scalajack +package json + +import co.blocke.scala_reflection.reflect.rtypeRefs.* +import co.blocke.scala_reflection.reflect.* +import co.blocke.scala_reflection.{RTypeRef, TypedName} +import co.blocke.scala_reflection.rtypes.* +import co.blocke.scala_reflection.Liftables.TypedNameToExpr +import scala.quoted.* +import co.blocke.scala_reflection.RType +import scala.jdk.CollectionConverters.* +import java.util.concurrent.ConcurrentHashMap +import scala.util.{Failure, Success} +import org.apache.commons.text.StringEscapeUtils + +object JsonWriter: + + // Tests whether we should write something or not--mainly in the case of Option, or wrapped Option + def isOkToWrite(a: Any, cfg: JsonConfig) = + a match + case None if !cfg.noneAsNull => false + case Left(None) if !cfg.noneAsNull => false + case Right(None) if !cfg.noneAsNull => false + case Failure(_) if cfg.tryFailureHandling == TryOption.NO_WRITE => false + case _ => true + + val refCache = new ConcurrentHashMap[TypedName, (?, StringBuilder, JsonConfig) => StringBuilder] + + def writeJsonFn[T](rtRef: RTypeRef[T], isMapKey: Boolean = false)(using tt: Type[T], q: Quotes): Expr[(T, StringBuilder, JsonConfig) => StringBuilder] = + import quotes.reflect.* + + rtRef match +//***** + case rt: PrimitiveRef[?] if rt.family == PrimFamily.Stringish => + val nullable = Expr(rt.isNullable) + '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => + if $nullable && a == null then sb.append("null") + else + sb.append('"') + sb.append(StringEscapeUtils.escapeJson(a.toString)) + sb.append('"') + } +//***** + case rt: PrimitiveRef[?] => + val nullable = Expr(rt.isNullable) + if isMapKey then + '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => + if $nullable && a == null then sb.append("null") + else + sb.append('"') + sb.append(a.toString) + sb.append('"') + } + else + '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => + if $nullable && a == null then sb.append("null") + else sb.append(a.toString) + } + +//***** + case rt: AliasRef[?] => + val fn = writeJsonFn[rt.T](rt.unwrappedType.asInstanceOf[RTypeRef[rt.T]], isMapKey)(using Type.of[rt.T]) + '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => $fn(a, sb, cfg) } + +//***** + case rt: ArrayRef[?] => + if isMapKey then throw new JsonError("Arrays cannot be map keys.") + rt.elementRef.refType match + case '[t] => + val elementFn = writeJsonFn[t](rt.elementRef.asInstanceOf[RTypeRef[t]]) + '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => + if a == null then sb.append("null") + else + sb.append('[') + val sbLen = sb.length + a.asInstanceOf[Array[t]].foreach { e => + if isOkToWrite(e, cfg) then + $elementFn(e.asInstanceOf[t], sb, cfg) + sb.append(',') + } + if sbLen == sb.length then sb.append(']') + else sb.setCharAt(sb.length() - 1, ']') + } + +//***** + case rt: ClassRef[?] => + if isMapKey then throw new JsonError("Classes cannot be map keys.") + val fieldFns = rt.fields.map { f => + f.fieldRef.refType match + case '[t] => (writeJsonFn[t](f.fieldRef.asInstanceOf[RTypeRef[t]]), f) + } + val typedName = Expr(rt.typedName) + '{ + val classFn = (a: T, sb: StringBuilder, cfg: JsonConfig) => + sb.append('{') + val sbLen = sb.length + ${ + val hintStmt = (rt match + case s: ScalaClassRef[?] if s.renderTrait.isDefined => + val traitName = Expr(s.renderTrait.get) + '{ + sb.append('"') + sb.append(cfg.typeHintLabelByTrait.getOrElse($traitName, cfg.typeHintLabel)) + sb.append('"') + sb.append(':') + sb.append('"') + val hint = cfg.typeHintTransformer.get(a.getClass.getName) match + case Some(xform) => xform(a) + case None => cfg.typeHintDefaultTransformer(a.getClass.getName) + sb.append(hint) + sb.append('"') + sb.append(',') + () + } + case _ => '{ () } + ) // .asInstanceOf[Expr[Unit]] + val stmts = hintStmt :: fieldFns.map { case (fn, field) => + '{ + val fieldValue = ${ + Select.unique('{ a }.asTerm, field.name).asExpr + } + if isOkToWrite(fieldValue, cfg) then + ${ + field.fieldRef.refType match + case '[t] => + '{ + sb.append('"') + sb.append(${ Expr(field.name) }) + sb.append('"') + sb.append(':') + val fn2 = $fn.asInstanceOf[(t, StringBuilder, JsonConfig) => StringBuilder] + fn2(fieldValue.asInstanceOf[t], sb, cfg) + sb.append(',') + } + } + } + } + Expr.ofList(stmts) + } + if sbLen == sb.length then sb.append('}') + else sb.setCharAt(sb.length() - 1, '}') + refCache.put($typedName, classFn) + classFn + } + + case rt: TraitRef[?] => + if isMapKey then throw new JsonError("Traits cannot be map keys.") + val typedName = Expr(rt.typedName) + val traitType = rt.expr + '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => + val comboName = ($traitType.typedName.toString + "::" + a.getClass.getName).asInstanceOf[TypedName] + Option(refCache.get(comboName)) match + case Some(writerFn) => + writerFn.asInstanceOf[(T, StringBuilder, JsonConfig) => StringBuilder](a, sb, cfg) + case None => + val writerFn = ReflectUtil.inTermsOf[T]($traitType, a, sb, cfg) + refCache.put(comboName, writerFn) + writerFn(a, sb, cfg) + } + +//***** + case rt: SeqRef[?] => + if isMapKey then throw new JsonError("Seq instances cannot be map keys.") + rt.elementRef.refType match + case '[t] => + val elementFn = writeJsonFn[t](rt.elementRef.asInstanceOf[RTypeRef[t]]) + '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => + sb.append('[') + val sbLen = sb.length + a.asInstanceOf[Seq[?]].foreach { e => + if isOkToWrite(e, cfg) then + $elementFn(e.asInstanceOf[t], sb, cfg) + sb.append(',') + } + if sbLen == sb.length then sb.append(']') + else sb.setCharAt(sb.length() - 1, ']') + } + +//***** + case rt: OptionRef[?] => + if isMapKey then throw new JsonError("Options cannot be map keys.") + rt.optionParamType.refType match + case '[t] => + val fn = writeJsonFn[t](rt.optionParamType.asInstanceOf[RTypeRef[t]]) + '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => + a match + case None => sb.append("null") + case Some(v) => $fn(v.asInstanceOf[t], sb, cfg) + } + +//***** + case rt: TryRef[?] => + if isMapKey then throw new JsonError("Try cannot be map keys.") + rt.tryRef.refType match + case '[t] => + val fn = writeJsonFn[t](rt.tryRef.asInstanceOf[RTypeRef[t]]) + '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => + a match + case Success(v) => $fn(v.asInstanceOf[t], sb, cfg) + case Failure(_) if cfg.tryFailureHandling == TryOption.AS_NULL => sb.append("null") + case Failure(v) => + sb.append('"') + sb.append(v.getMessage) + sb.append('"') + } + +//***** + case rt: MapRef[?] => + if isMapKey then throw new JsonError("Maps cannot be map keys.") + rt.elementRef.refType match + case '[k] => + rt.elementRef2.refType match + case '[v] => + val keyFn = writeJsonFn[k](rt.elementRef.asInstanceOf[RTypeRef[k]], true) + val valueFn = writeJsonFn[v](rt.elementRef2.asInstanceOf[RTypeRef[v]]) + '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => + sb.append('{') + val sbLen = sb.length + a.asInstanceOf[Map[?, ?]].foreach { case (key, value) => + if isOkToWrite(value, cfg) then + $keyFn(key.asInstanceOf[k], sb, cfg) + sb.append(':') + $valueFn(value.asInstanceOf[v], sb, cfg) + sb.append(',') + } + if sbLen == sb.length then sb.append('}') + else sb.setCharAt(sb.length() - 1, '}') + } + + case rt: LeftRightRef[?] => + if isMapKey then throw new JsonError("Union, intersection, or Either cannot be map keys.") + rt.leftRef.refType match + case '[lt] => + val leftFn = writeJsonFn[lt](rt.leftRef.asInstanceOf[RTypeRef[lt]]) + rt.rightRef.refType match + case '[rt] => + val rightFn = writeJsonFn[rt](rt.rightRef.asInstanceOf[RTypeRef[rt]]) + val rtypeExpr = rt.expr + '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => + $rtypeExpr match + case r if r.clazz == classOf[Either[_, _]] => + a match + case Left(v) => + $leftFn(v.asInstanceOf[lt], sb, cfg) + case Right(v) => + $rightFn(v.asInstanceOf[rt], sb, cfg) + case _: RType[?] => // Intersection & Union types.... take your best shot! It's all we've got. No definitive info here. + val trial = new StringBuilder() + if scala.util.Try($rightFn(a.asInstanceOf[rt], trial, cfg)).isFailure then $leftFn(a.asInstanceOf[lt], sb, cfg) + else sb ++= trial + } + + case rt: EnumRef[?] => + val expr = rt.expr + val isMapKeyExpr = Expr(isMapKey) + '{ + val rtype = $expr.asInstanceOf[EnumRType[?]] + (a: T, sb: StringBuilder, cfg: JsonConfig) => + val enumAsId = cfg.enumsAsIds match + case '*' => true + case aList: List[String] if aList.contains(rtype.name) => true + case _ => false + if enumAsId then + val enumVal = rtype.ordinal(a.toString).getOrElse(throw new JsonError(s"Value $a is not a valid enum value for ${rtype.name}")) + if $isMapKeyExpr then + sb.append('"') + sb.append(enumVal.toString) + sb.append('"') + else sb.append(enumVal.toString) + else + sb.append('"') + sb.append(a.toString) + sb.append('"') + } + + // TODO: Not sure this is right! + case rt: SealedTraitRef[?] => + '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => + sb.append('"') + a.toString + sb.append('"') + } + + case rt: TupleRef[?] => + if isMapKey then throw new JsonError("Tuples cannot be map keys.") + val elementFns = rt.tupleRefs.map { f => + f.refType match + case '[t] => (writeJsonFn[t](f.asInstanceOf[RTypeRef[t]]), f) + } + val numElementsExpr = Expr(elementFns.size) + '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => + sb.append('[') + val sbLen = sb.length + ${ + val stmts = elementFns.zipWithIndex.map { case ((fn, e), i) => + '{ + val fieldValue = ${ + Select.unique('{ a }.asTerm, "_" + (i + 1)).asExpr + } + ${ + e.refType match + case '[t] => + '{ + val fn2 = $fn.asInstanceOf[(t, StringBuilder, JsonConfig) => StringBuilder] + fn2(fieldValue.asInstanceOf[t], sb, cfg) + sb.append(',') + } + } + } + } + Expr.ofList(stmts) + } + if sbLen == sb.length then sb.append(']') + else sb.setCharAt(sb.length() - 1, ']') + } + + case rt: SelfRefRef[?] => + if isMapKey then throw new JsonError("Classes or traits cannot be map keys.") + val e = rt.expr + '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => + val fn = refCache.get($e.typedName).asInstanceOf[(T, StringBuilder, JsonConfig) => StringBuilder] + fn(a, sb, cfg) + } + + case rt: JavaCollectionRef[?] => + if isMapKey then throw new JsonError("Collections cannot be map keys.") + rt.elementRef.refType match + case '[t] => + val elementFn = writeJsonFn[t](rt.elementRef.asInstanceOf[RTypeRef[t]]) + '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => + sb.append('[') + val sbLen = sb.length + a.asInstanceOf[java.util.Collection[?]].toArray.foreach { e => + if isOkToWrite(e, cfg) then + $elementFn(e.asInstanceOf[t], sb, cfg) + sb.append(',') + } + if sbLen == sb.length then sb.append(']') + else sb.setCharAt(sb.length() - 1, ']') + } + + case rt: JavaMapRef[?] => + if isMapKey then throw new JsonError("Maps cannot be map keys.") + rt.elementRef.refType match + case '[k] => + rt.elementRef2.refType match + case '[v] => + val keyFn = writeJsonFn[k](rt.elementRef.asInstanceOf[RTypeRef[k]], true) + val valueFn = writeJsonFn[v](rt.elementRef.asInstanceOf[RTypeRef[v]]) + '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => + sb.append('{') + val sbLen = sb.length + a.asInstanceOf[java.util.Map[?, ?]].asScala.foreach { case (key, value) => + if isOkToWrite(value, cfg) then + $keyFn(key.asInstanceOf[k], sb, cfg) + sb.append(':') + $valueFn(value.asInstanceOf[v], sb, cfg) + sb.append(',') + } + if sbLen == sb.length then sb.append('}') + else sb.setCharAt(sb.length() - 1, '}') + } + + case rt: ObjectRef => + val name = Expr(rt.name) + '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => + sb.append('"') + sb.append($name) + sb.append('"') + } + + case rt: Scala2Ref[?] => + val name = Expr(rt.name) + '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => + sb.append('"') + sb.append($name) + sb.append('"') + } + + // case rt: NeoTypeRef[?] => + // import neotype.* + // rt.refType match + // case '[r] => + // '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => + // def goGet: T = summon[Newtype.WithType[?, T]] + // val unwrapped = a.asInstanceOf[Newtype[?]].unwrap + // } + + case rt: UnknownRef[?] => + '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => + sb.append('"') + sb.append("unknown") + sb.append('"') + } diff --git a/src/main/scala/co.blocke.scalajack/json/JsonWriter2.scalax b/src/main/scala/co.blocke.scalajack/json/JsonWriter2.scalax new file mode 100644 index 00000000..bb25f426 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/JsonWriter2.scalax @@ -0,0 +1,251 @@ +package co.blocke.scalajack +package json + +import scala.quoted.* +import co.blocke.scala_reflection.* +import co.blocke.scala_reflection.rtypes.* +import co.blocke.scala_reflection.reflect.{ReflectOnType, TypeSymbolMapper} +import co.blocke.scala_reflection.reflect.rtypeRefs.* +import scala.util.{Failure, Success, Try} +import scala.quoted.staging.* + +object JsonWriter2: + + // Tests whether we should write something or not--mainly in the case of Option, or wrapped Option + def isOkToWrite(a: Any, cfg: JsonConfig) = + a match + case None if !cfg.noneAsNull => false + case Left(None) if !cfg.noneAsNull => false + case Right(None) if !cfg.noneAsNull => false + case Failure(_) if cfg.tryFailureHandling == TryOption.NO_WRITE => false + case _ => true + + def refWrite[T](cfgE: Expr[JsonConfig], rt: RType[T], aE: Expr[T], sbE: Expr[StringBuilder], isMapKey: Boolean = false)(using Quotes)(using tt: Type[T]): Expr[StringBuilder] = + import quotes.reflect.* + + rt match + case StringRType() | CharRType() | JavaCharacterRType() => '{ if $aE == null then $sbE.append("null") else $sbE.append("\"" + $aE.toString + "\"") } + case t: PrimitiveRType => + if isMapKey then + '{ + if $aE == null then $sbE.append("\"null\"") + else + $sbE.append('"') + $sbE.append($aE.toString) + $sbE.append('"') + } + else '{ if $aE == null then $sbE.append("null") else $sbE.append($aE.toString) } + + case t: SeqRType[?] => + if isMapKey then throw new JsonError("Seq instances cannot be map keys") + + t.elementType.toType(quotes) match + case '[e] => + '{ + val sb = $sbE + if $aE == null then sb.append("null") + else + sb.append('[') + val sbLen = sb.length + + $aE.asInstanceOf[Seq[e]].foldLeft(sb) { (acc, one) => + if isOkToWrite(one, $cfgE) then + ${ refWrite[e](cfgE, t.elementType.asInstanceOf[RType[e]], '{ one }, '{ acc }) } + sb.append(',') + else sb + } + + if sbLen == sb.length then sb.append(']') + else sb.setCharAt(sb.length() - 1, ']') + } + case t: ArrayRType[?] => + if isMapKey then throw new JsonError("Arrays instances cannot be map keys") + + t.elementType.toType(quotes) match + case '[e] => + '{ + val sb = $sbE + if $aE == null then sb.append("null") + else + sb.append('[') + val sbLen = sb.length + + $aE.asInstanceOf[Seq[e]].foldLeft(sb) { (acc, one) => + if isOkToWrite(one, $cfgE) then + ${ refWrite[e](cfgE, t.elementType.asInstanceOf[RType[e]], '{ one }, '{ acc }) } + sb.append(',') + else sb + } + + if sbLen == sb.length then sb.append(']') + else sb.setCharAt(sb.length() - 1, ']') + } + + case t: ClassRType[?] => + '{ + val sb = $sbE + if $aE == null then sb.append("null") + else + sb.append('{') + val sbLen = sb.length + ${ + t.fields.foldLeft('{ sb }) { (accE, f) => + f.fieldType.toType(quotes) match + case '[e] => + val fieldValue = Select.unique(aE.asTerm, f.name).asExprOf[e] + val name = Expr(f.name) + '{ + val acc = $accE + if isOkToWrite($fieldValue, $cfgE) then + acc.append('"') + acc.append($name) + acc.append('"') + acc.append(':') + val b = ${ refWrite[e](cfgE, f.fieldType.asInstanceOf[RType[e]], fieldValue, '{ acc }) } + acc.append(',') + else acc + } + } + } + if sbLen == sb.length then sb.append('}') + else sb.setCharAt(sb.length() - 1, '}') + } + + case t: TraitRType[?] => + val foo: Expr[RType[T]] = '{ + // given Compiler = Compiler.make($aE.getClass.getClassLoader) + val sb = $sbE + sb.append('{') + sb.append(s"\"_hint\":\"${$aE.getClass.getName}\"") + sb.append('}') + RType.inTermsOf[T]($aE.getClass).asInstanceOf[RType[T]] + val rt: Expr[RType[T]] = ??? + ${refWrite[T](cfgE, rt.value, aE, sbE)} + } + + + // val fn = (q: Quotes) ?=> { + // import q.reflect.* + // ReflectUtil.inTermsOf2(RType.of[T].asInstanceOf[TraitRType[T]], $aE.getClass).expr.asInstanceOf[Expr[RTypeRef[?]]] + // } + // val classRType = quoted.staging.run(fn).asInstanceOf[RType[T]] + // ${ + // t.toType(quotes) match + // case '[e] => + + // } + // $sbE + // val cRT: ScalaClassRType[T] = ??? + // refWrite[T](cfgE, cRT, aE, sbE) + + /* + given Compiler = Compiler.make(this.getClass.getClassLoader) + '{ + val refMakerFn = (q: Quotes) ?=> + import q.reflect.* + (traitRef: TraitRef[T], inst: T, instE: Expr[T], cfgE: Expr[JsonConfig], sbE2: Expr[StringBuilder]) => + val clazz = inst.getClass + val classRef = ReflectOnType(quotes)(quotes.reflect.TypeRepr.typeConstructorOf(clazz), false)(using scala.collection.mutable.Map.empty[TypedName, Boolean]).asInstanceOf[ScalaClassRef[?]] + val inTermsOfRef = + if traitRef.typeParamSymbols.nonEmpty then + val seenBefore = scala.collection.mutable.Map.empty[TypedName, Boolean] + val paths = classRef.typePaths.getOrElse(traitRef.name, throw new ReflectException(s"No path in class ${classRef.name} for trait ${traitRef.name}")) + traitRef.refType match + case '[e] => + val typeParamTypes = TypeSymbolMapper.runPath(quotes)(paths, TypeRepr.of[e]) + val classQuotedTypeRepr = TypeRepr.typeConstructorOf(clazz) + ReflectOnType(q)(classQuotedTypeRepr.appliedTo(typeParamTypes))(using seenBefore) + else classRef + classRef.refType match + case '[e] => + ${ + val y = refWrite[e](cfgE, classRef.asInstanceOf[RTypeRef[e]], instE.asInstanceOf[Expr[e]], sbE2) + '{ y } + } + quoted.staging.run(refMakerFn(t, $aE, aE, cfgE, sbE)) + } + */ + + /* + '{ + val fn = (quotes: Quotes) ?=> + (ref2: TraitRef[T], aE2: Expr[T], a2: T, cfgE2: Expr[JsonConfig]) => + import quotes.reflect.* + val clazz = a2.getClass + val classRef = ReflectOnType(quotes)(quotes.reflect.TypeRepr.typeConstructorOf(clazz), false)(using scala.collection.mutable.Map.empty[TypedName, Boolean]).asInstanceOf[ScalaClassRef[?]] + val inTermsOfRef = + if ref2.typeParamSymbols.nonEmpty then + val seenBefore = scala.collection.mutable.Map.empty[TypedName, Boolean] + val paths = classRef.typePaths.getOrElse(ref2.name, throw new ReflectException(s"No path in class ${classRef.name} for trait ${ref2.name}")) + ref2.refType match + case '[e] => + val typeParamTypes = TypeSymbolMapper.runPath(quotes)(paths, TypeRepr.of[e]) + val classQuotedTypeRepr = TypeRepr.typeConstructorOf(clazz) + ReflectOnType(quotes)(classQuotedTypeRepr.appliedTo(typeParamTypes))(using seenBefore) + else classRef + + inTermsOfRef.refType match + case '[e] => + val asClassRef = inTermsOfRef.asInstanceOf[ScalaClassRef[e]].copy(renderTrait = Some(ref2.name)) // set renderTrait so ClassRef writer code renders type hint + refWrite[e](cfgE, asClassRef.asInstanceOf[RTypeRef[e]], aE2.asInstanceOf[Expr[e]], sbE) + + given Compiler = Compiler.make(this.getClass.getClassLoader) + quoted.staging.run(fn(t, aE, $aE, cfgE)) + } + */ + + /* + case t: OptionRef[?] => + if isMapKey then throw new JsonError("Option valuess cannot be map keys") + t.optionParamType.refType match + case '[e] => + '{ + $aE match + case None => $sbE.append("null") + case Some(v) => + ${ refWrite[e](cfgE, t.optionParamType.asInstanceOf[RTypeRef[e]], '{ v }.asInstanceOf[Expr[e]], sbE) } + } + + case t: MapRef[?] => + if isMapKey then throw new JsonError("Map values cannot be map keys") + t.elementRef.refType match + case '[k] => + t.elementRef2.refType match + case '[v] => + '{ + val sb = $sbE + if $aE == null then sb.append("null") + else + sb.append('{') + val sbLen = sb.length + $aE.asInstanceOf[Map[?, ?]].foreach { case (key, value) => + if isOkToWrite(value, $cfgE) then + val b = ${ refWrite[k](cfgE, t.elementRef.asInstanceOf[RTypeRef[k]], '{ key }.asInstanceOf[Expr[k]], sbE, true) } + b.append(':') + val b2 = ${ refWrite[v](cfgE, t.elementRef2.asInstanceOf[RTypeRef[v]], '{ value }.asInstanceOf[Expr[v]], sbE) } + b2.append(',') + } + if sbLen == sb.length then sb.append('}') + else sb.setCharAt(sb.length() - 1, '}') + } + + case t: TryRef[?] => + if isMapKey then throw new JsonError("Try values (Succeed/Fail) cannot be map keys") + t.tryRef.refType match + case '[e] => + '{ + $aE match + case Success(v) => + ${ refWrite[e](cfgE, t.tryRef.asInstanceOf[RTypeRef[e]], '{ v }.asInstanceOf[Expr[e]], sbE) } + case Failure(_) if $cfgE.tryFailureHandling == TryOption.AS_NULL => $sbE.append("null") + case Failure(v) => + $sbE.append('"') + $sbE.append(v.getMessage) + $sbE.append('"') + } + + case t: AliasRef[?] => + t.unwrappedType.refType match + case '[e] => + refWrite[e](cfgE, t.unwrappedType.asInstanceOf[RTypeRef[e]], aE.asInstanceOf[Expr[e]], sbE) +*/ \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json/JsonWriterRT.scala b/src/main/scala/co.blocke.scalajack/json/JsonWriterRT.scala new file mode 100644 index 00000000..9731c81d --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/JsonWriterRT.scala @@ -0,0 +1,160 @@ +package co.blocke.scalajack +package json + +import co.blocke.scala_reflection.* +import co.blocke.scala_reflection.rtypes.* +import co.blocke.scala_reflection.reflect.{ReflectOnType, TypeSymbolMapper} +import co.blocke.scala_reflection.reflect.rtypeRefs.* +import scala.util.{Failure, Success, Try} + +/** + * This class is horrible. It is a mirror of JsonWriter, except this one executes at runtime, and hence + * doens't have any Expr's. This is bad becuause it's slow--we're not able to take advantage of doing work + * at compile-time. This is ONLY used becuase of trait handling. The problem is we need to express some + * class C in terms of trait T. We know T at compile-time but don't know C until compile-time. There's + * (so far) no good way to get the information passed across the bridge, so... What we need to do in + * JsonWriter is leverage RType.inTermsOf to do the C->T magic, but this returns an RType, not an + * RTypeRef. We must render the rest of the trait (and any structures below it) at runtime, necessitating + * this class, JsonWriterRT. + * + * It should only be used for this special trait handling. Everything else leverages the compile-time + * JsonWriter. + */ + +object JsonWriterRT: + + // Tests whether we should write something or not--mainly in the case of Option, or wrapped Option + def isOkToWrite(a: Any, cfg: JsonConfig) = + a match + case None if !cfg.noneAsNull => false + case Left(None) if !cfg.noneAsNull => false + case Right(None) if !cfg.noneAsNull => false + case Failure(_) if cfg.tryFailureHandling == TryOption.NO_WRITE => false + case _ => true + + def refWriteRT[T]( + cfg: JsonConfig, + rt: RType[T], + a: T, + sb: StringBuilder, + isMapKey: Boolean = false + )(using classesSeen: scala.collection.mutable.Map[TypedName, RType[?]]): StringBuilder = + + rt match + case StringRType() | CharRType() | JavaCharacterRType() => if a == null then sb.append("null") else sb.append("\"" + a.toString + "\"") + case t: PrimitiveRType => + if isMapKey then + if a == null then sb.append("\"null\"") + else + sb.append('"') + sb.append(a.toString) + sb.append('"') + else if a == null then sb.append("null") else sb.append(a.toString) + + case t: SeqRType[?] => + if isMapKey then throw new JsonError("Seq instances cannot be map keys") + if a == null then sb.append("null") + else + sb.append('[') + val sbLen = sb.length + a.asInstanceOf[Seq[t.elementType.T]].foldLeft(sb) { (acc, one) => + if isOkToWrite(one, cfg) then + refWriteRT[t.elementType.T](cfg, t.elementType.asInstanceOf[RType[t.elementType.T]], one, acc) + sb.append(',') + else sb + } + if sbLen == sb.length then sb.append(']') + else sb.setCharAt(sb.length() - 1, ']') + + case t: ArrayRType[?] => + if isMapKey then throw new JsonError("Arrays instances cannot be map keys") + if a == null then sb.append("null") + else + sb.append('[') + val sbLen = sb.length + a.asInstanceOf[Seq[t.elementType.T]].foldLeft(sb) { (acc, one) => + if isOkToWrite(one, cfg) then + refWriteRT[t.elementType.T](cfg, t.elementType.asInstanceOf[RType[t.elementType.T]], one, acc) + sb.append(',') + else sb + } + if sbLen == sb.length then sb.append(']') + else sb.setCharAt(sb.length() - 1, ']') + + case t: ScalaClassRType[?] => + classesSeen.put(t.typedName, t) + if a == null then sb.append("null") + else + sb.append('{') + val sbLen = sb.length + t.renderTrait.map( traitName => sb.append(s"\"_hint\":\"$traitName\",") ) + t.fields.foldLeft(sb) { (acc, f) => + val m = a.getClass.getMethod(f.name) + m.setAccessible(true) + val fieldValue = m.invoke(a).asInstanceOf[f.fieldType.T] + if isOkToWrite(fieldValue, cfg) then + acc.append('"') + acc.append(f.name) + acc.append('"') + acc.append(':') + refWriteRT[f.fieldType.T](cfg, f.fieldType.asInstanceOf[RType[f.fieldType.T]], fieldValue, acc) + acc.append(',') + else acc + } + if sbLen == sb.length then sb.append('}') + else sb.setCharAt(sb.length() - 1, '}') + + case t: TraitRType[?] => + classesSeen.put(t.typedName, t) + val classRType = RType.inTermsOf[T](a.getClass).asInstanceOf[ScalaClassRType[T]].copy(renderTrait = Some(t.name)).asInstanceOf[RType[T]] + JsonWriterRT.refWriteRT[classRType.T](cfg, classRType, a.asInstanceOf[classRType.T], sb) + + case t: OptionRType[?] => + if isMapKey then throw new JsonError("Option valuess cannot be map keys") + a match + case None => sb.append("null") + case Some(v) => + refWriteRT[t.optionParamType.T](cfg, t.optionParamType.asInstanceOf[RType[t.optionParamType.T]], v.asInstanceOf[t.optionParamType.T], sb) + + case t: MapRType[?] => + if isMapKey then throw new JsonError("Map values cannot be map keys") + if a == null then sb.append("null") + else + sb.append('{') + val sbLen = sb.length + a.asInstanceOf[Map[?, ?]].foreach { case (key, value) => + if isOkToWrite(value, cfg) then + val b = refWriteRT[t.elementType.T](cfg, t.elementType.asInstanceOf[RType[t.elementType.T]], key.asInstanceOf[t.elementType.T], sb, true) + b.append(':') + val b2 = refWriteRT[t.elementType2.T](cfg, t.elementType2.asInstanceOf[RType[t.elementType2.T]], value.asInstanceOf[t.elementType2.T], sb) + b2.append(',') + } + if sbLen == sb.length then sb.append('}') + else sb.setCharAt(sb.length() - 1, '}') + + case t: TryRType[?] => + if isMapKey then throw new JsonError("Try values (Succeed/Fail) cannot be map keys") + a match + case Success(v) => + refWriteRT[t.tryType.T](cfg, t.tryType.asInstanceOf[RType[t.tryType.T]], v.asInstanceOf[t.tryType.T], sb) + case Failure(_) if cfg.tryFailureHandling == TryOption.AS_NULL => sb.append("null") + case Failure(v) => + sb.append('"') + sb.append(v.getMessage) + sb.append('"') + + case t: AliasRType[?] => + refWriteRT[t.unwrappedType.T](cfg, t.unwrappedType.asInstanceOf[RType[t.unwrappedType.T]], a.asInstanceOf[t.unwrappedType.T], sb) + + case t: SelfRefRType[?] => + if isMapKey then throw new JsonError("Classes or traits cannot be map keys.") + val again = classesSeen.getOrElse( + t.typedName, + { + // Need to add to cache. Since we're coming from compile-time side, the runtime side may not have seen this class before... + val v = RType.of[T] + classesSeen.put(t.typedName, v) + v + } + ) + JsonWriterRT.refWriteRT[again.T](cfg, again, a.asInstanceOf[again.T], sb) diff --git a/src/main/scala/co.blocke.scalajack/json/ReflectUtil.scala b/src/main/scala/co.blocke.scalajack/json/ReflectUtil.scala deleted file mode 100644 index 950e333c..00000000 --- a/src/main/scala/co.blocke.scalajack/json/ReflectUtil.scala +++ /dev/null @@ -1,52 +0,0 @@ -package co.blocke.scalajack -package json - -import co.blocke.scala_reflection.{ReflectException, RType, RTypeRef, TypedName} -import co.blocke.scala_reflection.rtypes.TraitRType -import co.blocke.scala_reflection.reflect.rtypeRefs.* -import co.blocke.scala_reflection.reflect.* -import scala.quoted.* -import scala.quoted.staging.* - -object ReflectUtil: - - /** This function takes the RType of a trait and an instance of T and does two things. - * First it expresses the instance's class *in terms of* the trait's concrete type parameters (if any). - * Then it generates a writer function for the now correctly-typed class. - * - * @param traitType - * @param a - * @param sb - * @param cfg - * @return - */ - def inTermsOf[T](traitType: RType[?], a: T, sb: StringBuilder, cfg: JsonConfig) = - given Compiler = Compiler.make(getClass.getClassLoader) - - val clazz = a.getClass - - val fn = (quotes: Quotes) ?=> { - import quotes.reflect.* - - val classRef = ReflectOnType(quotes)(quotes.reflect.TypeRepr.typeConstructorOf(clazz), false)(using scala.collection.mutable.Map.empty[TypedName, Boolean]).asInstanceOf[ScalaClassRef[?]] - - val inTermsOfRef = traitType match - case ttype: TraitRType[?] if ttype.typeParamSymbols.nonEmpty => - val seenBefore = scala.collection.mutable.Map.empty[TypedName, Boolean] - val paths = classRef.typePaths.getOrElse(ttype.name, throw new ReflectException(s"No path in class ${classRef.name} for trait ${ttype.name}")) - ttype.toType(quotes) match - case '[t] => - val typeParamTypes = TypeSymbolMapper.runPath(quotes)(paths, TypeRepr.of[t]) - val classQuotedTypeRepr = TypeRepr.typeConstructorOf(clazz) - ReflectOnType(quotes)(classQuotedTypeRepr.appliedTo(typeParamTypes))(using seenBefore) - - case traitRef: TraitRType[?] => classRef - case x => throw new ReflectException(s"${x.name} is not of type trait") - - inTermsOfRef.refType match - case '[t] => - val asClassRef = inTermsOfRef.asInstanceOf[ScalaClassRef[t]].copy(renderTrait = Some(traitType.name)) - JsonWriter.writeJsonFn[t](asClassRef.asInstanceOf[RTypeRef[t]]) - } - val writeFn = run(fn) - writeFn.asInstanceOf[(T, StringBuilder, JsonConfig) => StringBuilder] diff --git a/src/main/scala/co.blocke.scalajack/json/ReflectUtil.scalax b/src/main/scala/co.blocke.scalajack/json/ReflectUtil.scalax new file mode 100644 index 00000000..c8045491 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/ReflectUtil.scalax @@ -0,0 +1,59 @@ +package co.blocke.scalajack +package json + +import co.blocke.scala_reflection.{ReflectException, RType, RTypeRef, TypedName} +import co.blocke.scala_reflection.rtypes.TraitRType +import co.blocke.scala_reflection.reflect.rtypeRefs.* +import co.blocke.scala_reflection.reflect.* +import scala.quoted.* +import scala.quoted.staging.* + +object ReflectUtil: + + /** This function takes the RType of a trait and an instance of T and does two things. + * First it expresses the instance's class *in terms of* the trait's concrete type parameters (if any). + * Then it generates a writer function for the now correctly-typed class. + */ +// Returns: StringBuilder + def inTermsOf[T](traitType: TraitRType[?], a: T, aE: Expr[T], sbE: Expr[StringBuilder], cfgE: Expr[JsonConfig]) = + given Compiler = Compiler.make(getClass.getClassLoader) + + val clazz = a.getClass + + val fn = (quotes: Quotes) ?=> { + import quotes.reflect.* + + val classRef = ReflectOnType(quotes)(quotes.reflect.TypeRepr.typeConstructorOf(clazz), false)(using scala.collection.mutable.Map.empty[TypedName, Boolean]).asInstanceOf[ScalaClassRef[?]] + + val inTermsOfRef = traitType.toType(quotes) match + case '[t] => + if traitType.typeParamSymbols.nonEmpty then + val seenBefore = scala.collection.mutable.Map.empty[TypedName, Boolean] + val paths = classRef.typePaths.getOrElse(traitType.name, throw new ReflectException(s"No path in class ${classRef.name} for trait ${traitType.name}")) + val typeParamTypes = TypeSymbolMapper.runPath(quotes)(paths, TypeRepr.of[t]) + val classQuotedTypeRepr = TypeRepr.typeConstructorOf(clazz) + ReflectOnType(quotes)(classQuotedTypeRepr.appliedTo(typeParamTypes))(using seenBefore) + else classRef + + inTermsOfRef.refType match + case '[e] => + val asClassRef = inTermsOfRef.asInstanceOf[ScalaClassRef[e]].copy(renderTrait = Some(traitType.name)).asInstanceOf[co.blocke.scala_reflection.RTypeRef[e]] + JsonWriter.refWrite[e](cfgE, asClassRef, aE.asInstanceOf[Expr[e]], sbE) + } + run(fn) + + def inTermsOf2[T](traitType: TraitRType[?], clazz: Class[?])(using Quotes) = + import quotes.reflect.* + + val classRef = ReflectOnType(quotes)(quotes.reflect.TypeRepr.typeConstructorOf(clazz), false)(using scala.collection.mutable.Map.empty[TypedName, Boolean]).asInstanceOf[ScalaClassRef[?]] + + (traitType.toType(quotes) match + case '[t] => + if traitType.typeParamSymbols.nonEmpty then + val seenBefore = scala.collection.mutable.Map.empty[TypedName, Boolean] + val paths = classRef.typePaths.getOrElse(traitType.name, throw new ReflectException(s"No path in class ${classRef.name} for trait ${traitType.name}")) + val typeParamTypes = TypeSymbolMapper.runPath(quotes)(paths, TypeRepr.of[t]) + val classQuotedTypeRepr = TypeRepr.typeConstructorOf(clazz) + ReflectOnType(quotes)(classQuotedTypeRepr.appliedTo(typeParamTypes))(using seenBefore) + else classRef + ).asInstanceOf[ScalaClassRef[?]] diff --git a/src/main/scala/co.blocke.scalajack/json/readers/PrimitiveReader.scala b/src/main/scala/co.blocke.scalajack/json/readers/PrimitiveReader.scala index a00cc8d2..d290ac89 100644 --- a/src/main/scala/co.blocke.scalajack/json/readers/PrimitiveReader.scala +++ b/src/main/scala/co.blocke.scalajack/json/readers/PrimitiveReader.scala @@ -73,9 +73,11 @@ case class PrimitiveReader(next: ReaderModule, root: ReaderModule) extends Reade '{ (j: JsonConfig, p: JsonParser) => p.expectString(j, p) .flatMap(s => - s.toArray.headOption match - case Some(c) => Right(c.asInstanceOf[T]) - case None => Left(JsonParseError(p.showError(s"Cannot convert value '$s' into a Char."))) + if s == null then Left(JsonParseError(p.showError(s"Char typed values cannot be null at position [${p.getPos}]"))) + else + s.toArray.headOption match + case Some(c) => Right(c.asInstanceOf[T]) + case None => Left(JsonParseError(p.showError(s"Cannot convert value '$s' into a Char at position [${p.getPos}]"))) ) } @@ -235,7 +237,7 @@ case class PrimitiveReader(next: ReaderModule, root: ReaderModule) extends Reade else scala.util.Try(java.util.UUID.fromString(u)) match case Success(uuid) => Right(uuid.asInstanceOf[T]) - case Failure(_) => Left(JsonParseError(p.showError(s"Unable to marshal UUID from value '$u'."))) + case Failure(_) => Left(JsonParseError(p.showError(s"Unable to marshal UUID from value '$u' at position [${p.getPos}]"))) ) } diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index 4a459d71..78482968 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -19,16 +19,55 @@ object RunMe extends App: given json.JsonConfig = json .JsonConfig() + .copy(noneAsNull = true) // .copy(enumsAsIds = '*') try + val v = + Person( + "Greg", + Some( + Person( + "Lili", + Some( + Person( + "Katie", + None + ) + ) + ) + ) + ) + + println("HERE: " + ScalaJack.write[Person[Boolean]](v.asInstanceOf[Person[Boolean]])) + + // println("HERE: " + ScalaJack.write(Person("Greg", Foom('z'), Some(Person("Lili", Foom('x'), None))))) + + // val now = System.currentTimeMillis() + // for i <- 1 to 10000000 do ScalaJack.write(List(List(1, 2, 3), List(4, 5, 6), List(7, 8, 9))) + // val now2 = System.currentTimeMillis() + // println("Macro-based: " + (now2 - now)) + + // val person = Person("David", 16) + // val minor = MinorPerson.make(person) match + // case Right(r) => r + // case _ => throw new Exception("boom") + + // val p = SampleNeo(NonEmptyString("Greg"), "MVP", minor) + // val js = ScalaJack.write(x) + // println(js) + + /* val x = Blah("foo", WeekDay.Fri) val js = ScalaJack.write(x) println(js) val inst = ScalaJack.read[Blah](js) println(inst) + */ catch { - case t: Throwable => println(s"BOOM ($t): " + t.getMessage) + case t: Throwable => + println(s"BOOM ($t): " + t.getMessage) + t.printStackTrace } diff --git a/src/main/scala/co.blocke.scalajack/run/Sample.scala b/src/main/scala/co.blocke.scalajack/run/Sample.scala index b0519080..f37fbb3b 100644 --- a/src/main/scala/co.blocke.scalajack/run/Sample.scala +++ b/src/main/scala/co.blocke.scalajack/run/Sample.scala @@ -1,39 +1,60 @@ package co.blocke.scalajack.run -opaque type BigName = String +import neotype.* -case class Person(name: String, age: Int, isOk: List[Boolean], favColor: Colors, boss: BigName) +// opaque type BigName = String -trait Animal: - val name: String - val numLegs: Int - val friend: Option[Animal] +// case class Person(name: String, age: Int, isOk: List[Boolean], favColor: Colors, boss: BigName) -trait Animal2: - val name: String - val numLegs: Int - val friend: Option[Animal2] +// trait Animal: +// val name: String +// val numLegs: Int +// val friend: Option[Animal] -case class Dog(name: String, numLegs: Int, carsChased: Int, friend: Option[Animal2]) extends Animal2 +// trait Animal2: +// val name: String +// val numLegs: Int +// val friend: Option[Animal2] -enum Colors: - case Red, Blue, Green +// case class Dog(name: String, numLegs: Int, carsChased: Int, friend: Option[Animal2]) extends Animal2 -import scala.collection.immutable.* -enum Vehicle: - case Car, Bus, Train +// enum Colors: +// case Red, Blue, Green -object WeekDay extends Enumeration { - type WeekDay = Value - val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value -} -import WeekDay.* +// import scala.collection.immutable.* +// enum Vehicle: +// case Car, Bus, Train -case class Simple(a: Int, b: Boolean, c: Option[Simple], z: Int = 5) +// object WeekDay extends Enumeration { +// type WeekDay = Value +// val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value +// } +// import WeekDay.* -case class Blah(msg: String, stuff: WeekDay) +// case class Simple(a: Int, b: Boolean, c: Option[Simple], z: Int = 5) -object Talk: - def say(s: String): String = s"Say $s!" +// case class Blah(msg: String, stuff: WeekDay) -case class M1(v: Map[Long, Int], v2: HashMap[Colors, Int], v3: Map[co.blocke.scala_reflection.TypedName, Int]) +// object Talk: +// def say(s: String): String = s"Say $s!" + +// case class M1(v: Map[Long, Int], v2: HashMap[Colors, Int], v3: Map[co.blocke.scala_reflection.TypedName, Int]) + +trait Miss[E] { val x: E } +case class Foom[X](x: X) extends Miss[X] + +// case class Person[Y](name: String, age: Miss[Y], again: Option[Person[Y]]) + +case class Person[Y](name: String, again: Option[Person[Y]]) + +// type NonEmptyString = NonEmptyString.Type +// given NonEmptyString: Newtype[String] with +// inline def validate(input: String): Boolean = +// input.nonEmpty + +// type MinorPerson = MinorPerson.Type +// given MinorPerson: Newtype[Person] with +// inline def validate(input: Person): Boolean = +// input.age < 18 + +// case class SampleNeo(name: NonEmptyString, label: String, unknown: MinorPerson) diff --git a/src/test/scala/co.blocke.scalajack/JsonDiff.scala b/src/test/scala/co.blocke.scalajack/JsonDiff.scala index acafcefa..7412ceed 100644 --- a/src/test/scala/co.blocke.scalajack/JsonDiff.scala +++ b/src/test/scala/co.blocke.scalajack/JsonDiff.scala @@ -1,25 +1,21 @@ package co.blocke.scalajack package json -import org.json4s.JsonAST.{ JNothing, JObject, JValue } +import org.json4s.JsonAST.{JNothing, JObject, JValue} object JsonDiff { - def compare( - left: JValue, - right: JValue, - leftLabel: String = "left", - rightLabel: String = "right"): Seq[JsonDiff] = { + def compare(left: JValue, right: JValue, leftLabel: String = "left", rightLabel: String = "right"): Seq[JsonDiff] = (left, right) match { case (JObject(leftFields), JObject(rightFields)) => val allFieldNames = (leftFields.map(_._1) ++ rightFields.map(_._1)).distinct allFieldNames.sorted flatMap { fieldName => val leftFieldValue = leftFields - .collectFirst({ case (`fieldName`, fieldValue) => fieldValue }) + .collectFirst { case (`fieldName`, fieldValue) => fieldValue } .getOrElse(JNothing) val rightFieldValue = rightFields - .collectFirst({ case (`fieldName`, fieldValue) => fieldValue }) + .collectFirst { case (`fieldName`, fieldValue) => fieldValue } .getOrElse(JNothing) compare(leftFieldValue, rightFieldValue, leftLabel, rightLabel) } @@ -35,7 +31,7 @@ object JsonDiff { // } case _ => - if (left == right) { + if left == right then { Seq.empty } else { val outerLeft = left @@ -48,7 +44,6 @@ object JsonDiff { }) } } - } } diff --git a/src/test/scala/co.blocke.scalajack/JsonMatcher.scala b/src/test/scala/co.blocke.scalajack/JsonMatcher.scala index acc73607..9531db62 100644 --- a/src/test/scala/co.blocke.scalajack/JsonMatcher.scala +++ b/src/test/scala/co.blocke.scalajack/JsonMatcher.scala @@ -2,60 +2,27 @@ package co.blocke.scalajack package json import org.json4s.JsonAST.JValue -import org.json4s.native.JsonMethods._ - -import munit.internal.MacroCompat -import munit.{Location, Compare} -import munit.internal.console.StackTraces -import munit.internal.difflib.ComparisonFailExceptionHandler - -// object JsonMatcher { - -// def jsonMatches( expected: String, actual: String ): Boolean = -// val diffs = JsonDiff.compare( -// parseJValue(expected), -// parseJValue(actual), -// "expected", -// "actual" -// ) -// diffs.isEmpty - -// implicit def parseJValue(string: String): JValue = parse(string) -// } - - -object BlockeUtil extends BlockeUtil -trait BlockeUtil extends MacroCompat.CompileErrorMacro with munit.Assertions: - - // val munitLines = new Lines - - // def munitAnsiColors: Boolean = true - +import org.json4s.native.JsonMethods.* + +import org.scalatest.* +import matchers.* + +trait JsonMatchers { + class JsonMatchesMatcher(expectedJson: String) extends Matcher[String]: + def apply(srcJson: String) = + val diffs = JsonDiff.compare( + parseJValue(srcJson), + parseJValue(expectedJson), + "expected", + "actual" + ) + MatchResult( + diffs.isEmpty, + s"""JSON values did not match""", + s"""JSON values matched""" + ) + + def matchJson(targetJson: String) = new JsonMatchesMatcher(targetJson) implicit def parseJValue(string: String): JValue = parse(string) - - /** - * Asserts that two JSON strings are equal according to the `Compare[A, B]` type-class. - * - * By default, uses `==` to compare values. - * - * JSON is unorderd, so two JSON strings are equal if they contain the same elements, in any order - */ - def jsonMatches( - obtained: String, - expected: String, - clue: => Any = "values are not the same" - )(implicit loc: Location, compare: Compare[String, String]): Unit = { - StackTraces.dropInside { - val areEqual = - val diffs = JsonDiff.compare( - parseJValue(obtained), - parseJValue(expected), - "obtained", - "expected" - ) - diffs.isEmpty - - if (!areEqual) - compare.failEqualsComparison(obtained, expected, clue, loc, this) - } - } \ No newline at end of file +} +object JsonMatchers extends JsonMatchers diff --git a/src/test/scala/co.blocke.scalajack/TestUtil.scala b/src/test/scala/co.blocke.scalajack/TestUtil.scala index c8365bdf..b8a5d5d5 100644 --- a/src/test/scala/co.blocke.scalajack/TestUtil.scala +++ b/src/test/scala/co.blocke.scalajack/TestUtil.scala @@ -1,19 +1,21 @@ package co.blocke.scalajack -import munit.internal.console +// import munit.internal.console object TestUtil { - inline def describe(message: String, color: String = Console.MAGENTA): Unit = println(s"$color$message${Console.RESET}") - inline def pending = describe(" << Test Pending (below) >>", Console.YELLOW) + // inline def describe(message: String, color: String = Console.MAGENTA): Unit = TestingConsole.out.println(s"$color$message${Console.RESET}") + // inline def pending = describe(" << Test Pending (below) >>", Console.YELLOW) + + inline def colorString(str: String, color: String = Console.MAGENTA): String = + str.split("\n").map(s => s"$color$s${Console.RESET}").mkString("\n") def hexStringToByteArray(s: String): Array[Byte] = { val len = s.length val data = new Array[Byte](len / 2) var i = 0 - while ({ - i < len - }) { + while i < len + do { data(i / 2) = ((Character.digit(s.charAt(i), 16) << 4) + Character.digit( s.charAt(i + 1), 16 @@ -26,17 +28,16 @@ object TestUtil { // Utility to generate test code quickly def showException(label: String, fnStr: String, fn: () => Any) = - try { + try fn() - } catch { + catch { case x: IndexOutOfBoundsException => throw x case t: Throwable => - if (!t.getMessage.contains("\n")) - throw t + if !t.getMessage.contains("\n") then throw t val msg = "\"\"\"" + t.getMessage().replace("\n", "\n |") + "\"\"\"" println( label + " >> " + t.getClass.getName + "\n-----------------------\n" + s"val msg = $msg.stripMargin\nthe[${t.getClass.getName}] thrownBy $fnStr should have message msg\n" ) } -} \ No newline at end of file +} diff --git a/src/test/scala/co.blocke.scalajack/json/primitives/Model.scala b/src/test/scala/co.blocke.scalajack/json/primitives/Model.scala index 451c9854..ae207055 100644 --- a/src/test/scala/co.blocke.scalajack/json/primitives/Model.scala +++ b/src/test/scala/co.blocke.scalajack/json/primitives/Model.scala @@ -2,29 +2,13 @@ package co.blocke.scalajack package json.primitives import java.util.UUID -import java.lang.{ - Boolean => JBoolean, - Byte => JByte, - Character => JChar, - Double => JDouble, - Float => JFloat, - Integer => JInt, - Long => JLong, - Number => JNumber, - Short => JShort -} -import java.math.{ BigDecimal => JBigDecimal, BigInteger => JBigInteger } -import java.time._ -import scala.math._ +import java.lang.{Boolean as JBoolean, Byte as JByte, Character as JChar, Double as JDouble, Float as JFloat, Integer as JInt, Long as JLong, Number as JNumber, Short as JShort} +import java.math.{BigDecimal as JBigDecimal, BigInteger as JBigInteger} +import java.time.* +import scala.math.* // === Scala -case class SampleBigDecimal( - bd1: BigDecimal, - bd2: BigDecimal, - bd3: BigDecimal, - bd4: BigDecimal, - bd5: BigDecimal, - bd6: BigDecimal) +case class SampleBigDecimal(bd1: BigDecimal, bd2: BigDecimal, bd3: BigDecimal, bd4: BigDecimal, bd5: BigDecimal, bd6: BigDecimal) case class SampleBigInt(bi1: BigInt, bi2: BigInt, bi3: BigInt, bi4: BigInt) case class SampleBinary(b1: Array[Byte], b2: Array[Byte]) case class SampleBoolean(bool1: Boolean, bool2: Boolean) @@ -39,19 +23,13 @@ object SizeWithType extends Enumeration { type SizeWithType = Value val Little, Grand = Value } -import SizeWithType._ -case class SampleEnum( - e1: Size.Value, - e2: Size.Value, - e3: Size.Value, - e4: Size.Value, - e5: Size.Value, - e6: SizeWithType) +import SizeWithType.* +case class SampleEnum(e1: Size.Value, e2: Size.Value, e3: Size.Value, e4: Size.Value, e5: Size.Value, e6: SizeWithType) enum Color { - case Red, Blue, Green + case Red, Blue, Green } -case class TVColors( color1: Color, color2: Color ) +case class TVColors(color1: Color, color2: Color) sealed trait Flavor case object Vanilla extends Flavor @@ -63,8 +41,8 @@ case class Truck(numberOfWheels: Int) extends Vehicle case class Car(numberOfWheels: Int, color: String) extends Vehicle case class Plane(numberOfEngines: Int) extends Vehicle -case class Ride( wheels: Vehicle ) -case class Favorite( flavor: Flavor ) +case class Ride(wheels: Vehicle) +case class Favorite(flavor: Flavor) case class SampleFloat(f1: Float, f2: Float, f3: Float, f4: Float) case class SampleInt(i1: Int, i2: Int, i3: Int, i4: Int) @@ -73,103 +51,27 @@ case class SampleShort(s1: Short, s2: Short, s3: Short, s4: Short) case class SampleString(s1: String, s2: String, s3: String) // === Java -case class SampleJBigDecimal( - bd1: JBigDecimal, - bd2: JBigDecimal, - bd3: JBigDecimal, - bd4: JBigDecimal, - bd5: JBigDecimal) -case class SampleJBigInteger( - bi1: JBigInteger, - bi2: JBigInteger, - bi3: JBigInteger, - bi4: JBigInteger, - bi5: JBigInteger, - bi6: JBigInteger, - bi7: JBigInteger) -case class SampleJBoolean( - bool1: JBoolean, - bool2: JBoolean, - bool3: JBoolean, - bool4: JBoolean, - bool5: JBoolean) +case class SampleJBigDecimal(bd1: JBigDecimal, bd2: JBigDecimal, bd3: JBigDecimal, bd4: JBigDecimal, bd5: JBigDecimal) +case class SampleJBigInteger(bi1: JBigInteger, bi2: JBigInteger, bi3: JBigInteger, bi4: JBigInteger, bi5: JBigInteger, bi6: JBigInteger, bi7: JBigInteger) +case class SampleJBoolean(bool1: JBoolean, bool2: JBoolean, bool3: JBoolean, bool4: JBoolean, bool5: JBoolean) case class SampleJByte(b1: JByte, b2: JByte, b3: JByte, b4: JByte, b5: JByte) case class SampleJChar(c1: JChar, c2: JChar, c3: JChar) -case class SampleJDouble( - d1: JDouble, - d2: JDouble, - d3: JDouble, - d4: JDouble, - d5: JDouble) -case class SampleJFloat( - f1: JFloat, - f2: JFloat, - f3: JFloat, - f4: JFloat, - f5: JFloat) +case class SampleJDouble(d1: JDouble, d2: JDouble, d3: JDouble, d4: JDouble, d5: JDouble) +case class SampleJFloat(f1: JFloat, f2: JFloat, f3: JFloat, f4: JFloat, f5: JFloat) case class SampleJInt(i1: JInt, i2: JInt, i3: JInt, i4: JInt, i5: JInt) case class SampleJLong(l1: JLong, l2: JLong, l3: JLong, l4: JLong, l5: JLong) -case class SampleJNumber( - n1: JNumber, - n2: JNumber, - n3: JNumber, - n4: JNumber, - n5: JNumber, - n6: JNumber, - n7: JNumber, - n8: JNumber, - n9: JNumber, - n10: JNumber, - n11: JNumber, - n12: JNumber, - n13: JNumber, - n14: JNumber, - n15: JNumber, - n16: JNumber, - n17: JNumber) -case class SampleJShort( - s1: JShort, - s2: JShort, - s3: JShort, - s4: JShort, - s5: JShort) +case class SampleJNumber(n1: JNumber, n2: JNumber, n3: JNumber, n4: JNumber, n5: JNumber, n6: JNumber, n7: JNumber, n8: JNumber, n9: JNumber, n10: JNumber, n11: JNumber, n12: JNumber, n13: JNumber, n14: JNumber, n15: JNumber, n16: JNumber, n17: JNumber) +case class SampleJShort(s1: JShort, s2: JShort, s3: JShort, s4: JShort, s5: JShort) case class SampleUUID(u1: UUID, u2: UUID) // === Java Time case class SampleDuration(d1: Duration, d2: Duration, d3: Duration) -case class SampleInstant( - i1: Instant, - i2: Instant, - i3: Instant, - i4: Instant, - i5: Instant) -case class SampleLocalDateTime( - d1: LocalDateTime, - d2: LocalDateTime, - d3: LocalDateTime, - d4: LocalDateTime) -case class SampleLocalDate( - d1: LocalDate, - d2: LocalDate, - d3: LocalDate, - d4: LocalDate) -case class SampleLocalTime( - d1: LocalTime, - d2: LocalTime, - d3: LocalTime, - d4: LocalTime, - d5: LocalTime, - d6: LocalTime) -case class SampleOffsetDateTime( - o1: OffsetDateTime, - o2: OffsetDateTime, - o3: OffsetDateTime, - o4: OffsetDateTime) -case class SampleOffsetTime( - o1: OffsetTime, - o2: OffsetTime, - o3: OffsetTime, - o4: OffsetTime) +case class SampleInstant(i1: Instant, i2: Instant, i3: Instant, i4: Instant, i5: Instant) +case class SampleLocalDateTime(d1: LocalDateTime, d2: LocalDateTime, d3: LocalDateTime, d4: LocalDateTime) +case class SampleLocalDate(d1: LocalDate, d2: LocalDate, d3: LocalDate, d4: LocalDate) +case class SampleLocalTime(d1: LocalTime, d2: LocalTime, d3: LocalTime, d4: LocalTime, d5: LocalTime, d6: LocalTime) +case class SampleOffsetDateTime(o1: OffsetDateTime, o2: OffsetDateTime, o3: OffsetDateTime, o4: OffsetDateTime) +case class SampleOffsetTime(o1: OffsetTime, o2: OffsetTime, o3: OffsetTime, o4: OffsetTime) case class SamplePeriod(p1: Period, p2: Period, p3: Period) case class SampleZonedDateTime(o1: ZonedDateTime, o2: ZonedDateTime) @@ -194,4 +96,4 @@ case class VCUUID(vc: UUID) extends AnyVal case class VCNumber(vc: Number) extends AnyVal // === Permissives test -case class Holder[T](value: T) \ No newline at end of file +case class Holder[T](value: T) diff --git a/src/test/scala/co.blocke.scalajack/json/primitives/ScalaPrim.scala b/src/test/scala/co.blocke.scalajack/json/primitives/ScalaPrim.scala index b33705c0..b0eb163b 100644 --- a/src/test/scala/co.blocke.scalajack/json/primitives/ScalaPrim.scala +++ b/src/test/scala/co.blocke.scalajack/json/primitives/ScalaPrim.scala @@ -2,328 +2,301 @@ package co.blocke.scalajack package json package primitives -import co.blocke.scala_reflection._ +import co.blocke.scala_reflection.* import scala.math.BigDecimal import java.util.UUID -import TestUtil._ -import munit._ -import munit.internal.console +import TestUtil.* +// import munit.* +// import munit.internal.console +import org.scalatest.funspec.AnyFunSpec +import org.scalatest.matchers.should.Matchers.* +import org.scalatest.* -class ScalaPrim() extends FunSuite with BlockeUtil: +class ScalaPrim() extends AnyFunSpec with JsonMatchers: - test("Introduction") { - describe("---------------------------\n: Scala Primitive Tests :\n---------------------------", Console.YELLOW) - describe("+++ Positive Tests +++") - } + describe(colorString("---------------------------\n: Scala Primitive Tests :\n---------------------------", Console.YELLOW)) { + describe(colorString("+++ Positive Tests +++")) { + it("BigDecimal must work") { + val inst = SampleBigDecimal( + BigDecimal(123L), + BigDecimal(1.23), + BigDecimal(0), + BigDecimal("123.456"), + BigDecimal( + "0.1499999999999999944488848768742172978818416595458984375" + ), + null + ) - test("BigDecimal must work") { - val inst = SampleBigDecimal( - BigDecimal(123L), - BigDecimal(1.23), - BigDecimal(0), - BigDecimal("123.456"), - BigDecimal( - "0.1499999999999999944488848768742172978818416595458984375" - ), - null - ) + val js = ScalaJack.write(inst) + js should matchJson("""{"bd1":123,"bd2":1.23,"bd3":0,"bd4":123.456,"bd5":0.1499999999999999944488848768742172978818416595458984375,"bd6":null}""") + inst shouldEqual ScalaJack.read[SampleBigDecimal](js) + } - val js = ScalaJack.write(inst) - assertEquals(js, - """{"bd1":123,"bd2":1.23,"bd3":0,"bd4":123.456,"bd5":0.1499999999999999944488848768742172978818416595458984375,"bd6":null}""" - ) - assertEquals(inst, ScalaJack.read[SampleBigDecimal](js)) - } + it("BigInt must work") { + val inst = SampleBigInt( + BigInt("-90182736451928374653345"), + BigInt("90182736451928374653345"), + BigInt(0), + null + ) + val js = ScalaJack.write(inst) + js should matchJson("""{"bi1":-90182736451928374653345,"bi2":90182736451928374653345,"bi3":0,"bi4":null}""") + inst shouldEqual ScalaJack.read[SampleBigInt](js) + } - test("BigInt must work") { - val inst = SampleBigInt( - BigInt("-90182736451928374653345"), - BigInt("90182736451928374653345"), - BigInt(0), - null - ) - val js = ScalaJack.write(inst) - assertEquals(js, - """{"bi1":-90182736451928374653345,"bi2":90182736451928374653345,"bi3":0,"bi4":null}""" - ) - assertEquals(inst, ScalaJack.read[SampleBigInt](js)) - } + it("Binary must work") { + val inst = SampleBinary( + null, + hexStringToByteArray("e04fd020ea3a6910a2d808002b30309d") + ) + val js = ScalaJack.write(inst) + val inst2 = ScalaJack.read[SampleBinary](js) + js should matchJson("""{"b1":null,"b2":[-32,79,-48,32,-22,58,105,16,-94,-40,8,0,43,48,48,-99]}""") + inst2.b1 shouldEqual null + inst.b2.toList shouldEqual inst2.b2.toList + } - test("Binary must work") { - val inst = SampleBinary( - null, - hexStringToByteArray("e04fd020ea3a6910a2d808002b30309d") - ) - val js = ScalaJack.write(inst) - val inst2 = ScalaJack.read[SampleBinary](js) - assert(null == inst2.b1) - assertEquals(inst.b2.toList, inst2.b2.toList) - } + it("Boolean must work (not nullable)") { + val inst = SampleBoolean(bool1 = true, bool2 = false) + val js = ScalaJack.write(inst) + js should matchJson("""{"bool1":true,"bool2":false}""") + inst shouldEqual ScalaJack.read[SampleBoolean](js) + } - test("Boolean must work (not nullable)") { - val inst = SampleBoolean(bool1 = true, bool2 = false) - val js = ScalaJack.write(inst) - jsonMatches(js, """{"bool1":true,"bool2":false}""") - assertEquals(inst, ScalaJack.read[SampleBoolean](js)) - } + it("Byte must work (not nullable)") { + val inst = SampleByte(Byte.MaxValue, Byte.MinValue, 0, 64) + val js = ScalaJack.write(inst) + js should matchJson("""{"b1":127,"b2":-128,"b3":0,"b4":64}""") + inst shouldEqual ScalaJack.read[SampleByte](js) + } - test("Byte must work (not nullable)") { - val inst = SampleByte(Byte.MaxValue, Byte.MinValue, 0, 64) - val js = ScalaJack.write(inst) - jsonMatches(js, """{"b1":127,"b2":-128,"b3":0,"b4":64}""") - assertEquals(inst, ScalaJack.read[SampleByte](js)) - } + it("Char must work (not nullable)") { + val inst = SampleChar(Char.MaxValue, 'Z', '\u20A0') + val js = ScalaJack.write(inst) + js should matchJson("""{"c1":"\""" + """uffff","c2":"Z","c3":"\""" + """u20a0"}""") + inst shouldEqual ScalaJack.read[SampleChar](js) + } - test("Char must work (not nullable)") { - val inst = SampleChar(Char.MaxValue, 'Z', '\u20A0') - val js = ScalaJack.write(inst) - jsonMatches(js,"""{"c1":"\""" + """uffff","c2":"Z","c3":"\""" + """u20a0"}""") - assertEquals(inst, ScalaJack.read[SampleChar](js)) - } + it("Double must work (not nullable)") { + val inst = + SampleDouble(Double.MaxValue, Double.MinValue, 0.0, -123.4567) + val js = ScalaJack.write(inst) + js should matchJson("""{"d1":1.7976931348623157E308,"d2":-1.7976931348623157E308,"d3":0.0,"d4":-123.4567}""") + inst shouldEqual ScalaJack.read[SampleDouble](js) + } - test("Double must work (not nullable)") { - val inst = - SampleDouble(Double.MaxValue, Double.MinValue, 0.0, -123.4567) - val js = ScalaJack.write(inst) - jsonMatches(js, - """{"d1":1.7976931348623157E308,"d2":-1.7976931348623157E308,"d3":0.0,"d4":-123.4567}""") - assertEquals(inst, ScalaJack.read[SampleDouble](js)) - } + it("Float must work") { + val inst = SampleFloat(Float.MaxValue, Float.MinValue, 0.0f, -123.4567f) + val js = ScalaJack.write(inst) + js should matchJson("""{"f1":3.4028235E38,"f2":-3.4028235E38,"f3":0.0,"f4":-123.4567}""") + inst shouldEqual ScalaJack.read[SampleFloat](js) + } - test("Float must work") { - val inst = SampleFloat(Float.MaxValue, Float.MinValue, 0.0F, -123.4567F) - val js = ScalaJack.write(inst) - jsonMatches(js, - """{"f1":3.4028235E38,"f2":-3.4028235E38,"f3":0.0,"f4":-123.4567}""") - assertEquals(inst, ScalaJack.read[SampleFloat](js)) - } + it("Int must work (not nullable)") { + val inst = SampleInt(Int.MaxValue, Int.MinValue, 0, 123) + val js = ScalaJack.write(inst) + js should matchJson("""{"i1":2147483647,"i2":-2147483648,"i3":0,"i4":123}""") + inst shouldEqual ScalaJack.read[SampleInt](js) + } - test("Int must work (not nullable)") { - val inst = SampleInt(Int.MaxValue, Int.MinValue, 0, 123) - val js = ScalaJack.write(inst) - jsonMatches(js, """{"i1":2147483647,"i2":-2147483648,"i3":0,"i4":123}""") - assertEquals(inst, ScalaJack.read[SampleInt](js)) - } + it("Long must work (not nullable)") { + val inst = SampleLong(Long.MaxValue, Long.MinValue, 0L, 123L) + val js = ScalaJack.write(inst) + js should matchJson("""{"l1":9223372036854775807,"l2":-9223372036854775808,"l3":0,"l4":123}""") + inst shouldEqual ScalaJack.read[SampleLong](js) + } - test("Long must work (not nullable)") { - val inst = SampleLong(Long.MaxValue, Long.MinValue, 0L, 123L) - val js = ScalaJack.write(inst) - jsonMatches(js, - """{"l1":9223372036854775807,"l2":-9223372036854775808,"l3":0,"l4":123}""") - assertEquals(inst, ScalaJack.read[SampleLong](js)) - } + it("Short must work (not nullable)") { + val inst = SampleShort(Short.MaxValue, Short.MinValue, 0, 123) + val js = ScalaJack.write(inst) + js should matchJson("""{"s1":32767,"s2":-32768,"s3":0,"s4":123}""") + inst shouldEqual ScalaJack.read[SampleShort](js) + } - test("Short must work (not nullable)") { - val inst = SampleShort(Short.MaxValue, Short.MinValue, 0, 123) - val js = ScalaJack.write(inst) - jsonMatches(js,"""{"s1":32767,"s2":-32768,"s3":0,"s4":123}""") - assertEquals(inst, ScalaJack.read[SampleShort](js)) - } + it("String must work") { + val inst = SampleString("something\b\n\f\r\t☆", "", null) + val js = ScalaJack.write(inst) + // The weird '+' here is to break up the unicode so it won't be interpreted and wreck the test. + js should matchJson("""{"s1":"something\b\n\f\r\t\""" + """u2606","s2":"","s3":null}""") + inst shouldEqual ScalaJack.read[SampleString](js) + } - test("String must work") { - val inst = SampleString("something\b\n\f\r\t☆", "", null) - val js = ScalaJack.write(inst) - // The weird '+' here is to break up the unicode so it won't be interpreted and wreck the test. - jsonMatches(js, - """{"s1":"something\b\n\f\r\t\""" + """u2606","s2":"","s3":null}""") - assertEquals(inst, ScalaJack.read[SampleString](js)) - } - - test("UUID must work") { - val inst = SampleUUID( - null, - UUID.fromString("580afe0d-81c0-458f-9e09-4486c7af0fe9") - ) - val js = ScalaJack.write(inst) - jsonMatches(js, - """{"u1":null,"u2":"580afe0d-81c0-458f-9e09-4486c7af0fe9"}""") - assertEquals(inst, ScalaJack.read[SampleUUID](js)) - } + it("UUID must work") { + val inst = SampleUUID( + null, + UUID.fromString("580afe0d-81c0-458f-9e09-4486c7af0fe9") + ) + val js = ScalaJack.write(inst) + js should matchJson("""{"u1":null,"u2":"580afe0d-81c0-458f-9e09-4486c7af0fe9"}""") + inst shouldEqual ScalaJack.read[SampleUUID](js) + } + } + // -------------------------------------------------------- - //-------------------------------------------------------- + describe(colorString("--- Negative Tests ---")) { + it("BigDecimal must break") { + val js = + """{"bd1":123,"bd2":1.23,"bd3":0,"bd4":123.456,"bd5":"0.1499999999999999944488848768742172978818416595458984375","bd6":null}""" + val msg: String = + """Float/Double expected but couldn't parse from """" + "\"" + """" at position [50] + |{"bd1":123,"bd2":1.23,"bd3":0,"bd4":123.456,"bd5":"0.149999999999999994448884... + |--------------------------------------------------^""".stripMargin + val thrown = the[JsonParseError] thrownBy ScalaJack.read[SampleBigDecimal](js) + thrown.getMessage should equal(msg) + } -/* - test("BigDecimal must break") { - describe("--- Negative Tests ---") - val js = - """{"bd1":123,"bd2":1.23,"bd3":0,"bd4":123.456,"bd5":"0.1499999999999999944488848768742172978818416595458984375","bd6":null}""" - val msg = - """Expected a Number here - |{"bd1":123,"bd2":1.23,"bd3":0,"bd4":123.456,"bd5":"0.149999999999999994448884... - |--------------------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - ScalaJack.read[SampleBigDecimal](js) - } - } + it("BigInt must break") { + val js = + """{"bi1":"-90182736451928374653345","bi2":90182736451928374653345,"bi3":0,"bi4":null}""" + val msg = + """Int/Long expected but couldn't parse from """" + "\"" + """" at position [7] + |{"bi1":"-90182736451928374653345","bi2":90182736451928374653345,"bi3":0,"bi4"... + |-------^""".stripMargin + val thrown = the[JsonParseError] thrownBy ScalaJack.read[SampleBigInt](js) + thrown.getMessage should equal(msg) + } - test("BigInt must break") { - val js = - """{"bi1":"-90182736451928374653345","bi2":90182736451928374653345,"bi3":0,"bi4":null}""" - val msg = - """Expected a Number here - |{"bi1":"-90182736451928374653345","bi2":90182736451928374653345,"bi3":0,"bi4"... - |-------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - ScalaJack.read[SampleBigInt](js) - } - } - - test("Boolean must break") { - val js = """{"bool1":true,"bool2":"false"}""" - val msg = """Expected a Boolean here - |{"bool1":true,"bool2":"false"} - |----------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - ScalaJack.read[SampleBoolean](js) - } - val js2 = """{"bool1":true,"bool2":123}""" - val msg2 = """Expected a Boolean here - |{"bool1":true,"bool2":123} - |----------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ - ScalaJack.read[SampleBoolean](js2) - } - val js3 = """{"bool1":true,"bool2":null}""" - val msg3 = """Expected a Boolean here - |{"bool1":true,"bool2":null} - |----------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg3){ - ScalaJack.read[SampleBoolean](js3) - } - } + it("Boolean must break") { + val js = """{"bool1":true,"bool2":"false"}""" + val msg = """Unexpected character '"' where beginning of boolean value expected at position [22] + |{"bool1":true,"bool2":"false"} + |----------------------^""".stripMargin + val thrown = the[JsonParseError] thrownBy ScalaJack.read[SampleBoolean](js) + thrown.getMessage should equal(msg) + val js2 = """{"bool1":true,"bool2":123}""" + val msg2 = """Unexpected character '1' where beginning of boolean value expected at position [22] + |{"bool1":true,"bool2":123} + |----------------------^""".stripMargin + val thrown2 = the[JsonParseError] thrownBy ScalaJack.read[SampleBoolean](js2) + thrown2.getMessage should equal(msg2) + val js3 = """{"bool1":true,"bool2":null}""" + val msg3 = """Unexpected character 'n' where beginning of boolean value expected at position [22] + |{"bool1":true,"bool2":null} + |----------------------^""".stripMargin + val thrown3 = the[JsonParseError] thrownBy ScalaJack.read[SampleBoolean](js3) + thrown3.getMessage should equal(msg3) + } - test("Byte must break") { - val js = """{"b1":true,"b2":-128,"b3":0,"b4":64}""" - val msg = """Expected a Number here - |{"b1":true,"b2":-128,"b3":0,"b4":64} - |------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - ScalaJack.read[SampleByte](js) - } - val js2 = """{"b1":12,"b2":-128,"b3":0,"b4":null}""" - val msg2 = """Expected a Number here - |{"b1":12,"b2":-128,"b3":0,"b4":null} - |-------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ - ScalaJack.read[SampleByte](js2) - } - } + it("Byte must break") { + val js = """{"b1":true,"b2":-128,"b3":0,"b4":64}""" + val msg = """Int/Long expected but couldn't parse from "t" at position [6] + |{"b1":true,"b2":-128,"b3":0,"b4":64} + |------^""".stripMargin + val thrown = the[JsonParseError] thrownBy ScalaJack.read[SampleByte](js) + thrown.getMessage should equal(msg) + val js2 = """{"b1":12,"b2":-128,"b3":0,"b4":null}""" + val msg2 = """Int/Long expected but couldn't parse from "n" at position [31] + |{"b1":12,"b2":-128,"b3":0,"b4":null} + |-------------------------------^""".stripMargin + val thrown2 = the[JsonParseError] thrownBy ScalaJack.read[SampleByte](js2) + thrown2.getMessage should equal(msg2) + } - test("Char must break") { - val js = """{"c1":null,"c2":"Y","c3":"Z"}""" - val msg = """A Char typed value cannot be null - |{"c1":null,"c2":"Y","c3":"Z"} - |---------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - ScalaJack.read[SampleChar](js) - } - val js2 = """{"c1":"","c2":"Y","c3":"Z"}""" - val msg2 = """Tried to read a Char but empty string found - |{"c1":"","c2":"Y","c3":"Z"} - |-------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ - ScalaJack.read[SampleChar](js2) - } - } + it("Char must break") { + val js = """{"c1":null,"c2":"Y","c3":"Z"}""" + val msg = """Char typed values cannot be null at position [10] + |{"c1":null,"c2":"Y","c3":"Z"} + |----------^""".stripMargin + val thrown = the[JsonParseError] thrownBy ScalaJack.read[SampleChar](js) + thrown.getMessage should equal(msg) + val js2 = """{"c1":"","c2":"Y","c3":"Z"}""" + val msg2 = """Cannot convert value '' into a Char at position [8] + |{"c1":"","c2":"Y","c3":"Z"} + |--------^""".stripMargin + val thrown2 = the[JsonParseError] thrownBy ScalaJack.read[SampleChar](js2) + thrown2.getMessage should equal(msg2) + } - test("Double must break") { - val js = - """{"d1":1.79769313486E23157E308,"d2":-1.7976931348623157E308,"d3":0.0,"d4":-123.4567}""" - val msg = - """Cannot parse an Double from value - |{"d1":1.79769313486E23157E308,"d2":-1.7976931348623157E308,"d3":0.0,"d4":-123... - |----------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - ScalaJack.read[SampleDouble](js) - } - } + it("Double must break") { + val js = + """{"d1":1.79769313486E23157E308,"d2":-1.7976931348623157E308,"d3":0.0,"d4":-123.4567}""" + val msg = + """Float/Double expected but couldn't parse from "1.79769313486E23157E308" at position [29] + |{"d1":1.79769313486E23157E308,"d2":-1.7976931348623157E308,"d3":0.0,"d4":-123... + |------^""".stripMargin + val thrown = the[JsonParseError] thrownBy ScalaJack.read[SampleDouble](js) + thrown.getMessage should equal(msg) + } - test("Float must break") { - val js = - """{"f1":3.4028235E38,"f2":"-3.4028235E38","f3":0.0,"f4":-123.4567}""" - val msg = - """Expected a Number here - |{"f1":3.4028235E38,"f2":"-3.4028235E38","f3":0.0,"f4":-123.4567} - |------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - ScalaJack.read[SampleFloat](js) - } - } + it("Float must break") { + val js = + """{"f1":3.4028235E38,"f2":"-3.4028235E38","f3":0.0,"f4":-123.4567}""" + val msg = + """Float/Double expected but couldn't parse from """" + "\"" + """" at position [24] + |{"f1":3.4028235E38,"f2":"-3.4028235E38","f3":0.0,"f4":-123.4567} + |------------------------^""".stripMargin + val thrown = the[JsonParseError] thrownBy ScalaJack.read[SampleFloat](js) + thrown.getMessage should equal(msg) + } - test("Int must break") { - val js = """{"i1":2147483647,"i2":-2147483648,"i3":"0","i4":123}""" - val msg = """Expected a Number here - |{"i1":2147483647,"i2":-2147483648,"i3":"0","i4":123} - |---------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - ScalaJack.read[SampleInt](js) - } - val js2 = """{"i1":2147483647,"i2":-2147483648,"i3":2.3,"i4":123}""" - val msg2 = """Cannot parse an Int from value - |{"i1":2147483647,"i2":-2147483648,"i3":2.3,"i4":123} - |-----------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ - ScalaJack.read[SampleInt](js2) - } - } + it("Int must break") { + val js = """{"i1":2147483647,"i2":-2147483648,"i3":"0","i4":123}""" + val msg = """Int/Long expected but couldn't parse from """" + "\"" + """" at position [39] + |{"i1":2147483647,"i2":-2147483648,"i3":"0","i4":123} + |---------------------------------------^""".stripMargin + val thrown = the[JsonParseError] thrownBy ScalaJack.read[SampleInt](js) + thrown.getMessage should equal(msg) + val js2 = """{"i1":2147483647,"i2":-2147483648,"i3":2.3,"i4":123}""" + val msg2 = """Comma expected at position [40] + |{"i1":2147483647,"i2":-2147483648,"i3":2.3,"i4":123} + |----------------------------------------^""".stripMargin + val thrown2 = the[CommaExpected] thrownBy ScalaJack.read[SampleInt](js2) + thrown2.getMessage should equal(msg2) + } - test("Long must break") { - val js = - """{"l1":9223372036854775807,"l2":-9223372036854775808,"l3":true,"l4":123}""" - val msg = - """Expected a Number here - |...23372036854775807,"l2":-9223372036854775808,"l3":true,"l4":123} - |----------------------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - ScalaJack.read[SampleLong](js) - } - val js2 = - """{"l1":9223372036854775807,"l2":-9223372036854775808,"l3":0.3,"l4":123}""" - val msg2 = - """Cannot parse an Long from value - |...372036854775807,"l2":-9223372036854775808,"l3":0.3,"l4":123} - |----------------------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ - ScalaJack.read[SampleLong](js2) - } - } + it("Long must break") { + val js = + """{"l1":9223372036854775807,"l2":-9223372036854775808,"l3":true,"l4":123}""" + val msg = + """Int/Long expected but couldn't parse from "t" at position [57] + |...23372036854775807,"l2":-9223372036854775808,"l3":true,"l4":123} + |----------------------------------------------------^""".stripMargin + val thrown = the[JsonParseError] thrownBy ScalaJack.read[SampleLong](js) + thrown.getMessage should equal(msg) + val js2 = + """{"l1":9223372036854775807,"l2":-9223372036854775808,"l3":0.3,"l4":123}""" + val msg2 = + """Comma expected at position [58] + |...3372036854775807,"l2":-9223372036854775808,"l3":0.3,"l4":123} + |----------------------------------------------------^""".stripMargin + val thrown2 = the[CommaExpected] thrownBy ScalaJack.read[SampleLong](js2) + thrown2.getMessage should equal(msg2) + } - test("Short must break") { - val js = """{"s1":32767,"s2":true,"s3":0,"s4":123}""" - val msg = """Expected a Number here - |{"s1":32767,"s2":true,"s3":0,"s4":123} - |-----------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - ScalaJack.read[SampleShort](js) - } - val js2 = """{"s1":32767,"s2":3.4,"s3":0,"s4":123}""" - val msg2 = """Cannot parse an Short from value - |{"s1":32767,"s2":3.4,"s3":0,"s4":123} - |-------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ - ScalaJack.read[SampleShort](js2) - } - } + it("Short must break") { + val js = """{"s1":32767,"s2":true,"s3":0,"s4":123}""" + val msg = """Int/Long expected but couldn't parse from "t" at position [17] + |{"s1":32767,"s2":true,"s3":0,"s4":123} + |-----------------^""".stripMargin + val thrown = the[JsonParseError] thrownBy ScalaJack.read[SampleShort](js) + thrown.getMessage should equal(msg) + val js2 = """{"s1":32767,"s2":3.4,"s3":0,"s4":123}""" + val msg2 = """Comma expected at position [18] + |{"s1":32767,"s2":3.4,"s3":0,"s4":123} + |------------------^""".stripMargin + val thrown2 = the[CommaExpected] thrownBy ScalaJack.read[SampleShort](js2) + thrown2.getMessage should equal(msg2) + } - test("String must break") { - val js = """{"s1":"something","s2":-19,"s3":null}""" - val msg = """Expected a String here - |{"s1":"something","s2":-19,"s3":null} - |-----------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - ScalaJack.read[SampleString](js) - } - } + it("String must break") { + val js = """{"s1":"something","s2":-19,"s3":null}""" + val msg = """Unexpected character '-' where beginning of a string expected at position [23] + |{"s1":"something","s2":-19,"s3":null} + |-----------------------^""".stripMargin + val thrown = the[JsonParseError] thrownBy ScalaJack.read[SampleString](js) + thrown.getMessage should equal(msg) + } - test("UUID must break") { - val js = - """{"u1":"bogus","u2":"580afe0d-81c0-458f-9e09-4486c7af0fe9"}""" - val msg = """Failed to create UUID value from parsed text bogus - |{"u1":"bogus","u2":"580afe0d-81c0-458f-9e09-4486c7af0fe9"} - |------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - ScalaJack.read[SampleUUID](js) + it("UUID must break") { + val js = + """{"u1":"bogus","u2":"580afe0d-81c0-458f-9e09-4486c7af0fe9"}""" + val msg = """Unable to marshal UUID from value 'bogus' at position [13] + |{"u1":"bogus","u2":"580afe0d-81c0-458f-9e09-4486c7af0fe9"} + |-------------^""".stripMargin + val thrown = the[JsonParseError] thrownBy ScalaJack.read[SampleUUID](js) + thrown.getMessage should equal(msg) + } } } -*/ \ No newline at end of file From 03e76d52effecd689209111e422bcda513ad1fd2 Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Sun, 5 Nov 2023 10:22:59 -0600 Subject: [PATCH 16/65] fix benchmark table --- benchmark/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmark/README.md b/benchmark/README.md index d8cb4251..2dac5479 100644 --- a/benchmark/README.md +++ b/benchmark/README.md @@ -16,7 +16,7 @@ sbt "jmh:run -i 10 -wi 10 -f 2 -t 1 co.blocke.WritingBenchmark" ## Writing Performance: | Benchmark | Mode | Cnt | Score | Error | Units | -|------------------|-------|-----|-------------|--------------|-------| +|------------------|-------|----:|------------:|-------------:|-------| | Hand-Tooled | thrpt | 20 | 2575393.513 | ± 178731.952 | ops/s | | Circe | thrpt | 20 | 1939339.085 | ± 6279.547 | ops/s | | ScalaJack 8 | thrpt | 20 | 1703256.521 | ± 12260.518 | ops/s | From 194117ca1f6c8323408b067251a59d6696f3af87 Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Sun, 5 Nov 2023 10:26:02 -0600 Subject: [PATCH 17/65] more fixes --- benchmark/README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/benchmark/README.md b/benchmark/README.md index 2dac5479..39314635 100644 --- a/benchmark/README.md +++ b/benchmark/README.md @@ -17,13 +17,13 @@ sbt "jmh:run -i 10 -wi 10 -f 2 -t 1 co.blocke.WritingBenchmark" | Benchmark | Mode | Cnt | Score | Error | Units | |------------------|-------|----:|------------:|-------------:|-------| -| Hand-Tooled | thrpt | 20 | 2575393.513 | ± 178731.952 | ops/s | -| Circe | thrpt | 20 | 1939339.085 | ± 6279.547 | ops/s | -| ScalaJack 8 | thrpt | 20 | 1703256.521 | ± 12260.518 | ops/s | -| ZIO JSON | thrpt | 20 | 818228.736 | ± 3070.298 | ops/s | -| Argonaut | thrpt | 20 | 716228.404 | ± 6241.145 | ops/s | -| Play JSON | thrpt | 20 | 438538.475 | ± 16319.198 | ops/s | -| ScalaJack 7 | thrpt | 20 | 106292.338 | ± 330.111 | ops/s | +| Hand-Tooled | thrpt | 20 | 2,575,393.513 | ± 178731.952 | ops/s | +| Circe | thrpt | 20 | 1,939,339.085 | ± 6279.547 | ops/s | +|** ScalaJack 8 | thrpt | 20 | 1,703,256.521 | ± 12260.518 | ops/s** | +| ZIO JSON | thrpt | 20 | 818,228.736 | ± 3070.298 | ops/s | +| Argonaut | thrpt | 20 | 716,228.404 | ± 6241.145 | ops/s | +| Play JSON | thrpt | 20 | 438,538.475 | ± 16319.198 | ops/s | +| ScalaJack 7 | thrpt | 20 | 106,292.338 | ± 330.111 | ops/s | ### Writing Interpretation From 6a47401fb4f332cf84c9b973ece45db230054664 Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Sun, 5 Nov 2023 10:26:42 -0600 Subject: [PATCH 18/65] more fixes --- benchmark/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmark/README.md b/benchmark/README.md index 39314635..a0fa946e 100644 --- a/benchmark/README.md +++ b/benchmark/README.md @@ -19,7 +19,7 @@ sbt "jmh:run -i 10 -wi 10 -f 2 -t 1 co.blocke.WritingBenchmark" |------------------|-------|----:|------------:|-------------:|-------| | Hand-Tooled | thrpt | 20 | 2,575,393.513 | ± 178731.952 | ops/s | | Circe | thrpt | 20 | 1,939,339.085 | ± 6279.547 | ops/s | -|** ScalaJack 8 | thrpt | 20 | 1,703,256.521 | ± 12260.518 | ops/s** | +|**ScalaJack 8** | thrpt | 20 | **1,703,256.521** | ± 12260.518 | ops/s | | ZIO JSON | thrpt | 20 | 818,228.736 | ± 3070.298 | ops/s | | Argonaut | thrpt | 20 | 716,228.404 | ± 6241.145 | ops/s | | Play JSON | thrpt | 20 | 438,538.475 | ± 16319.198 | ops/s | From 7e3bd6b359937a41a05f4289be7ee4bfceeb18b9 Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Sun, 5 Nov 2023 18:22:49 -0600 Subject: [PATCH 19/65] more write types --- benchmark/README.md | 2 +- .../co.blocke.scalajack/json/JsonConfig.scala | 6 +- .../co.blocke.scalajack/json/JsonWriter.scala | 121 ++++++++- .../json/JsonWriter.scalax | 9 +- .../json/JsonWriter2.scalax | 251 ------------------ .../json/JsonWriterRT.scala | 160 +++++++---- .../scala/co.blocke.scalajack/run/Play.scala | 18 +- .../co.blocke.scalajack/run/Sample.scala | 16 +- 8 files changed, 242 insertions(+), 341 deletions(-) delete mode 100644 src/main/scala/co.blocke.scalajack/json/JsonWriter2.scalax diff --git a/benchmark/README.md b/benchmark/README.md index a0fa946e..7d0de445 100644 --- a/benchmark/README.md +++ b/benchmark/README.md @@ -15,7 +15,7 @@ sbt "jmh:run -i 10 -wi 10 -f 2 -t 1 co.blocke.WritingBenchmark" ## Writing Performance: -| Benchmark | Mode | Cnt | Score | Error | Units | +| Benchmark | Mode | Count | Score | Error | Units | |------------------|-------|----:|------------:|-------------:|-------| | Hand-Tooled | thrpt | 20 | 2,575,393.513 | ± 178731.952 | ops/s | | Circe | thrpt | 20 | 1,939,339.085 | ± 6279.547 | ops/s | diff --git a/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala b/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala index 5aa07adb..8b245e40 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala @@ -5,6 +5,7 @@ case class JsonConfig( noneAsNull: Boolean = false, forbidNullsInInput: Boolean = false, tryFailureHandling: TryOption = TryOption.NO_WRITE, + undefinedFieldHandling: UndefinedValueOption = UndefinedValueOption.THROW_EXCEPTION, permissivePrimitives: Boolean = false, // -------------------------- typeHintLabel: String = "_hint", @@ -16,4 +17,7 @@ case class JsonConfig( ) enum TryOption: - case AS_NULL, NO_WRITE, ERR_MSG_STRING + case AS_NULL, NO_WRITE, ERR_MSG_STRING, THROW_EXCEPTION + +enum UndefinedValueOption: + case AS_NULL, AS_SYMBOL, THROW_EXCEPTION diff --git a/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala b/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala index d78bc15a..0de3fc7b 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala @@ -3,7 +3,7 @@ package json import scala.quoted.* import co.blocke.scala_reflection.* -import co.blocke.scala_reflection.rtypes.{ScalaClassRType, TraitRType} +import co.blocke.scala_reflection.rtypes.{EnumRType, ScalaClassRType, TraitRType} import co.blocke.scala_reflection.reflect.{ReflectOnType, TypeSymbolMapper} import co.blocke.scala_reflection.reflect.rtypeRefs.* import scala.util.{Failure, Success, Try} @@ -13,21 +13,21 @@ import scala.quoted.staging.* TODO: [ ] - Scala non-case class [ ] - Java class (Do I still want to support this???) - [ ] - Enum - [ ] - Enumeration - [ ] - Java Enum + [*] - Enum + [*] - Enumeration + [*] - Java Enum [ ] - Java Collections [ ] - Java Map - [ ] - Intersection - [ ] - Union - [ ] - Either - [ ] - Object (How???) + [*] - Intersection + [*] - Union + [*] - Either + [*] - Object (How???) [ ] - Sealed Trait (How???) [*] - SelfRef [ ] - Tuple - [ ] - Unknown (throw exception) - [ ] - Scala 2 (throw exception) - [ ] - TypeSymbol (throw exception) + [*] - Unknown (throw exception) + [*] - Scala 2 (throw exception) + [*] - TypeSymbol (throw exception) */ object JsonWriter: @@ -129,7 +129,7 @@ object JsonWriter: acc.append($name) acc.append('"') acc.append(':') - val b = ${ refWrite[e](cfgE, f.fieldRef.asInstanceOf[RTypeRef[e]], fieldValue, '{ acc }) } + ${ refWrite[e](cfgE, f.fieldRef.asInstanceOf[RTypeRef[e]], fieldValue, '{ acc }) } acc.append(',') else acc } @@ -189,6 +189,41 @@ object JsonWriter: else sb.setCharAt(sb.length() - 1, '}') } + case t: LeftRightRef[?] => + if isMapKey then throw new JsonError("Union, Intersection, or Either-typed values cannot be map keys.") + t.leftRef.refType match + case '[lt] => + t.rightRef.refType match + case '[rt] => + val isEither = Expr(t.lrkind == LRKind.EITHER) + '{ + if $isEither then + $aE match + case Left(v) => + val vv = v.asInstanceOf[lt] + ${ refWrite[lt](cfgE, t.leftRef.asInstanceOf[RTypeRef[lt]], '{ vv }.asInstanceOf[Expr[lt]], sbE) } + case Right(v) => + val vv = v.asInstanceOf[rt] + ${ refWrite[rt](cfgE, t.rightRef.asInstanceOf[RTypeRef[rt]], '{ vv }.asInstanceOf[Expr[rt]], sbE) } + else + // Intersection/Union types....take your best shot! It's all we've got. No definitive info here. + val trial = new StringBuilder() + val lrSb = scala.util.Try { + val vv = $aE.asInstanceOf[rt] + ${ + refWrite[rt](cfgE, t.rightRef.asInstanceOf[RTypeRef[rt]], '{ vv }, '{ trial }) + } + } match + case Success(trialSb) => trialSb + case Failure(_) => + trial.clear + val vv = $aE.asInstanceOf[lt] + ${ + refWrite[lt](cfgE, t.leftRef.asInstanceOf[RTypeRef[lt]], '{ vv }, '{ trial }) + } + $sbE ++= lrSb + } + case t: TryRef[?] => if isMapKey then throw new JsonError("Try values (Succeed/Fail) cannot be map keys") t.tryRef.refType match @@ -198,10 +233,13 @@ object JsonWriter: case Success(v) => ${ refWrite[e](cfgE, t.tryRef.asInstanceOf[RTypeRef[e]], '{ v }.asInstanceOf[Expr[e]], sbE) } case Failure(_) if $cfgE.tryFailureHandling == TryOption.AS_NULL => $sbE.append("null") + case Failure(_) if $cfgE.tryFailureHandling == TryOption.AS_NULL => $sbE.append("null") + case Failure(f) if $cfgE.tryFailureHandling == TryOption.THROW_EXCEPTION => + throw new JsonError("A try value was Failure with message: " + f.getMessage()) case Failure(v) => - $sbE.append('"') + $sbE.append("\"Failure(") $sbE.append(v.getMessage) - $sbE.append('"') + $sbE.append(")\"") } case t: AliasRef[?] => @@ -218,3 +256,58 @@ object JsonWriter: JsonWriterRT.refWriteRT[T]($cfgE, again, $aE.asInstanceOf[T], $sbE)(using scala.collection.mutable.Map.empty[TypedName, RType[?]]) $sbE } + + case t: EnumRef[?] => + val enumE = t.expr + val isMapKeyE = Expr(isMapKey) + '{ + val enumRT = $enumE.asInstanceOf[EnumRType[T]] + val enumAsId = $cfgE.enumsAsIds match + case '*' => true + case aList: List[String] if aList.contains(enumRT.name) => true + case _ => false + if enumAsId then + val enumVal = enumRT.ordinal($aE.toString).getOrElse(throw new JsonError("Value " + $aE.toString + s" is not a valid enum value for ${enumRT.name}")) + if $isMapKeyE then + $sbE.append('"') + $sbE.append(enumVal.toString) + $sbE.append('"') + else $sbE.append(enumVal.toString) + else + $sbE.append('"') + $sbE.append($aE.toString) + $sbE.append('"') + } + + case t: ObjectRef => + val tname = Expr(t.name) + '{ + $sbE.append("\"" + $tname + "\"") + } + + case t: Scala2Ref[?] => + val tname = Expr(t.name) + '{ + $cfgE.undefinedFieldHandling match + case UndefinedValueOption.AS_NULL => $sbE.append("null") + case UndefinedValueOption.AS_SYMBOL => $sbE.append("\"" + $tname + "\"") + case UndefinedValueOption.THROW_EXCEPTION => throw new JsonError("Value " + $aE.toString + " is of some unknown/unsupported Scala 2 type " + $tname) + } + + case t: UnknownRef[?] => + val tname = Expr(t.name) + '{ + $cfgE.undefinedFieldHandling match + case UndefinedValueOption.AS_NULL => $sbE.append("null") + case UndefinedValueOption.AS_SYMBOL => $sbE.append("\"" + $tname + "\"") + case UndefinedValueOption.THROW_EXCEPTION => throw new JsonError("Value " + $aE.toString + " is of some unknown/unsupported type " + $tname) + } + + case t: TypeSymbolRef => + val tname = Expr(t.name) + '{ + $cfgE.undefinedFieldHandling match + case UndefinedValueOption.AS_NULL => $sbE.append("null") + case UndefinedValueOption.AS_SYMBOL => $sbE.append("\"" + $tname + "\"") + case UndefinedValueOption.THROW_EXCEPTION => throw new JsonError("Value " + $aE.toString + " is of some unknown/unsupported type " + $tname + ". (Class didn't fully define all its type parameters.)") + } diff --git a/src/main/scala/co.blocke.scalajack/json/JsonWriter.scalax b/src/main/scala/co.blocke.scalajack/json/JsonWriter.scalax index d33919ef..eb9a4245 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonWriter.scalax +++ b/src/main/scala/co.blocke.scalajack/json/JsonWriter.scalax @@ -312,8 +312,9 @@ object JsonWriter: } if sbLen == sb.length then sb.append(']') else sb.setCharAt(sb.length() - 1, ']') + } - +//***** case rt: SelfRefRef[?] => if isMapKey then throw new JsonError("Classes or traits cannot be map keys.") val e = rt.expr @@ -360,7 +361,7 @@ object JsonWriter: if sbLen == sb.length then sb.append('}') else sb.setCharAt(sb.length() - 1, '}') } - +//***** case rt: ObjectRef => val name = Expr(rt.name) '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => @@ -368,7 +369,7 @@ object JsonWriter: sb.append($name) sb.append('"') } - +//***** case rt: Scala2Ref[?] => val name = Expr(rt.name) '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => @@ -385,7 +386,7 @@ object JsonWriter: // def goGet: T = summon[Newtype.WithType[?, T]] // val unwrapped = a.asInstanceOf[Newtype[?]].unwrap // } - +//***** case rt: UnknownRef[?] => '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => sb.append('"') diff --git a/src/main/scala/co.blocke.scalajack/json/JsonWriter2.scalax b/src/main/scala/co.blocke.scalajack/json/JsonWriter2.scalax deleted file mode 100644 index bb25f426..00000000 --- a/src/main/scala/co.blocke.scalajack/json/JsonWriter2.scalax +++ /dev/null @@ -1,251 +0,0 @@ -package co.blocke.scalajack -package json - -import scala.quoted.* -import co.blocke.scala_reflection.* -import co.blocke.scala_reflection.rtypes.* -import co.blocke.scala_reflection.reflect.{ReflectOnType, TypeSymbolMapper} -import co.blocke.scala_reflection.reflect.rtypeRefs.* -import scala.util.{Failure, Success, Try} -import scala.quoted.staging.* - -object JsonWriter2: - - // Tests whether we should write something or not--mainly in the case of Option, or wrapped Option - def isOkToWrite(a: Any, cfg: JsonConfig) = - a match - case None if !cfg.noneAsNull => false - case Left(None) if !cfg.noneAsNull => false - case Right(None) if !cfg.noneAsNull => false - case Failure(_) if cfg.tryFailureHandling == TryOption.NO_WRITE => false - case _ => true - - def refWrite[T](cfgE: Expr[JsonConfig], rt: RType[T], aE: Expr[T], sbE: Expr[StringBuilder], isMapKey: Boolean = false)(using Quotes)(using tt: Type[T]): Expr[StringBuilder] = - import quotes.reflect.* - - rt match - case StringRType() | CharRType() | JavaCharacterRType() => '{ if $aE == null then $sbE.append("null") else $sbE.append("\"" + $aE.toString + "\"") } - case t: PrimitiveRType => - if isMapKey then - '{ - if $aE == null then $sbE.append("\"null\"") - else - $sbE.append('"') - $sbE.append($aE.toString) - $sbE.append('"') - } - else '{ if $aE == null then $sbE.append("null") else $sbE.append($aE.toString) } - - case t: SeqRType[?] => - if isMapKey then throw new JsonError("Seq instances cannot be map keys") - - t.elementType.toType(quotes) match - case '[e] => - '{ - val sb = $sbE - if $aE == null then sb.append("null") - else - sb.append('[') - val sbLen = sb.length - - $aE.asInstanceOf[Seq[e]].foldLeft(sb) { (acc, one) => - if isOkToWrite(one, $cfgE) then - ${ refWrite[e](cfgE, t.elementType.asInstanceOf[RType[e]], '{ one }, '{ acc }) } - sb.append(',') - else sb - } - - if sbLen == sb.length then sb.append(']') - else sb.setCharAt(sb.length() - 1, ']') - } - case t: ArrayRType[?] => - if isMapKey then throw new JsonError("Arrays instances cannot be map keys") - - t.elementType.toType(quotes) match - case '[e] => - '{ - val sb = $sbE - if $aE == null then sb.append("null") - else - sb.append('[') - val sbLen = sb.length - - $aE.asInstanceOf[Seq[e]].foldLeft(sb) { (acc, one) => - if isOkToWrite(one, $cfgE) then - ${ refWrite[e](cfgE, t.elementType.asInstanceOf[RType[e]], '{ one }, '{ acc }) } - sb.append(',') - else sb - } - - if sbLen == sb.length then sb.append(']') - else sb.setCharAt(sb.length() - 1, ']') - } - - case t: ClassRType[?] => - '{ - val sb = $sbE - if $aE == null then sb.append("null") - else - sb.append('{') - val sbLen = sb.length - ${ - t.fields.foldLeft('{ sb }) { (accE, f) => - f.fieldType.toType(quotes) match - case '[e] => - val fieldValue = Select.unique(aE.asTerm, f.name).asExprOf[e] - val name = Expr(f.name) - '{ - val acc = $accE - if isOkToWrite($fieldValue, $cfgE) then - acc.append('"') - acc.append($name) - acc.append('"') - acc.append(':') - val b = ${ refWrite[e](cfgE, f.fieldType.asInstanceOf[RType[e]], fieldValue, '{ acc }) } - acc.append(',') - else acc - } - } - } - if sbLen == sb.length then sb.append('}') - else sb.setCharAt(sb.length() - 1, '}') - } - - case t: TraitRType[?] => - val foo: Expr[RType[T]] = '{ - // given Compiler = Compiler.make($aE.getClass.getClassLoader) - val sb = $sbE - sb.append('{') - sb.append(s"\"_hint\":\"${$aE.getClass.getName}\"") - sb.append('}') - RType.inTermsOf[T]($aE.getClass).asInstanceOf[RType[T]] - val rt: Expr[RType[T]] = ??? - ${refWrite[T](cfgE, rt.value, aE, sbE)} - } - - - // val fn = (q: Quotes) ?=> { - // import q.reflect.* - // ReflectUtil.inTermsOf2(RType.of[T].asInstanceOf[TraitRType[T]], $aE.getClass).expr.asInstanceOf[Expr[RTypeRef[?]]] - // } - // val classRType = quoted.staging.run(fn).asInstanceOf[RType[T]] - // ${ - // t.toType(quotes) match - // case '[e] => - - // } - // $sbE - // val cRT: ScalaClassRType[T] = ??? - // refWrite[T](cfgE, cRT, aE, sbE) - - /* - given Compiler = Compiler.make(this.getClass.getClassLoader) - '{ - val refMakerFn = (q: Quotes) ?=> - import q.reflect.* - (traitRef: TraitRef[T], inst: T, instE: Expr[T], cfgE: Expr[JsonConfig], sbE2: Expr[StringBuilder]) => - val clazz = inst.getClass - val classRef = ReflectOnType(quotes)(quotes.reflect.TypeRepr.typeConstructorOf(clazz), false)(using scala.collection.mutable.Map.empty[TypedName, Boolean]).asInstanceOf[ScalaClassRef[?]] - val inTermsOfRef = - if traitRef.typeParamSymbols.nonEmpty then - val seenBefore = scala.collection.mutable.Map.empty[TypedName, Boolean] - val paths = classRef.typePaths.getOrElse(traitRef.name, throw new ReflectException(s"No path in class ${classRef.name} for trait ${traitRef.name}")) - traitRef.refType match - case '[e] => - val typeParamTypes = TypeSymbolMapper.runPath(quotes)(paths, TypeRepr.of[e]) - val classQuotedTypeRepr = TypeRepr.typeConstructorOf(clazz) - ReflectOnType(q)(classQuotedTypeRepr.appliedTo(typeParamTypes))(using seenBefore) - else classRef - classRef.refType match - case '[e] => - ${ - val y = refWrite[e](cfgE, classRef.asInstanceOf[RTypeRef[e]], instE.asInstanceOf[Expr[e]], sbE2) - '{ y } - } - quoted.staging.run(refMakerFn(t, $aE, aE, cfgE, sbE)) - } - */ - - /* - '{ - val fn = (quotes: Quotes) ?=> - (ref2: TraitRef[T], aE2: Expr[T], a2: T, cfgE2: Expr[JsonConfig]) => - import quotes.reflect.* - val clazz = a2.getClass - val classRef = ReflectOnType(quotes)(quotes.reflect.TypeRepr.typeConstructorOf(clazz), false)(using scala.collection.mutable.Map.empty[TypedName, Boolean]).asInstanceOf[ScalaClassRef[?]] - val inTermsOfRef = - if ref2.typeParamSymbols.nonEmpty then - val seenBefore = scala.collection.mutable.Map.empty[TypedName, Boolean] - val paths = classRef.typePaths.getOrElse(ref2.name, throw new ReflectException(s"No path in class ${classRef.name} for trait ${ref2.name}")) - ref2.refType match - case '[e] => - val typeParamTypes = TypeSymbolMapper.runPath(quotes)(paths, TypeRepr.of[e]) - val classQuotedTypeRepr = TypeRepr.typeConstructorOf(clazz) - ReflectOnType(quotes)(classQuotedTypeRepr.appliedTo(typeParamTypes))(using seenBefore) - else classRef - - inTermsOfRef.refType match - case '[e] => - val asClassRef = inTermsOfRef.asInstanceOf[ScalaClassRef[e]].copy(renderTrait = Some(ref2.name)) // set renderTrait so ClassRef writer code renders type hint - refWrite[e](cfgE, asClassRef.asInstanceOf[RTypeRef[e]], aE2.asInstanceOf[Expr[e]], sbE) - - given Compiler = Compiler.make(this.getClass.getClassLoader) - quoted.staging.run(fn(t, aE, $aE, cfgE)) - } - */ - - /* - case t: OptionRef[?] => - if isMapKey then throw new JsonError("Option valuess cannot be map keys") - t.optionParamType.refType match - case '[e] => - '{ - $aE match - case None => $sbE.append("null") - case Some(v) => - ${ refWrite[e](cfgE, t.optionParamType.asInstanceOf[RTypeRef[e]], '{ v }.asInstanceOf[Expr[e]], sbE) } - } - - case t: MapRef[?] => - if isMapKey then throw new JsonError("Map values cannot be map keys") - t.elementRef.refType match - case '[k] => - t.elementRef2.refType match - case '[v] => - '{ - val sb = $sbE - if $aE == null then sb.append("null") - else - sb.append('{') - val sbLen = sb.length - $aE.asInstanceOf[Map[?, ?]].foreach { case (key, value) => - if isOkToWrite(value, $cfgE) then - val b = ${ refWrite[k](cfgE, t.elementRef.asInstanceOf[RTypeRef[k]], '{ key }.asInstanceOf[Expr[k]], sbE, true) } - b.append(':') - val b2 = ${ refWrite[v](cfgE, t.elementRef2.asInstanceOf[RTypeRef[v]], '{ value }.asInstanceOf[Expr[v]], sbE) } - b2.append(',') - } - if sbLen == sb.length then sb.append('}') - else sb.setCharAt(sb.length() - 1, '}') - } - - case t: TryRef[?] => - if isMapKey then throw new JsonError("Try values (Succeed/Fail) cannot be map keys") - t.tryRef.refType match - case '[e] => - '{ - $aE match - case Success(v) => - ${ refWrite[e](cfgE, t.tryRef.asInstanceOf[RTypeRef[e]], '{ v }.asInstanceOf[Expr[e]], sbE) } - case Failure(_) if $cfgE.tryFailureHandling == TryOption.AS_NULL => $sbE.append("null") - case Failure(v) => - $sbE.append('"') - $sbE.append(v.getMessage) - $sbE.append('"') - } - - case t: AliasRef[?] => - t.unwrappedType.refType match - case '[e] => - refWrite[e](cfgE, t.unwrappedType.asInstanceOf[RTypeRef[e]], aE.asInstanceOf[Expr[e]], sbE) -*/ \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json/JsonWriterRT.scala b/src/main/scala/co.blocke.scalajack/json/JsonWriterRT.scala index 9731c81d..2aaa92b1 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonWriterRT.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonWriterRT.scala @@ -7,16 +7,15 @@ import co.blocke.scala_reflection.reflect.{ReflectOnType, TypeSymbolMapper} import co.blocke.scala_reflection.reflect.rtypeRefs.* import scala.util.{Failure, Success, Try} -/** - * This class is horrible. It is a mirror of JsonWriter, except this one executes at runtime, and hence +/** This class is horrible. It is a mirror of JsonWriter, except this one executes at runtime, and hence * doens't have any Expr's. This is bad becuause it's slow--we're not able to take advantage of doing work * at compile-time. This is ONLY used becuase of trait handling. The problem is we need to express some * class C in terms of trait T. We know T at compile-time but don't know C until compile-time. There's - * (so far) no good way to get the information passed across the bridge, so... What we need to do in + * (so far) no good way to get the information passed across the bridge, so... What we need to do in * JsonWriter is leverage RType.inTermsOf to do the C->T magic, but this returns an RType, not an * RTypeRef. We must render the rest of the trait (and any structures below it) at runtime, necessitating * this class, JsonWriterRT. - * + * * It should only be used for this special trait handling. Everything else leverages the compile-time * JsonWriter. */ @@ -33,35 +32,34 @@ object JsonWriterRT: case _ => true def refWriteRT[T]( - cfg: JsonConfig, - rt: RType[T], - a: T, - sb: StringBuilder, - isMapKey: Boolean = false - )(using classesSeen: scala.collection.mutable.Map[TypedName, RType[?]]): StringBuilder = - + cfg: JsonConfig, + rt: RType[T], + a: T, + sb: StringBuilder, + isMapKey: Boolean = false + )(using classesSeen: scala.collection.mutable.Map[TypedName, RType[?]]): StringBuilder = rt match case StringRType() | CharRType() | JavaCharacterRType() => if a == null then sb.append("null") else sb.append("\"" + a.toString + "\"") - case t: PrimitiveRType => + case t: PrimitiveRType => if isMapKey then if a == null then sb.append("\"null\"") else sb.append('"') sb.append(a.toString) sb.append('"') - else if a == null then sb.append("null") else sb.append(a.toString) + else if a == null then sb.append("null") + else sb.append(a.toString) case t: SeqRType[?] => if isMapKey then throw new JsonError("Seq instances cannot be map keys") if a == null then sb.append("null") - else - sb.append('[') + else sb.append('[') val sbLen = sb.length a.asInstanceOf[Seq[t.elementType.T]].foldLeft(sb) { (acc, one) => - if isOkToWrite(one, cfg) then - refWriteRT[t.elementType.T](cfg, t.elementType.asInstanceOf[RType[t.elementType.T]], one, acc) - sb.append(',') - else sb + if isOkToWrite(one, cfg) then + refWriteRT[t.elementType.T](cfg, t.elementType.asInstanceOf[RType[t.elementType.T]], one, acc) + sb.append(',') + else sb } if sbLen == sb.length then sb.append(']') else sb.setCharAt(sb.length() - 1, ']') @@ -69,14 +67,13 @@ object JsonWriterRT: case t: ArrayRType[?] => if isMapKey then throw new JsonError("Arrays instances cannot be map keys") if a == null then sb.append("null") - else - sb.append('[') + else sb.append('[') val sbLen = sb.length a.asInstanceOf[Seq[t.elementType.T]].foldLeft(sb) { (acc, one) => - if isOkToWrite(one, cfg) then - refWriteRT[t.elementType.T](cfg, t.elementType.asInstanceOf[RType[t.elementType.T]], one, acc) - sb.append(',') - else sb + if isOkToWrite(one, cfg) then + refWriteRT[t.elementType.T](cfg, t.elementType.asInstanceOf[RType[t.elementType.T]], one, acc) + sb.append(',') + else sb } if sbLen == sb.length then sb.append(']') else sb.setCharAt(sb.length() - 1, ']') @@ -85,24 +82,24 @@ object JsonWriterRT: classesSeen.put(t.typedName, t) if a == null then sb.append("null") else - sb.append('{') - val sbLen = sb.length - t.renderTrait.map( traitName => sb.append(s"\"_hint\":\"$traitName\",") ) - t.fields.foldLeft(sb) { (acc, f) => - val m = a.getClass.getMethod(f.name) - m.setAccessible(true) - val fieldValue = m.invoke(a).asInstanceOf[f.fieldType.T] - if isOkToWrite(fieldValue, cfg) then - acc.append('"') - acc.append(f.name) - acc.append('"') - acc.append(':') - refWriteRT[f.fieldType.T](cfg, f.fieldType.asInstanceOf[RType[f.fieldType.T]], fieldValue, acc) - acc.append(',') - else acc - } - if sbLen == sb.length then sb.append('}') - else sb.setCharAt(sb.length() - 1, '}') + sb.append('{') + val sbLen = sb.length + t.renderTrait.map(traitName => sb.append(s"\"_hint\":\"$traitName\",")) + t.fields.foldLeft(sb) { (acc, f) => + val m = a.getClass.getMethod(f.name) + m.setAccessible(true) + val fieldValue = m.invoke(a).asInstanceOf[f.fieldType.T] + if isOkToWrite(fieldValue, cfg) then + acc.append('"') + acc.append(f.name) + acc.append('"') + acc.append(':') + refWriteRT[f.fieldType.T](cfg, f.fieldType.asInstanceOf[RType[f.fieldType.T]], fieldValue, acc) + acc.append(',') + else acc + } + if sbLen == sb.length then sb.append('}') + else sb.setCharAt(sb.length() - 1, '}') case t: TraitRType[?] => classesSeen.put(t.typedName, t) @@ -132,12 +129,46 @@ object JsonWriterRT: if sbLen == sb.length then sb.append('}') else sb.setCharAt(sb.length() - 1, '}') + case t: EitherRType[?] => + if isMapKey then throw new JsonError("Union, Intersection, or Either-typed values cannot be map keys.") + a match + case Left(v) => + refWriteRT[t.leftType.T](cfg, t.leftType.asInstanceOf[RType[t.leftType.T]], v.asInstanceOf[t.leftType.T], sb) + case Right(v) => + refWriteRT[t.rightType.T](cfg, t.rightType.asInstanceOf[RType[t.rightType.T]], v.asInstanceOf[t.rightType.T], sb) + + case t: UnionRType[?] => + // Intersection/Union types....take your best shot! It's all we've got. No definitive info here. + val trial = new StringBuilder() + val lrSb = scala.util.Try( + refWriteRT[t.rightType.T](cfg, t.rightType.asInstanceOf[RType[t.rightType.T]], a.asInstanceOf[t.rightType.T], trial) + ) match + case Success(trialSb) => trialSb + case Failure(_) => + trial.clear + refWriteRT[t.leftType.T](cfg, t.leftType.asInstanceOf[RType[t.leftType.T]], v.asInstanceOf[t.leftType.T], trial) + sb ++= lrSb + + case t: IntersectionRType[?] => + // Intersection/Union types....take your best shot! It's all we've got. No definitive info here. + val trial = new StringBuilder() + val lrSb = scala.util.Try( + refWriteRT[t.rightType.T](cfg, t.rightType.asInstanceOf[RType[t.rightType.T]], a.asInstanceOf[t.rightType.T], trial) + ) match + case Success(trialSb) => trialSb + case Failure(_) => + trial.clear + refWriteRT[t.leftType.T](cfg, t.leftType.asInstanceOf[RType[t.leftType.T]], v.asInstanceOf[t.leftType.T], trial) + sb ++= lrSb + case t: TryRType[?] => if isMapKey then throw new JsonError("Try values (Succeed/Fail) cannot be map keys") a match case Success(v) => refWriteRT[t.tryType.T](cfg, t.tryType.asInstanceOf[RType[t.tryType.T]], v.asInstanceOf[t.tryType.T], sb) case Failure(_) if cfg.tryFailureHandling == TryOption.AS_NULL => sb.append("null") + case Failure(f) if cfg.tryFailureHandling == TryOption.THROW_EXCEPTION => + throw new JsonError("A try value was Failure with message: " + f.getMessage()) case Failure(v) => sb.append('"') sb.append(v.getMessage) @@ -149,12 +180,49 @@ object JsonWriterRT: case t: SelfRefRType[?] => if isMapKey then throw new JsonError("Classes or traits cannot be map keys.") val again = classesSeen.getOrElse( - t.typedName, - { + t.typedName, { // Need to add to cache. Since we're coming from compile-time side, the runtime side may not have seen this class before... val v = RType.of[T] classesSeen.put(t.typedName, v) v } - ) + ) JsonWriterRT.refWriteRT[again.T](cfg, again, a.asInstanceOf[again.T], sb) + + case t: EnumRType[?] => + val enumAsId = cfg.enumsAsIds match + case '*' => true + case aList: List[String] if aList.contains(t.name) => true + case _ => false + if enumAsId then + val enumVal = t.ordinal(a.toString).getOrElse(throw new JsonError(s"Value $a is not a valid enum value for ${t.name}")) + if isMapKey then + sb.append('"') + sb.append(enumVal.toString) + sb.append('"') + else sb.append(enumVal.toString) + else + sb.append('"') + sb.append(a.toString) + sb.append('"') + + case t: ObjectRef => + sb.append("\"" + t.name + "\"") + + case t: Scala2RType[?] => + cfg.undefinedFieldHandling match + case UndefinedValueOption.AS_NULL => sb.append("null") + case UndefinedValueOption.AS_SYMBOL => sb.append("\"" + t.name + "\"") + case UndefinedValueOption.THROW_EXCEPTION => throw new JsonError("Value " + a.toString + " is of some unknown/unsupported Scala 2 type " + t.name) + + case t: UnknownRType[?] => + cfg.undefinedFieldHandling match + case UndefinedValueOption.AS_NULL => sb.append("null") + case UndefinedValueOption.AS_SYMBOL => sb.append("\"" + t.name + "\"") + case UndefinedValueOption.THROW_EXCEPTION => throw new JsonError("Value " + a.toString + " is of some unknown/unsupported type " + t.name) + + case t: TypeSymbolRType => + cfg.undefinedFieldHandling match + case UndefinedValueOption.AS_NULL => sb.append("null") + case UndefinedValueOption.AS_SYMBOL => sb.append("\"" + t.name + "\"") + case UndefinedValueOption.THROW_EXCEPTION => throw new JsonError("Value " + a.toString + " is of some unknown/unsupported type " + t.name + ". (Class didn't fully define all its type parameters.)") diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index 78482968..ef3a95d3 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -23,23 +23,9 @@ object RunMe extends App: // .copy(enumsAsIds = '*') try - val v = - Person( - "Greg", - Some( - Person( - "Lili", - Some( - Person( - "Katie", - None - ) - ) - ) - ) - ) + val v = Person("Greg", 33) - println("HERE: " + ScalaJack.write[Person[Boolean]](v.asInstanceOf[Person[Boolean]])) + println("HERE: " + ScalaJack.write(v)) // println("HERE: " + ScalaJack.write(Person("Greg", Foom('z'), Some(Person("Lili", Foom('x'), None))))) diff --git a/src/main/scala/co.blocke.scalajack/run/Sample.scala b/src/main/scala/co.blocke.scalajack/run/Sample.scala index f37fbb3b..e938af60 100644 --- a/src/main/scala/co.blocke.scalajack/run/Sample.scala +++ b/src/main/scala/co.blocke.scalajack/run/Sample.scala @@ -18,18 +18,18 @@ import neotype.* // case class Dog(name: String, numLegs: Int, carsChased: Int, friend: Option[Animal2]) extends Animal2 -// enum Colors: -// case Red, Blue, Green +enum Colors: + case Red, Blue, Green // import scala.collection.immutable.* // enum Vehicle: // case Car, Bus, Train -// object WeekDay extends Enumeration { -// type WeekDay = Value -// val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value -// } -// import WeekDay.* +object WeekDay extends Enumeration { + type WeekDay = Value + val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value +} +import WeekDay.* // case class Simple(a: Int, b: Boolean, c: Option[Simple], z: Int = 5) @@ -45,7 +45,7 @@ case class Foom[X](x: X) extends Miss[X] // case class Person[Y](name: String, age: Miss[Y], again: Option[Person[Y]]) -case class Person[Y](name: String, again: Option[Person[Y]]) +case class Person(name: String, color: String | Int) // type NonEmptyString = NonEmptyString.Type // given NonEmptyString: Newtype[String] with From bc311c7d8cd6361dac550df2637e4bf22b71da54 Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Sun, 5 Nov 2023 20:45:19 -0600 Subject: [PATCH 20/65] More write progress --- benchmark/README.md | 22 +++--- .../co.blocke.scalajack/json/JsonWriter.scala | 71 ++++++++++++++++++- .../json/JsonWriter.scalax | 8 +-- .../json/JsonWriterRT.scala | 46 +++++++++++- .../scala/co.blocke.scalajack/run/Play.scala | 5 +- .../co.blocke.scalajack/run/Sample.scala | 2 +- 6 files changed, 132 insertions(+), 22 deletions(-) diff --git a/benchmark/README.md b/benchmark/README.md index 7d0de445..f28eaf79 100644 --- a/benchmark/README.md +++ b/benchmark/README.md @@ -1,6 +1,6 @@ # Performance -JSON serialization enchmarks I found in github often measured (IMO) silly things like how fast a parser +JSON serialization benchmarks I found in various repos often measured (IMO) silly things like how fast a parser could handle a small list of Int. For this benchmark I used a more substantial model + JSON. It's still not large by any measure, but it does have some nested objects and collections that make it a more realistic test. @@ -25,22 +25,22 @@ sbt "jmh:run -i 10 -wi 10 -f 2 -t 1 co.blocke.WritingBenchmark" | Play JSON | thrpt | 20 | 438,538.475 | ± 16319.198 | ops/s | | ScalaJack 7 | thrpt | 20 | 106,292.338 | ± 330.111 | ops/s | -### Writing Interpretation +### Interpretation -The Hand-Tooled case is straight, manual JSON creation in code. I included it to show a likely -upper-bound on achievable performance. No parser, with whatever logic it must do, can be faster +The Hand-Tooled case is creating JSON manually in code. I included it to show the presumed +upper-bound of achievable performance. No serializer, with whatever logic it must do, can be faster than hand-tooled code that is hard-wired in its output and requires zero logic. -We see that both Circe and ScalaJack are very close in performance--close to each other and -shockingly close to hand-tooled code. +We see in these results that both Circe and ScalaJack are very close in performance--close to each +other and very close to the performance of hand-tooled code. Circe is the gold standard for JSON serializers due to its many features, excellent performance, and widespread adoption. The one cost Circe imposes is the same one virtually all other serializers -require: that boilerplate be provided to define encoders/decoders to aid the serializion. Circe's +require: boilerplate must be provided to define encoders/decoders to aid the serializion. Circe's boilerplate is actually not terrible. Others require a fair bit of extra code per class serialized. ScalaJack's focus is first and foremost to be frictionless--no drama to the user. The very slight -difference in maximal performance is a worthy expense--its still blazing fast, and positioned well -vs the pack. ScalaJack requires zero boilerplate--you can throw any Scala object at it with no -pre-preparation and it will serialize it. You'll notice the order-of-magnitude improvement ScalaJack 8 -has over ScalaJack 7, due to moving everything possible into compile-time macros for speed. +difference in maximal performance is a worthy expense--its still blazing fast. ScalaJack requires +zero boilerplate--you can throw any Scala object (or even a Java object) at it with no pre-preparation +and it will serialize it. You'll notice the enormous performange improvement ScalaJack 8 has over +ScalaJack 7, due to moving everything possible into compile-time macros for speed. diff --git a/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala b/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala index 0de3fc7b..f4bf7cf1 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala @@ -6,6 +6,7 @@ import co.blocke.scala_reflection.* import co.blocke.scala_reflection.rtypes.{EnumRType, ScalaClassRType, TraitRType} import co.blocke.scala_reflection.reflect.{ReflectOnType, TypeSymbolMapper} import co.blocke.scala_reflection.reflect.rtypeRefs.* +import scala.jdk.CollectionConverters.* import scala.util.{Failure, Success, Try} import scala.quoted.staging.* @@ -16,15 +17,15 @@ import scala.quoted.staging.* [*] - Enum [*] - Enumeration [*] - Java Enum - [ ] - Java Collections - [ ] - Java Map + [*] - Java Collections + [*] - Java Map [*] - Intersection [*] - Union [*] - Either [*] - Object (How???) [ ] - Sealed Trait (How???) [*] - SelfRef - [ ] - Tuple + [*] - Tuple [*] - Unknown (throw exception) [*] - Scala 2 (throw exception) [*] - TypeSymbol (throw exception) @@ -242,6 +243,70 @@ object JsonWriter: $sbE.append(")\"") } + case t: TupleRef[?] => + if isMapKey then throw new JsonError("Tuples cannot be map keys") + '{ + val sb = $sbE + if $aE == null then sb.append("null") + sb.append('[') + val sbLen = sb.length + ${ + val tupleBuf = t.tupleRefs.zipWithIndex.foldLeft(sbE) { case (accE, (ref, i)) => + ref.refType match + case '[e] => + val fieldValue = Select.unique(aE.asTerm, "_" + (i + 1)).asExprOf[e] + '{ + val acc = $accE + ${ refWrite[e](cfgE, ref.asInstanceOf[RTypeRef[e]], fieldValue, '{ acc }) } + acc.append(',') + } + } + tupleBuf + } + if sbLen == sb.length then sb.append(']') + else sb.setCharAt(sb.length() - 1, ']') + } + + case t: JavaCollectionRef[?] => + if isMapKey then throw new JsonError("Collections cannot be map keys.") + t.elementRef.refType match + case '[e] => + '{ + val sb = $sbE + if $aE == null then sb.append("null") + sb.append('[') + val sbLen = sb.length + $aE.asInstanceOf[java.util.Collection[?]].toArray.foreach { elem => + if isOkToWrite(elem, $cfgE) then + ${ refWrite[e](cfgE, t.elementRef.asInstanceOf[RTypeRef[e]], '{ elem }.asInstanceOf[Expr[e]], sbE) } + sb.append(',') + } + if sbLen == sb.length then sb.append(']') + else sb.setCharAt(sb.length() - 1, ']') + } + + case t: JavaMapRef[?] => + if isMapKey then throw new JsonError("Maps cannot be map keys.") + t.elementRef.refType match + case '[k] => + t.elementRef2.refType match + case '[v] => + '{ + val sb = $sbE + if $aE == null then sb.append("null") + sb.append('{') + val sbLen = sb.length + $aE.asInstanceOf[java.util.Map[?, ?]].asScala.foreach { case (key, value) => + if isOkToWrite(value, $cfgE) then + ${ refWrite[k](cfgE, t.elementRef.asInstanceOf[RTypeRef[k]], '{ key }.asInstanceOf[Expr[k]], sbE, true) } + sb.append(':') + ${ refWrite[v](cfgE, t.elementRef2.asInstanceOf[RTypeRef[v]], '{ value }.asInstanceOf[Expr[v]], sbE) } + sb.append(',') + } + if sbLen == sb.length then sb.append('}') + else sb.setCharAt(sb.length() - 1, '}') + } + case t: AliasRef[?] => t.unwrappedType.refType match case '[e] => diff --git a/src/main/scala/co.blocke.scalajack/json/JsonWriter.scalax b/src/main/scala/co.blocke.scalajack/json/JsonWriter.scalax index eb9a4245..a581b05f 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonWriter.scalax +++ b/src/main/scala/co.blocke.scalajack/json/JsonWriter.scalax @@ -226,7 +226,7 @@ object JsonWriter: if sbLen == sb.length then sb.append('}') else sb.setCharAt(sb.length() - 1, '}') } - +//***** case rt: LeftRightRef[?] => if isMapKey then throw new JsonError("Union, intersection, or Either cannot be map keys.") rt.leftRef.refType match @@ -249,7 +249,7 @@ object JsonWriter: if scala.util.Try($rightFn(a.asInstanceOf[rt], trial, cfg)).isFailure then $leftFn(a.asInstanceOf[lt], sb, cfg) else sb ++= trial } - +//***** case rt: EnumRef[?] => val expr = rt.expr val isMapKeyExpr = Expr(isMapKey) @@ -280,7 +280,7 @@ object JsonWriter: a.toString sb.append('"') } - +//***** case rt: TupleRef[?] => if isMapKey then throw new JsonError("Tuples cannot be map keys.") val elementFns = rt.tupleRefs.map { f => @@ -322,7 +322,7 @@ object JsonWriter: val fn = refCache.get($e.typedName).asInstanceOf[(T, StringBuilder, JsonConfig) => StringBuilder] fn(a, sb, cfg) } - +//***** case rt: JavaCollectionRef[?] => if isMapKey then throw new JsonError("Collections cannot be map keys.") rt.elementRef.refType match diff --git a/src/main/scala/co.blocke.scalajack/json/JsonWriterRT.scala b/src/main/scala/co.blocke.scalajack/json/JsonWriterRT.scala index 2aaa92b1..d217d38f 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonWriterRT.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonWriterRT.scala @@ -5,6 +5,7 @@ import co.blocke.scala_reflection.* import co.blocke.scala_reflection.rtypes.* import co.blocke.scala_reflection.reflect.{ReflectOnType, TypeSymbolMapper} import co.blocke.scala_reflection.reflect.rtypeRefs.* +import scala.jdk.CollectionConverters.* import scala.util.{Failure, Success, Try} /** This class is horrible. It is a mirror of JsonWriter, except this one executes at runtime, and hence @@ -146,7 +147,7 @@ object JsonWriterRT: case Success(trialSb) => trialSb case Failure(_) => trial.clear - refWriteRT[t.leftType.T](cfg, t.leftType.asInstanceOf[RType[t.leftType.T]], v.asInstanceOf[t.leftType.T], trial) + refWriteRT[t.leftType.T](cfg, t.leftType.asInstanceOf[RType[t.leftType.T]], a.asInstanceOf[t.leftType.T], trial) sb ++= lrSb case t: IntersectionRType[?] => @@ -158,7 +159,7 @@ object JsonWriterRT: case Success(trialSb) => trialSb case Failure(_) => trial.clear - refWriteRT[t.leftType.T](cfg, t.leftType.asInstanceOf[RType[t.leftType.T]], v.asInstanceOf[t.leftType.T], trial) + refWriteRT[t.leftType.T](cfg, t.leftType.asInstanceOf[RType[t.leftType.T]], a.asInstanceOf[t.leftType.T], trial) sb ++= lrSb case t: TryRType[?] => @@ -174,6 +175,47 @@ object JsonWriterRT: sb.append(v.getMessage) sb.append('"') + case t: TupleRType[?] => + if isMapKey then throw new JsonError("Tuples cannot be map keys") + if a == null then sb.append("null") + sb.append('[') + val sbLen = sb.length + val fieldValues = a.asInstanceOf[Product].productIterator.toList + t.typeParamValues.zipWithIndex.foreach { case (rt, i) => + val fieldValue = fieldValues(i).asInstanceOf[rt.T] + refWriteRT[rt.T](cfg, rt.asInstanceOf[RType[rt.T]], fieldValue, sb) + sb.append(',') + } + if sbLen == sb.length then sb.append(']') + else sb.setCharAt(sb.length() - 1, ']') + + case t: JavaCollectionRType[?] => + if isMapKey then throw new JsonError("Collections cannot be map keys.") + if a == null then sb.append("null") + sb.append('[') + val sbLen = sb.length + a.asInstanceOf[java.util.Collection[?]].toArray.foreach { elem => + if isOkToWrite(elem, cfg) then refWriteRT[t.elementType.T](cfg, t.elementType.asInstanceOf[RType[t.elementType.T]], elem.asInstanceOf[t.elementType.T], sb) + } + sb.append(',') + if sbLen == sb.length then sb.append(']') + else sb.setCharAt(sb.length() - 1, ']') + + case t: JavaMapRType[?] => + if isMapKey then throw new JsonError("Collections cannot be map keys.") + if a == null then sb.append("null") + sb.append('{') + val sbLen = sb.length + a.asInstanceOf[java.util.Map[?, ?]].asScala.foreach { case (key, value) => + if isOkToWrite(value, cfg) then + refWriteRT[t.elementType.T](cfg, t.elementType.asInstanceOf[RType[t.elementType.T]], key.asInstanceOf[t.elementType.T], sb, true) + sb.append(':') + refWriteRT[t.elementType2.T](cfg, t.elementType2.asInstanceOf[RType[t.elementType2.T]], value.asInstanceOf[t.elementType2.T], sb) + sb.append(',') + } + if sbLen == sb.length then sb.append('}') + else sb.setCharAt(sb.length() - 1, '}') + case t: AliasRType[?] => refWriteRT[t.unwrappedType.T](cfg, t.unwrappedType.asInstanceOf[RType[t.unwrappedType.T]], a.asInstanceOf[t.unwrappedType.T], sb) diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index ef3a95d3..ffd08df9 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -23,7 +23,10 @@ object RunMe extends App: // .copy(enumsAsIds = '*') try - val v = Person("Greg", 33) + val s = new java.util.HashMap[Int, Boolean]() + s.put(1, true) + s.put(0, false) + val v = Person("Greg", s) println("HERE: " + ScalaJack.write(v)) diff --git a/src/main/scala/co.blocke.scalajack/run/Sample.scala b/src/main/scala/co.blocke.scalajack/run/Sample.scala index e938af60..e0681803 100644 --- a/src/main/scala/co.blocke.scalajack/run/Sample.scala +++ b/src/main/scala/co.blocke.scalajack/run/Sample.scala @@ -45,7 +45,7 @@ case class Foom[X](x: X) extends Miss[X] // case class Person[Y](name: String, age: Miss[Y], again: Option[Person[Y]]) -case class Person(name: String, color: String | Int) +case class Person(name: String, color: java.util.HashMap[Int, Boolean]) // type NonEmptyString = NonEmptyString.Type // given NonEmptyString: Newtype[String] with From 62ddc8267d2501cbe5e1272f951393506943cc86 Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Tue, 7 Nov 2023 01:13:37 -0600 Subject: [PATCH 21/65] sealed traits and more --- build.sbt | 2 +- .../co.blocke.scalajack/json/JsonConfig.scala | 1 + .../co.blocke.scalajack/json/JsonWriter.scala | 87 ++-- .../json/JsonWriter.scalax | 395 ------------------ .../json/ReflectUtil.scalax | 59 --- .../scala/co.blocke.scalajack/run/Play.scala | 10 +- .../co.blocke.scalajack/run/Sample.scala | 44 +- 7 files changed, 73 insertions(+), 525 deletions(-) delete mode 100644 src/main/scala/co.blocke.scalajack/json/JsonWriter.scalax delete mode 100644 src/main/scala/co.blocke.scalajack/json/ReflectUtil.scalax diff --git a/build.sbt b/build.sbt index 98a371dc..21a9dc1a 100644 --- a/build.sbt +++ b/build.sbt @@ -35,7 +35,7 @@ lazy val root = project Test / parallelExecution := false, scalafmtOnCompile := !isCI, libraryDependencies ++= Seq( - "co.blocke" %% "scala-reflection" % "sj_fixes_edbef8", + "co.blocke" %% "scala-reflection" % "sj_fixes_3a7daa", "org.apache.commons" % "commons-text" % "1.10.0", "io.github.kitlangton" %% "neotype" % "0.0.9", "org.scalatest" %% "scalatest" % "3.2.17" % Test, diff --git a/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala b/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala index 8b245e40..5f494d0e 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala @@ -7,6 +7,7 @@ case class JsonConfig( tryFailureHandling: TryOption = TryOption.NO_WRITE, undefinedFieldHandling: UndefinedValueOption = UndefinedValueOption.THROW_EXCEPTION, permissivePrimitives: Boolean = false, + writeNonConstructorFields: Boolean = false, // -------------------------- typeHintLabel: String = "_hint", typeHintLabelByTrait: Map[String, String] = Map.empty[String, String], // Trait name -> type hint label diff --git a/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala b/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala index f4bf7cf1..4ec474d1 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala @@ -23,16 +23,24 @@ import scala.quoted.staging.* [*] - Union [*] - Either [*] - Object (How???) - [ ] - Sealed Trait (How???) + [*] - Trait (How???) + [*] - Sealed trait + [ ] - sealed abstract class (handle like sealed trait....) [*] - SelfRef [*] - Tuple [*] - Unknown (throw exception) [*] - Scala 2 (throw exception) [*] - TypeSymbol (throw exception) + + [ ] -- correct all the 'if $aE == null...' + [ ] -- type hint label mapping + [ ] -- type hint value mapping */ object JsonWriter: + final inline def lastPart(n: String) = n.split('.').last.stripSuffix("$") + // Tests whether we should write something or not--mainly in the case of Option, or wrapped Option def isOkToWrite(a: Any, cfg: JsonConfig) = a match @@ -142,19 +150,39 @@ object JsonWriter: case t: TraitRef[?] => classesSeen.put(t.typedName, t) - val rt = t.expr - '{ - given Compiler = Compiler.make($aE.getClass.getClassLoader) - val fn = (q: Quotes) ?=> { - import q.reflect.* - val sb = $sbE - val classRType = RType.inTermsOf[T]($aE.getClass).asInstanceOf[ScalaClassRType[T]].copy(renderTrait = Some($rt.name)).asInstanceOf[RType[T]] - JsonWriterRT.refWriteRT[classRType.T]($cfgE, classRType, $aE.asInstanceOf[classRType.T], $sbE)(using scala.collection.mutable.Map.empty[TypedName, RType[?]]) - Expr(1) // do-nothing... '{} requires Expr(something) be returned, so... + val rt = t.expr.asInstanceOf[Expr[TraitRType[T]]] + if t.childrenAreObject then + // case object -> just write the simple name of the object + '{ + $sbE.append('"') + $sbE.append(lastPart($aE.getClass.getName)) + $sbE.append('"') + } + else if t.isSealed then + // Wow! If this is a sealed trait, all the children already have correctly-typed parameters--no need for expensive inTermsOf() + '{ + val className = $aE.getClass.getName + $rt.sealedChildren + .find(_.name == className) + .map(foundKid => + val augmented = foundKid.asInstanceOf[ScalaClassRType[foundKid.T]].copy(renderTrait = Some($rt.name)).asInstanceOf[RType[foundKid.T]] + JsonWriterRT.refWriteRT[foundKid.T]($cfgE, augmented, $aE.asInstanceOf[foundKid.T], $sbE)(using scala.collection.mutable.Map.empty[TypedName, RType[?]]) + ) + .getOrElse(throw new JsonError(s"Unrecognized child $className of seald trait " + $rt.name)) + } + else + '{ + given Compiler = Compiler.make($aE.getClass.getClassLoader) + val fn = (q: Quotes) ?=> { + import q.reflect.* + val sb = $sbE + val classRType = RType.inTermsOf[T]($aE.getClass).asInstanceOf[ScalaClassRType[T]].copy(renderTrait = Some($rt.name)).asInstanceOf[RType[T]] + JsonWriterRT.refWriteRT[classRType.T]($cfgE, classRType, $aE.asInstanceOf[classRType.T], $sbE)(using scala.collection.mutable.Map.empty[TypedName, RType[?]]) + Expr(1) // do-nothing... '{} requires Expr(something) be returned, so... + } + quoted.staging.run(fn) + $sbE } - quoted.staging.run(fn) - $sbE - } case t: OptionRef[?] => if isMapKey then throw new JsonError("Option valuess cannot be map keys") @@ -248,23 +276,24 @@ object JsonWriter: '{ val sb = $sbE if $aE == null then sb.append("null") - sb.append('[') - val sbLen = sb.length - ${ - val tupleBuf = t.tupleRefs.zipWithIndex.foldLeft(sbE) { case (accE, (ref, i)) => - ref.refType match - case '[e] => - val fieldValue = Select.unique(aE.asTerm, "_" + (i + 1)).asExprOf[e] - '{ - val acc = $accE - ${ refWrite[e](cfgE, ref.asInstanceOf[RTypeRef[e]], fieldValue, '{ acc }) } - acc.append(',') - } + else + sb.append('[') + val sbLen = sb.length + ${ + val tupleBuf = t.tupleRefs.zipWithIndex.foldLeft(sbE) { case (accE, (ref, i)) => + ref.refType match + case '[e] => + val fieldValue = Select.unique(aE.asTerm, "_" + (i + 1)).asExprOf[e] + '{ + val acc = $accE + ${ refWrite[e](cfgE, ref.asInstanceOf[RTypeRef[e]], fieldValue, '{ acc }) } + acc.append(',') + } + } + tupleBuf } - tupleBuf - } - if sbLen == sb.length then sb.append(']') - else sb.setCharAt(sb.length() - 1, ']') + if sbLen == sb.length then sb.append(']') + else sb.setCharAt(sb.length() - 1, ']') } case t: JavaCollectionRef[?] => diff --git a/src/main/scala/co.blocke.scalajack/json/JsonWriter.scalax b/src/main/scala/co.blocke.scalajack/json/JsonWriter.scalax deleted file mode 100644 index a581b05f..00000000 --- a/src/main/scala/co.blocke.scalajack/json/JsonWriter.scalax +++ /dev/null @@ -1,395 +0,0 @@ -package co.blocke.scalajack -package json - -import co.blocke.scala_reflection.reflect.rtypeRefs.* -import co.blocke.scala_reflection.reflect.* -import co.blocke.scala_reflection.{RTypeRef, TypedName} -import co.blocke.scala_reflection.rtypes.* -import co.blocke.scala_reflection.Liftables.TypedNameToExpr -import scala.quoted.* -import co.blocke.scala_reflection.RType -import scala.jdk.CollectionConverters.* -import java.util.concurrent.ConcurrentHashMap -import scala.util.{Failure, Success} -import org.apache.commons.text.StringEscapeUtils - -object JsonWriter: - - // Tests whether we should write something or not--mainly in the case of Option, or wrapped Option - def isOkToWrite(a: Any, cfg: JsonConfig) = - a match - case None if !cfg.noneAsNull => false - case Left(None) if !cfg.noneAsNull => false - case Right(None) if !cfg.noneAsNull => false - case Failure(_) if cfg.tryFailureHandling == TryOption.NO_WRITE => false - case _ => true - - val refCache = new ConcurrentHashMap[TypedName, (?, StringBuilder, JsonConfig) => StringBuilder] - - def writeJsonFn[T](rtRef: RTypeRef[T], isMapKey: Boolean = false)(using tt: Type[T], q: Quotes): Expr[(T, StringBuilder, JsonConfig) => StringBuilder] = - import quotes.reflect.* - - rtRef match -//***** - case rt: PrimitiveRef[?] if rt.family == PrimFamily.Stringish => - val nullable = Expr(rt.isNullable) - '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - if $nullable && a == null then sb.append("null") - else - sb.append('"') - sb.append(StringEscapeUtils.escapeJson(a.toString)) - sb.append('"') - } -//***** - case rt: PrimitiveRef[?] => - val nullable = Expr(rt.isNullable) - if isMapKey then - '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - if $nullable && a == null then sb.append("null") - else - sb.append('"') - sb.append(a.toString) - sb.append('"') - } - else - '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - if $nullable && a == null then sb.append("null") - else sb.append(a.toString) - } - -//***** - case rt: AliasRef[?] => - val fn = writeJsonFn[rt.T](rt.unwrappedType.asInstanceOf[RTypeRef[rt.T]], isMapKey)(using Type.of[rt.T]) - '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => $fn(a, sb, cfg) } - -//***** - case rt: ArrayRef[?] => - if isMapKey then throw new JsonError("Arrays cannot be map keys.") - rt.elementRef.refType match - case '[t] => - val elementFn = writeJsonFn[t](rt.elementRef.asInstanceOf[RTypeRef[t]]) - '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - if a == null then sb.append("null") - else - sb.append('[') - val sbLen = sb.length - a.asInstanceOf[Array[t]].foreach { e => - if isOkToWrite(e, cfg) then - $elementFn(e.asInstanceOf[t], sb, cfg) - sb.append(',') - } - if sbLen == sb.length then sb.append(']') - else sb.setCharAt(sb.length() - 1, ']') - } - -//***** - case rt: ClassRef[?] => - if isMapKey then throw new JsonError("Classes cannot be map keys.") - val fieldFns = rt.fields.map { f => - f.fieldRef.refType match - case '[t] => (writeJsonFn[t](f.fieldRef.asInstanceOf[RTypeRef[t]]), f) - } - val typedName = Expr(rt.typedName) - '{ - val classFn = (a: T, sb: StringBuilder, cfg: JsonConfig) => - sb.append('{') - val sbLen = sb.length - ${ - val hintStmt = (rt match - case s: ScalaClassRef[?] if s.renderTrait.isDefined => - val traitName = Expr(s.renderTrait.get) - '{ - sb.append('"') - sb.append(cfg.typeHintLabelByTrait.getOrElse($traitName, cfg.typeHintLabel)) - sb.append('"') - sb.append(':') - sb.append('"') - val hint = cfg.typeHintTransformer.get(a.getClass.getName) match - case Some(xform) => xform(a) - case None => cfg.typeHintDefaultTransformer(a.getClass.getName) - sb.append(hint) - sb.append('"') - sb.append(',') - () - } - case _ => '{ () } - ) // .asInstanceOf[Expr[Unit]] - val stmts = hintStmt :: fieldFns.map { case (fn, field) => - '{ - val fieldValue = ${ - Select.unique('{ a }.asTerm, field.name).asExpr - } - if isOkToWrite(fieldValue, cfg) then - ${ - field.fieldRef.refType match - case '[t] => - '{ - sb.append('"') - sb.append(${ Expr(field.name) }) - sb.append('"') - sb.append(':') - val fn2 = $fn.asInstanceOf[(t, StringBuilder, JsonConfig) => StringBuilder] - fn2(fieldValue.asInstanceOf[t], sb, cfg) - sb.append(',') - } - } - } - } - Expr.ofList(stmts) - } - if sbLen == sb.length then sb.append('}') - else sb.setCharAt(sb.length() - 1, '}') - refCache.put($typedName, classFn) - classFn - } - - case rt: TraitRef[?] => - if isMapKey then throw new JsonError("Traits cannot be map keys.") - val typedName = Expr(rt.typedName) - val traitType = rt.expr - '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - val comboName = ($traitType.typedName.toString + "::" + a.getClass.getName).asInstanceOf[TypedName] - Option(refCache.get(comboName)) match - case Some(writerFn) => - writerFn.asInstanceOf[(T, StringBuilder, JsonConfig) => StringBuilder](a, sb, cfg) - case None => - val writerFn = ReflectUtil.inTermsOf[T]($traitType, a, sb, cfg) - refCache.put(comboName, writerFn) - writerFn(a, sb, cfg) - } - -//***** - case rt: SeqRef[?] => - if isMapKey then throw new JsonError("Seq instances cannot be map keys.") - rt.elementRef.refType match - case '[t] => - val elementFn = writeJsonFn[t](rt.elementRef.asInstanceOf[RTypeRef[t]]) - '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - sb.append('[') - val sbLen = sb.length - a.asInstanceOf[Seq[?]].foreach { e => - if isOkToWrite(e, cfg) then - $elementFn(e.asInstanceOf[t], sb, cfg) - sb.append(',') - } - if sbLen == sb.length then sb.append(']') - else sb.setCharAt(sb.length() - 1, ']') - } - -//***** - case rt: OptionRef[?] => - if isMapKey then throw new JsonError("Options cannot be map keys.") - rt.optionParamType.refType match - case '[t] => - val fn = writeJsonFn[t](rt.optionParamType.asInstanceOf[RTypeRef[t]]) - '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - a match - case None => sb.append("null") - case Some(v) => $fn(v.asInstanceOf[t], sb, cfg) - } - -//***** - case rt: TryRef[?] => - if isMapKey then throw new JsonError("Try cannot be map keys.") - rt.tryRef.refType match - case '[t] => - val fn = writeJsonFn[t](rt.tryRef.asInstanceOf[RTypeRef[t]]) - '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - a match - case Success(v) => $fn(v.asInstanceOf[t], sb, cfg) - case Failure(_) if cfg.tryFailureHandling == TryOption.AS_NULL => sb.append("null") - case Failure(v) => - sb.append('"') - sb.append(v.getMessage) - sb.append('"') - } - -//***** - case rt: MapRef[?] => - if isMapKey then throw new JsonError("Maps cannot be map keys.") - rt.elementRef.refType match - case '[k] => - rt.elementRef2.refType match - case '[v] => - val keyFn = writeJsonFn[k](rt.elementRef.asInstanceOf[RTypeRef[k]], true) - val valueFn = writeJsonFn[v](rt.elementRef2.asInstanceOf[RTypeRef[v]]) - '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - sb.append('{') - val sbLen = sb.length - a.asInstanceOf[Map[?, ?]].foreach { case (key, value) => - if isOkToWrite(value, cfg) then - $keyFn(key.asInstanceOf[k], sb, cfg) - sb.append(':') - $valueFn(value.asInstanceOf[v], sb, cfg) - sb.append(',') - } - if sbLen == sb.length then sb.append('}') - else sb.setCharAt(sb.length() - 1, '}') - } -//***** - case rt: LeftRightRef[?] => - if isMapKey then throw new JsonError("Union, intersection, or Either cannot be map keys.") - rt.leftRef.refType match - case '[lt] => - val leftFn = writeJsonFn[lt](rt.leftRef.asInstanceOf[RTypeRef[lt]]) - rt.rightRef.refType match - case '[rt] => - val rightFn = writeJsonFn[rt](rt.rightRef.asInstanceOf[RTypeRef[rt]]) - val rtypeExpr = rt.expr - '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - $rtypeExpr match - case r if r.clazz == classOf[Either[_, _]] => - a match - case Left(v) => - $leftFn(v.asInstanceOf[lt], sb, cfg) - case Right(v) => - $rightFn(v.asInstanceOf[rt], sb, cfg) - case _: RType[?] => // Intersection & Union types.... take your best shot! It's all we've got. No definitive info here. - val trial = new StringBuilder() - if scala.util.Try($rightFn(a.asInstanceOf[rt], trial, cfg)).isFailure then $leftFn(a.asInstanceOf[lt], sb, cfg) - else sb ++= trial - } -//***** - case rt: EnumRef[?] => - val expr = rt.expr - val isMapKeyExpr = Expr(isMapKey) - '{ - val rtype = $expr.asInstanceOf[EnumRType[?]] - (a: T, sb: StringBuilder, cfg: JsonConfig) => - val enumAsId = cfg.enumsAsIds match - case '*' => true - case aList: List[String] if aList.contains(rtype.name) => true - case _ => false - if enumAsId then - val enumVal = rtype.ordinal(a.toString).getOrElse(throw new JsonError(s"Value $a is not a valid enum value for ${rtype.name}")) - if $isMapKeyExpr then - sb.append('"') - sb.append(enumVal.toString) - sb.append('"') - else sb.append(enumVal.toString) - else - sb.append('"') - sb.append(a.toString) - sb.append('"') - } - - // TODO: Not sure this is right! - case rt: SealedTraitRef[?] => - '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - sb.append('"') - a.toString - sb.append('"') - } -//***** - case rt: TupleRef[?] => - if isMapKey then throw new JsonError("Tuples cannot be map keys.") - val elementFns = rt.tupleRefs.map { f => - f.refType match - case '[t] => (writeJsonFn[t](f.asInstanceOf[RTypeRef[t]]), f) - } - val numElementsExpr = Expr(elementFns.size) - '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - sb.append('[') - val sbLen = sb.length - ${ - val stmts = elementFns.zipWithIndex.map { case ((fn, e), i) => - '{ - val fieldValue = ${ - Select.unique('{ a }.asTerm, "_" + (i + 1)).asExpr - } - ${ - e.refType match - case '[t] => - '{ - val fn2 = $fn.asInstanceOf[(t, StringBuilder, JsonConfig) => StringBuilder] - fn2(fieldValue.asInstanceOf[t], sb, cfg) - sb.append(',') - } - } - } - } - Expr.ofList(stmts) - } - if sbLen == sb.length then sb.append(']') - else sb.setCharAt(sb.length() - 1, ']') - - } -//***** - case rt: SelfRefRef[?] => - if isMapKey then throw new JsonError("Classes or traits cannot be map keys.") - val e = rt.expr - '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - val fn = refCache.get($e.typedName).asInstanceOf[(T, StringBuilder, JsonConfig) => StringBuilder] - fn(a, sb, cfg) - } -//***** - case rt: JavaCollectionRef[?] => - if isMapKey then throw new JsonError("Collections cannot be map keys.") - rt.elementRef.refType match - case '[t] => - val elementFn = writeJsonFn[t](rt.elementRef.asInstanceOf[RTypeRef[t]]) - '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - sb.append('[') - val sbLen = sb.length - a.asInstanceOf[java.util.Collection[?]].toArray.foreach { e => - if isOkToWrite(e, cfg) then - $elementFn(e.asInstanceOf[t], sb, cfg) - sb.append(',') - } - if sbLen == sb.length then sb.append(']') - else sb.setCharAt(sb.length() - 1, ']') - } - - case rt: JavaMapRef[?] => - if isMapKey then throw new JsonError("Maps cannot be map keys.") - rt.elementRef.refType match - case '[k] => - rt.elementRef2.refType match - case '[v] => - val keyFn = writeJsonFn[k](rt.elementRef.asInstanceOf[RTypeRef[k]], true) - val valueFn = writeJsonFn[v](rt.elementRef.asInstanceOf[RTypeRef[v]]) - '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - sb.append('{') - val sbLen = sb.length - a.asInstanceOf[java.util.Map[?, ?]].asScala.foreach { case (key, value) => - if isOkToWrite(value, cfg) then - $keyFn(key.asInstanceOf[k], sb, cfg) - sb.append(':') - $valueFn(value.asInstanceOf[v], sb, cfg) - sb.append(',') - } - if sbLen == sb.length then sb.append('}') - else sb.setCharAt(sb.length() - 1, '}') - } -//***** - case rt: ObjectRef => - val name = Expr(rt.name) - '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - sb.append('"') - sb.append($name) - sb.append('"') - } -//***** - case rt: Scala2Ref[?] => - val name = Expr(rt.name) - '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - sb.append('"') - sb.append($name) - sb.append('"') - } - - // case rt: NeoTypeRef[?] => - // import neotype.* - // rt.refType match - // case '[r] => - // '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - // def goGet: T = summon[Newtype.WithType[?, T]] - // val unwrapped = a.asInstanceOf[Newtype[?]].unwrap - // } -//***** - case rt: UnknownRef[?] => - '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - sb.append('"') - sb.append("unknown") - sb.append('"') - } diff --git a/src/main/scala/co.blocke.scalajack/json/ReflectUtil.scalax b/src/main/scala/co.blocke.scalajack/json/ReflectUtil.scalax deleted file mode 100644 index c8045491..00000000 --- a/src/main/scala/co.blocke.scalajack/json/ReflectUtil.scalax +++ /dev/null @@ -1,59 +0,0 @@ -package co.blocke.scalajack -package json - -import co.blocke.scala_reflection.{ReflectException, RType, RTypeRef, TypedName} -import co.blocke.scala_reflection.rtypes.TraitRType -import co.blocke.scala_reflection.reflect.rtypeRefs.* -import co.blocke.scala_reflection.reflect.* -import scala.quoted.* -import scala.quoted.staging.* - -object ReflectUtil: - - /** This function takes the RType of a trait and an instance of T and does two things. - * First it expresses the instance's class *in terms of* the trait's concrete type parameters (if any). - * Then it generates a writer function for the now correctly-typed class. - */ -// Returns: StringBuilder - def inTermsOf[T](traitType: TraitRType[?], a: T, aE: Expr[T], sbE: Expr[StringBuilder], cfgE: Expr[JsonConfig]) = - given Compiler = Compiler.make(getClass.getClassLoader) - - val clazz = a.getClass - - val fn = (quotes: Quotes) ?=> { - import quotes.reflect.* - - val classRef = ReflectOnType(quotes)(quotes.reflect.TypeRepr.typeConstructorOf(clazz), false)(using scala.collection.mutable.Map.empty[TypedName, Boolean]).asInstanceOf[ScalaClassRef[?]] - - val inTermsOfRef = traitType.toType(quotes) match - case '[t] => - if traitType.typeParamSymbols.nonEmpty then - val seenBefore = scala.collection.mutable.Map.empty[TypedName, Boolean] - val paths = classRef.typePaths.getOrElse(traitType.name, throw new ReflectException(s"No path in class ${classRef.name} for trait ${traitType.name}")) - val typeParamTypes = TypeSymbolMapper.runPath(quotes)(paths, TypeRepr.of[t]) - val classQuotedTypeRepr = TypeRepr.typeConstructorOf(clazz) - ReflectOnType(quotes)(classQuotedTypeRepr.appliedTo(typeParamTypes))(using seenBefore) - else classRef - - inTermsOfRef.refType match - case '[e] => - val asClassRef = inTermsOfRef.asInstanceOf[ScalaClassRef[e]].copy(renderTrait = Some(traitType.name)).asInstanceOf[co.blocke.scala_reflection.RTypeRef[e]] - JsonWriter.refWrite[e](cfgE, asClassRef, aE.asInstanceOf[Expr[e]], sbE) - } - run(fn) - - def inTermsOf2[T](traitType: TraitRType[?], clazz: Class[?])(using Quotes) = - import quotes.reflect.* - - val classRef = ReflectOnType(quotes)(quotes.reflect.TypeRepr.typeConstructorOf(clazz), false)(using scala.collection.mutable.Map.empty[TypedName, Boolean]).asInstanceOf[ScalaClassRef[?]] - - (traitType.toType(quotes) match - case '[t] => - if traitType.typeParamSymbols.nonEmpty then - val seenBefore = scala.collection.mutable.Map.empty[TypedName, Boolean] - val paths = classRef.typePaths.getOrElse(traitType.name, throw new ReflectException(s"No path in class ${classRef.name} for trait ${traitType.name}")) - val typeParamTypes = TypeSymbolMapper.runPath(quotes)(paths, TypeRepr.of[t]) - val classQuotedTypeRepr = TypeRepr.typeConstructorOf(clazz) - ReflectOnType(quotes)(classQuotedTypeRepr.appliedTo(typeParamTypes))(using seenBefore) - else classRef - ).asInstanceOf[ScalaClassRef[?]] diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index ffd08df9..ba3f4315 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -4,9 +4,6 @@ package run import co.blocke.scala_reflection.* import scala.jdk.CollectionConverters.* -enum Color: - case Red, Green, Blue - object RunMe extends App: /* @@ -23,13 +20,12 @@ object RunMe extends App: // .copy(enumsAsIds = '*') try - val s = new java.util.HashMap[Int, Boolean]() - s.put(1, true) - s.put(0, false) - val v = Person("Greg", s) + val v = Person("Greg", DIAMOND, Command(15), Foom(3)) println("HERE: " + ScalaJack.write(v)) + // println(RType.of[Person[Int]]) + // println("HERE: " + ScalaJack.write(Person("Greg", Foom('z'), Some(Person("Lili", Foom('x'), None))))) // val now = System.currentTimeMillis() diff --git a/src/main/scala/co.blocke.scalajack/run/Sample.scala b/src/main/scala/co.blocke.scalajack/run/Sample.scala index e0681803..8739f15b 100644 --- a/src/main/scala/co.blocke.scalajack/run/Sample.scala +++ b/src/main/scala/co.blocke.scalajack/run/Sample.scala @@ -2,50 +2,26 @@ package co.blocke.scalajack.run import neotype.* -// opaque type BigName = String +// Enumeration sealed trait +sealed trait card extends Enumeration +case object CLUB extends card +case object HEART extends card +case object DIAMOND extends card +case object SPADE extends card -// case class Person(name: String, age: Int, isOk: List[Boolean], favColor: Colors, boss: BigName) - -// trait Animal: -// val name: String -// val numLegs: Int -// val friend: Option[Animal] - -// trait Animal2: -// val name: String -// val numLegs: Int -// val friend: Option[Animal2] - -// case class Dog(name: String, numLegs: Int, carsChased: Int, friend: Option[Animal2]) extends Animal2 +sealed trait msg[X] +case class Command[T](item: T) extends msg[T] +case class Query[T](item: T) extends msg[T] enum Colors: case Red, Blue, Green -// import scala.collection.immutable.* -// enum Vehicle: -// case Car, Bus, Train - -object WeekDay extends Enumeration { - type WeekDay = Value - val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value -} -import WeekDay.* - -// case class Simple(a: Int, b: Boolean, c: Option[Simple], z: Int = 5) - -// case class Blah(msg: String, stuff: WeekDay) - -// object Talk: -// def say(s: String): String = s"Say $s!" - -// case class M1(v: Map[Long, Int], v2: HashMap[Colors, Int], v3: Map[co.blocke.scala_reflection.TypedName, Int]) - trait Miss[E] { val x: E } case class Foom[X](x: X) extends Miss[X] // case class Person[Y](name: String, age: Miss[Y], again: Option[Person[Y]]) -case class Person(name: String, color: java.util.HashMap[Int, Boolean]) +case class Person[T](name: String, card: card, msg: msg[T], miss: Miss[T]) // type NonEmptyString = NonEmptyString.Type // given NonEmptyString: Newtype[String] with From e48b358705ad9cba93a75ae72c2889e82f16b895 Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Wed, 8 Nov 2023 01:01:27 -0600 Subject: [PATCH 22/65] most of write finished --- .../scalajack/AESEncryptionDecryption.java | 50 ++++ .../java/co/blocke/scalajack/TypeHint.java | 8 + .../co.blocke.scalajack/json/JsonWriter.scala | 216 ++++++++++++------ .../json/JsonWriterRT.scala | 189 +++++++++++---- .../scala/co.blocke.scalajack/run/Play.scala | 4 +- .../co.blocke.scalajack/run/Sample.scala | 15 +- 6 files changed, 363 insertions(+), 119 deletions(-) create mode 100644 src/main/java/co/blocke/scalajack/AESEncryptionDecryption.java create mode 100644 src/main/java/co/blocke/scalajack/TypeHint.java diff --git a/src/main/java/co/blocke/scalajack/AESEncryptionDecryption.java b/src/main/java/co/blocke/scalajack/AESEncryptionDecryption.java new file mode 100644 index 00000000..b03ebddb --- /dev/null +++ b/src/main/java/co/blocke/scalajack/AESEncryptionDecryption.java @@ -0,0 +1,50 @@ +package co.blocke.scalajack; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.InvalidKeyException; +import java.io.UnsupportedEncodingException; +import javax.crypto.IllegalBlockSizeException; +import java.util.Arrays; +import java.util.Base64; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.BadPaddingException; + +/** + * Java String Encryption Decryption Example + * @author Ramesh Fadatare + * + */ +public class AESEncryptionDecryption { + private static SecretKeySpec secretKey; + private static byte[] key; + private static final String ALGORITHM = "AES"; + + public void prepareSecreteKey(String myKey) throws NoSuchAlgorithmException { + MessageDigest sha = null; + key = myKey.getBytes(StandardCharsets.UTF_8); + sha = MessageDigest.getInstance("SHA-1"); + key = sha.digest(key); + key = Arrays.copyOf(key, 16); + secretKey = new SecretKeySpec(key, ALGORITHM); + } + + public String encrypt(String strToEncrypt, String secret) + throws NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException, IllegalBlockSizeException, NoSuchPaddingException, BadPaddingException { + prepareSecreteKey(secret); + Cipher cipher = Cipher.getInstance(ALGORITHM); + cipher.init(Cipher.ENCRYPT_MODE, secretKey); + return Base64.getEncoder().encodeToString(cipher.doFinal(strToEncrypt.getBytes("UTF-8"))); + } + + public String decrypt(String strToDecrypt, String secret) throws NoSuchAlgorithmException, NoSuchPaddingException, BadPaddingException, InvalidKeyException, IllegalBlockSizeException { + prepareSecreteKey(secret); + Cipher cipher = Cipher.getInstance(ALGORITHM); + cipher.init(Cipher.DECRYPT_MODE, secretKey); + return new String(cipher.doFinal(Base64.getDecoder().decode(strToDecrypt))); + } +} \ No newline at end of file diff --git a/src/main/java/co/blocke/scalajack/TypeHint.java b/src/main/java/co/blocke/scalajack/TypeHint.java new file mode 100644 index 00000000..9d958b9c --- /dev/null +++ b/src/main/java/co/blocke/scalajack/TypeHint.java @@ -0,0 +1,8 @@ +package co.blocke.scalajack; + +import java.lang.annotation.*; + +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface TypeHint{} + diff --git a/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala b/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala index 4ec474d1..869240c1 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala @@ -3,7 +3,7 @@ package json import scala.quoted.* import co.blocke.scala_reflection.* -import co.blocke.scala_reflection.rtypes.{EnumRType, ScalaClassRType, TraitRType} +import co.blocke.scala_reflection.rtypes.{EnumRType, NonConstructorFieldInfo, ScalaClassRType, TraitRType} import co.blocke.scala_reflection.reflect.{ReflectOnType, TypeSymbolMapper} import co.blocke.scala_reflection.reflect.rtypeRefs.* import scala.jdk.CollectionConverters.* @@ -12,8 +12,8 @@ import scala.quoted.staging.* /* TODO: - [ ] - Scala non-case class - [ ] - Java class (Do I still want to support this???) + [*] - Scala non-case class + [*] - Java class (Do I still want to support this???) [*] - Enum [*] - Enumeration [*] - Java Enum @@ -25,16 +25,22 @@ import scala.quoted.staging.* [*] - Object (How???) [*] - Trait (How???) [*] - Sealed trait - [ ] - sealed abstract class (handle like sealed trait....) + [*] - sealed abstract class (handle like sealed trait....) [*] - SelfRef [*] - Tuple [*] - Unknown (throw exception) [*] - Scala 2 (throw exception) [*] - TypeSymbol (throw exception) + [*] - Value class - [ ] -- correct all the 'if $aE == null...' - [ ] -- type hint label mapping - [ ] -- type hint value mapping + [*] -- correct all the 'if $aE == null...' + [*] -- type hint label mapping + [*] -- type hint value mapping + [*] -- Discontinue use of inTermsOf "open" (non-sealed) trait support (?!) + [*] -- update runtime-size TraitRType handling to match new compile-time code + + [ ] -- Streaming JSON write support + [ ] -- BigJSON support (eg. multi-gig file) */ object JsonWriter: @@ -45,6 +51,7 @@ object JsonWriter: def isOkToWrite(a: Any, cfg: JsonConfig) = a match case None if !cfg.noneAsNull => false + case o: java.util.Optional[?] if o.isEmpty && !cfg.noneAsNull => false case Left(None) if !cfg.noneAsNull => false case Right(None) if !cfg.noneAsNull => false case Failure(_) if cfg.tryFailureHandling == TryOption.NO_WRITE => false @@ -62,15 +69,16 @@ object JsonWriter: ref match case t: PrimitiveRef[?] if t.family == PrimFamily.Stringish => '{ if $aE == null then $sbE.append("null") else $sbE.append("\"" + $aE.toString + "\"") } case t: PrimitiveRef[?] => + val isNullable = Expr(t.isNullable) if isMapKey then '{ - if $aE == null then $sbE.append("\"null\"") + if $isNullable && $aE == null then $sbE.append("\"null\"") else $sbE.append('"') $sbE.append($aE.toString) $sbE.append('"') } - else '{ if $aE == null then $sbE.append("null") else $sbE.append($aE.toString) } + else '{ if $isNullable && $aE == null then $sbE.append("null") else $sbE.append($aE.toString) } case t: SeqRef[?] => if isMapKey then throw new JsonError("Seq instances cannot be map keys") @@ -117,8 +125,44 @@ object JsonWriter: else sb.setCharAt(sb.length() - 1, ']') } - case t: ClassRef[?] => + case t: ScalaClassRef[?] if t.isSealed && t.isAbstractClass => + classesSeen.put(t.typedName, t) + if t.childrenAreObject then + // case object -> just write the simple name of the object + '{ + if $aE == null then $sbE.append("null") + else + $sbE.append('"') + $sbE.append(lastPart($aE.getClass.getName)) + $sbE.append('"') + } + else + // Wow! If this is a sealed trait, all the children already have correctly-typed parameters--no need for expensive inTermsOf() + val rt = t.expr.asInstanceOf[Expr[ScalaClassRType[T]]] + '{ + if $aE == null then $sbE.append("null") + else + val className = $aE.getClass.getName + $rt.sealedChildren + .find(_.name == className) + .map(foundKid => + val augmented = foundKid.asInstanceOf[ScalaClassRType[foundKid.T]].copy(renderTrait = Some($rt.name)).asInstanceOf[RType[foundKid.T]] + JsonWriterRT.refWriteRT[foundKid.T]($cfgE, augmented, $aE.asInstanceOf[foundKid.T], $sbE)(using scala.collection.mutable.Map.empty[TypedName, RType[?]]) + ) + .getOrElse(throw new JsonError(s"Unrecognized child $className of seald trait " + $rt.name)) + } + + case t: ScalaClassRef[?] if t.isValueClass => + val theField = t.fields.head.fieldRef + theField.refType match + case '[e] => + val fieldValue = Select.unique(aE.asTerm, t.fields.head.name).asExprOf[e] + refWrite[e](cfgE, theField.asInstanceOf[RTypeRef[e]], fieldValue, sbE) + + case t: ScalaClassRef[?] => + if t.isAbstractClass then throw new JsonError("Cannot serialize an abstract class") classesSeen.put(t.typedName, t) + val isCase = Expr(t.isCaseClass) '{ val sb = $sbE if $aE == null then sb.append("null") @@ -144,6 +188,26 @@ object JsonWriter: } } } + if ! $isCase && $cfgE.writeNonConstructorFields then + ${ + t.nonConstructorFields.foldLeft('{ sb }) { (accE, f) => + f.fieldRef.refType match + case '[e] => + val fieldValue = Select.unique(aE.asTerm, f.getterLabel).asExprOf[e] + val name = Expr(f.name) + '{ + val acc = $accE + if isOkToWrite($fieldValue, $cfgE) then + acc.append('"') + acc.append($name) + acc.append('"') + acc.append(':') + ${ refWrite[e](cfgE, f.fieldRef.asInstanceOf[RTypeRef[e]], fieldValue, '{ acc }) } + acc.append(',') + else acc + } + } + } if sbLen == sb.length then sb.append('}') else sb.setCharAt(sb.length() - 1, '}') } @@ -154,45 +218,53 @@ object JsonWriter: if t.childrenAreObject then // case object -> just write the simple name of the object '{ - $sbE.append('"') - $sbE.append(lastPart($aE.getClass.getName)) - $sbE.append('"') + if $aE == null then $sbE.append("null") + else + $sbE.append('"') + $sbE.append(lastPart($aE.getClass.getName)) + $sbE.append('"') } else if t.isSealed then // Wow! If this is a sealed trait, all the children already have correctly-typed parameters--no need for expensive inTermsOf() '{ - val className = $aE.getClass.getName - $rt.sealedChildren - .find(_.name == className) - .map(foundKid => - val augmented = foundKid.asInstanceOf[ScalaClassRType[foundKid.T]].copy(renderTrait = Some($rt.name)).asInstanceOf[RType[foundKid.T]] - JsonWriterRT.refWriteRT[foundKid.T]($cfgE, augmented, $aE.asInstanceOf[foundKid.T], $sbE)(using scala.collection.mutable.Map.empty[TypedName, RType[?]]) - ) - .getOrElse(throw new JsonError(s"Unrecognized child $className of seald trait " + $rt.name)) - } - else - '{ - given Compiler = Compiler.make($aE.getClass.getClassLoader) - val fn = (q: Quotes) ?=> { - import q.reflect.* - val sb = $sbE - val classRType = RType.inTermsOf[T]($aE.getClass).asInstanceOf[ScalaClassRType[T]].copy(renderTrait = Some($rt.name)).asInstanceOf[RType[T]] - JsonWriterRT.refWriteRT[classRType.T]($cfgE, classRType, $aE.asInstanceOf[classRType.T], $sbE)(using scala.collection.mutable.Map.empty[TypedName, RType[?]]) - Expr(1) // do-nothing... '{} requires Expr(something) be returned, so... - } - quoted.staging.run(fn) - $sbE + if $aE == null then $sbE.append("null") + else + val className = $aE.getClass.getName + $rt.sealedChildren + .find(_.name == className) + .map(foundKid => + val augmented = foundKid.asInstanceOf[ScalaClassRType[foundKid.T]].copy(renderTrait = Some($rt.name)).asInstanceOf[RType[foundKid.T]] + JsonWriterRT.refWriteRT[foundKid.T]($cfgE, augmented, $aE.asInstanceOf[foundKid.T], $sbE)(using scala.collection.mutable.Map.empty[TypedName, RType[?]]) + ) + .getOrElse(throw new JsonError(s"Unrecognized child $className of seald trait " + $rt.name)) } + else throw new JsonError("non-sealed traits are not supported") + // '{ + // if $aE == null then $sbE.append("null") + // else + // given Compiler = Compiler.make($aE.getClass.getClassLoader) + // val fn = (q: Quotes) ?=> { + // import q.reflect.* + // val sb = $sbE + // val classRType = RType.inTermsOf[T]($aE.getClass).asInstanceOf[ScalaClassRType[T]].copy(renderTrait = Some($rt.name)).asInstanceOf[RType[T]] + // JsonWriterRT.refWriteRT[classRType.T]($cfgE, classRType, $aE.asInstanceOf[classRType.T], $sbE)(using scala.collection.mutable.Map.empty[TypedName, RType[?]]) + // Expr(1) // do-nothing... '{} requires Expr(something) be returned, so... + // } + // quoted.staging.run(fn) + // $sbE + // } case t: OptionRef[?] => if isMapKey then throw new JsonError("Option valuess cannot be map keys") t.optionParamType.refType match case '[e] => '{ - $aE match - case None => $sbE.append("null") - case Some(v) => - ${ refWrite[e](cfgE, t.optionParamType.asInstanceOf[RTypeRef[e]], '{ v }.asInstanceOf[Expr[e]], sbE) } + if $aE == null then $sbE.append("null") + else + $aE match + case None => $sbE.append("null") + case Some(v) => + ${ refWrite[e](cfgE, t.optionParamType.asInstanceOf[RTypeRef[e]], '{ v }.asInstanceOf[Expr[e]], sbE) } } case t: MapRef[?] => @@ -258,17 +330,19 @@ object JsonWriter: t.tryRef.refType match case '[e] => '{ - $aE match - case Success(v) => - ${ refWrite[e](cfgE, t.tryRef.asInstanceOf[RTypeRef[e]], '{ v }.asInstanceOf[Expr[e]], sbE) } - case Failure(_) if $cfgE.tryFailureHandling == TryOption.AS_NULL => $sbE.append("null") - case Failure(_) if $cfgE.tryFailureHandling == TryOption.AS_NULL => $sbE.append("null") - case Failure(f) if $cfgE.tryFailureHandling == TryOption.THROW_EXCEPTION => - throw new JsonError("A try value was Failure with message: " + f.getMessage()) - case Failure(v) => - $sbE.append("\"Failure(") - $sbE.append(v.getMessage) - $sbE.append(")\"") + if $aE == null then $sbE.append("null") + else + $aE match + case Success(v) => + ${ refWrite[e](cfgE, t.tryRef.asInstanceOf[RTypeRef[e]], '{ v }.asInstanceOf[Expr[e]], sbE) } + case Failure(_) if $cfgE.tryFailureHandling == TryOption.AS_NULL => $sbE.append("null") + case Failure(_) if $cfgE.tryFailureHandling == TryOption.AS_NULL => $sbE.append("null") + case Failure(f) if $cfgE.tryFailureHandling == TryOption.THROW_EXCEPTION => + throw new JsonError("A try value was Failure with message: " + f.getMessage()) + case Failure(v) => + $sbE.append("\"Failure(") + $sbE.append(v.getMessage) + $sbE.append(")\"") } case t: TupleRef[?] => @@ -355,24 +429,34 @@ object JsonWriter: val enumE = t.expr val isMapKeyE = Expr(isMapKey) '{ - val enumRT = $enumE.asInstanceOf[EnumRType[T]] - val enumAsId = $cfgE.enumsAsIds match - case '*' => true - case aList: List[String] if aList.contains(enumRT.name) => true - case _ => false - if enumAsId then - val enumVal = enumRT.ordinal($aE.toString).getOrElse(throw new JsonError("Value " + $aE.toString + s" is not a valid enum value for ${enumRT.name}")) - if $isMapKeyE then + if $aE == null then $sbE.append("null") + else + val enumRT = $enumE.asInstanceOf[EnumRType[T]] + val enumAsId = $cfgE.enumsAsIds match + case '*' => true + case aList: List[String] if aList.contains(enumRT.name) => true + case _ => false + if enumAsId then + val enumVal = enumRT.ordinal($aE.toString).getOrElse(throw new JsonError("Value " + $aE.toString + s" is not a valid enum value for ${enumRT.name}")) + if $isMapKeyE then + $sbE.append('"') + $sbE.append(enumVal.toString) + $sbE.append('"') + else $sbE.append(enumVal.toString) + else $sbE.append('"') - $sbE.append(enumVal.toString) + $sbE.append($aE.toString) $sbE.append('"') - else $sbE.append(enumVal.toString) - else - $sbE.append('"') - $sbE.append($aE.toString) - $sbE.append('"') } + // Just handle Java classes 100% runtime since we need to leverage Java reflection entirely anyway + case t: JavaClassRef[?] => + t.refType match + case '[e] => + '{ + JsonWriterRT.refWriteRT[e]($cfgE, ${ t.expr }.asInstanceOf[RType[e]], $aE.asInstanceOf[e], $sbE)(using scala.collection.mutable.Map.empty[TypedName, RType[?]]) + } + case t: ObjectRef => val tname = Expr(t.name) '{ @@ -385,7 +469,7 @@ object JsonWriter: $cfgE.undefinedFieldHandling match case UndefinedValueOption.AS_NULL => $sbE.append("null") case UndefinedValueOption.AS_SYMBOL => $sbE.append("\"" + $tname + "\"") - case UndefinedValueOption.THROW_EXCEPTION => throw new JsonError("Value " + $aE.toString + " is of some unknown/unsupported Scala 2 type " + $tname) + case UndefinedValueOption.THROW_EXCEPTION => throw new JsonError("Unknown/unsupported Scala 2 type " + $tname) } case t: UnknownRef[?] => @@ -394,7 +478,7 @@ object JsonWriter: $cfgE.undefinedFieldHandling match case UndefinedValueOption.AS_NULL => $sbE.append("null") case UndefinedValueOption.AS_SYMBOL => $sbE.append("\"" + $tname + "\"") - case UndefinedValueOption.THROW_EXCEPTION => throw new JsonError("Value " + $aE.toString + " is of some unknown/unsupported type " + $tname) + case UndefinedValueOption.THROW_EXCEPTION => throw new JsonError("Unknown/unsupported type " + $tname) } case t: TypeSymbolRef => @@ -403,5 +487,5 @@ object JsonWriter: $cfgE.undefinedFieldHandling match case UndefinedValueOption.AS_NULL => $sbE.append("null") case UndefinedValueOption.AS_SYMBOL => $sbE.append("\"" + $tname + "\"") - case UndefinedValueOption.THROW_EXCEPTION => throw new JsonError("Value " + $aE.toString + " is of some unknown/unsupported type " + $tname + ". (Class didn't fully define all its type parameters.)") + case UndefinedValueOption.THROW_EXCEPTION => throw new JsonError("Unknown/unsupported type " + $tname + ". (Class didn't fully define all its type parameters.)") } diff --git a/src/main/scala/co.blocke.scalajack/json/JsonWriterRT.scala b/src/main/scala/co.blocke.scalajack/json/JsonWriterRT.scala index d217d38f..4ceced2a 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonWriterRT.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonWriterRT.scala @@ -23,14 +23,8 @@ import scala.util.{Failure, Success, Try} object JsonWriterRT: - // Tests whether we should write something or not--mainly in the case of Option, or wrapped Option - def isOkToWrite(a: Any, cfg: JsonConfig) = - a match - case None if !cfg.noneAsNull => false - case Left(None) if !cfg.noneAsNull => false - case Right(None) if !cfg.noneAsNull => false - case Failure(_) if cfg.tryFailureHandling == TryOption.NO_WRITE => false - case _ => true + val secret = "N@rrow !s the w@y" + val encryptionDecryption = new AESEncryptionDecryption() def refWriteRT[T]( cfg: JsonConfig, @@ -40,15 +34,15 @@ object JsonWriterRT: isMapKey: Boolean = false )(using classesSeen: scala.collection.mutable.Map[TypedName, RType[?]]): StringBuilder = rt match - case StringRType() | CharRType() | JavaCharacterRType() => if a == null then sb.append("null") else sb.append("\"" + a.toString + "\"") + case StringRType(_) | CharRType(_) | JavaCharacterRType(_) => if a == null then sb.append("null") else sb.append("\"" + a.toString + "\"") case t: PrimitiveRType => if isMapKey then - if a == null then sb.append("\"null\"") + if t.isNullable && a == null then sb.append("\"null\"") else sb.append('"') sb.append(a.toString) sb.append('"') - else if a == null then sb.append("null") + else if t.isNullable && a == null then sb.append("null") else sb.append(a.toString) case t: SeqRType[?] => @@ -57,7 +51,7 @@ object JsonWriterRT: else sb.append('[') val sbLen = sb.length a.asInstanceOf[Seq[t.elementType.T]].foldLeft(sb) { (acc, one) => - if isOkToWrite(one, cfg) then + if JsonWriter.isOkToWrite(one, cfg) then refWriteRT[t.elementType.T](cfg, t.elementType.asInstanceOf[RType[t.elementType.T]], one, acc) sb.append(',') else sb @@ -71,7 +65,7 @@ object JsonWriterRT: else sb.append('[') val sbLen = sb.length a.asInstanceOf[Seq[t.elementType.T]].foldLeft(sb) { (acc, one) => - if isOkToWrite(one, cfg) then + if JsonWriter.isOkToWrite(one, cfg) then refWriteRT[t.elementType.T](cfg, t.elementType.asInstanceOf[RType[t.elementType.T]], one, acc) sb.append(',') else sb @@ -79,18 +73,56 @@ object JsonWriterRT: if sbLen == sb.length then sb.append(']') else sb.setCharAt(sb.length() - 1, ']') + case t: ScalaClassRType[?] if t.isSealed && t.isAbstractClass => + classesSeen.put(t.typedName, t) + if a == null then sb.append("null") + else + val className = a.getClass.getName + if t.childrenAreObject then + // case object -> just write the simple name of the object + sb.append('"') + sb.append(JsonWriter.lastPart(className)) + sb.append('"') + else + // Wow! If this is a sealed trait, all the children already have correctly-typed parameters--no need for expensive inTermsOf() + t.sealedChildren + .find(_.name == className) + .map(foundKid => + val augmented = foundKid.asInstanceOf[ScalaClassRType[foundKid.T]].copy(renderTrait = Some(t.name)).asInstanceOf[RType[foundKid.T]] + JsonWriterRT.refWriteRT[foundKid.T](cfg, augmented, a.asInstanceOf[foundKid.T], sb) + ) + .getOrElse(throw new JsonError(s"Unrecognized child $className of seald trait " + t.name)) + case t: ScalaClassRType[?] => + if t.isAbstractClass then throw new JsonError("Cannot serialize an abstract class") classesSeen.put(t.typedName, t) if a == null then sb.append("null") else sb.append('{') val sbLen = sb.length - t.renderTrait.map(traitName => sb.append(s"\"_hint\":\"$traitName\",")) + t.renderTrait.map { traitName => + sb.append('"') + sb.append(cfg.typeHintLabelByTrait.getOrElse(traitName, cfg.typeHintLabel)) + sb.append('"') + sb.append(':') + sb.append('"') + val hint = t.annotations + .get("co.blocke.scalajack.TypeHint") + .map(_ => encryptionDecryption.encrypt(JsonWriter.lastPart(t.name), secret)) // only need lat part of class name because the rest is the same as the parent + .getOrElse { + cfg.typeHintTransformer.get(a.getClass.getName) match + case Some(xform) => xform(a) + case None => cfg.typeHintDefaultTransformer(a.getClass.getName) + } + sb.append(hint) + sb.append('"') + sb.append(',') + } t.fields.foldLeft(sb) { (acc, f) => val m = a.getClass.getMethod(f.name) m.setAccessible(true) val fieldValue = m.invoke(a).asInstanceOf[f.fieldType.T] - if isOkToWrite(fieldValue, cfg) then + if JsonWriter.isOkToWrite(fieldValue, cfg) then acc.append('"') acc.append(f.name) acc.append('"') @@ -99,20 +131,52 @@ object JsonWriterRT: acc.append(',') else acc } + if !t.isCaseClass && cfg.writeNonConstructorFields then + t.nonConstructorFields.foldLeft(sb) { (acc, f) => + val m = a.getClass.getMethod(f.getterLabel) + m.setAccessible(true) + val fieldValue = m.invoke(a).asInstanceOf[f.fieldType.T] + if JsonWriter.isOkToWrite(fieldValue, cfg) then + acc.append('"') + acc.append(f.name) + acc.append('"') + acc.append(':') + refWriteRT[f.fieldType.T](cfg, f.fieldType.asInstanceOf[RType[f.fieldType.T]], fieldValue, acc) + acc.append(',') + else acc + } if sbLen == sb.length then sb.append('}') else sb.setCharAt(sb.length() - 1, '}') case t: TraitRType[?] => classesSeen.put(t.typedName, t) - val classRType = RType.inTermsOf[T](a.getClass).asInstanceOf[ScalaClassRType[T]].copy(renderTrait = Some(t.name)).asInstanceOf[RType[T]] - JsonWriterRT.refWriteRT[classRType.T](cfg, classRType, a.asInstanceOf[classRType.T], sb) + if a == null then sb.append("null") + if t.childrenAreObject then + sb.append('"') + sb.append(JsonWriter.lastPart(a.getClass.getName)) + sb.append('"') + else if t.isSealed then + // Wow! If this is a sealed trait, all the children already have correctly-typed parameters--no need for expensive inTermsOf() + val className = a.getClass.getName + t.sealedChildren + .find(_.name == className) + .map(foundKid => + val augmented = foundKid.asInstanceOf[ScalaClassRType[foundKid.T]].copy(renderTrait = Some(t.name)).asInstanceOf[RType[foundKid.T]] + JsonWriterRT.refWriteRT[foundKid.T](cfg, augmented, a.asInstanceOf[foundKid.T], sb) + ) + .getOrElse(throw new JsonError(s"Unrecognized child $className of seald trait " + t.name)) + else throw new JsonError("non-sealed traits are not supported") + // val classRType = RType.inTermsOf[T](a.getClass).asInstanceOf[ScalaClassRType[T]].copy(renderTrait = Some(t.name)).asInstanceOf[RType[T]] + // JsonWriterRT.refWriteRT[classRType.T](cfg, classRType, a.asInstanceOf[classRType.T], sb) case t: OptionRType[?] => if isMapKey then throw new JsonError("Option valuess cannot be map keys") - a match - case None => sb.append("null") - case Some(v) => - refWriteRT[t.optionParamType.T](cfg, t.optionParamType.asInstanceOf[RType[t.optionParamType.T]], v.asInstanceOf[t.optionParamType.T], sb) + if a == null then sb.append("null") + else + a match + case None => sb.append("null") + case Some(v) => + refWriteRT[t.optionParamType.T](cfg, t.optionParamType.asInstanceOf[RType[t.optionParamType.T]], v.asInstanceOf[t.optionParamType.T], sb) case t: MapRType[?] => if isMapKey then throw new JsonError("Map values cannot be map keys") @@ -121,7 +185,7 @@ object JsonWriterRT: sb.append('{') val sbLen = sb.length a.asInstanceOf[Map[?, ?]].foreach { case (key, value) => - if isOkToWrite(value, cfg) then + if JsonWriter.isOkToWrite(value, cfg) then val b = refWriteRT[t.elementType.T](cfg, t.elementType.asInstanceOf[RType[t.elementType.T]], key.asInstanceOf[t.elementType.T], sb, true) b.append(':') val b2 = refWriteRT[t.elementType2.T](cfg, t.elementType2.asInstanceOf[RType[t.elementType2.T]], value.asInstanceOf[t.elementType2.T], sb) @@ -164,16 +228,18 @@ object JsonWriterRT: case t: TryRType[?] => if isMapKey then throw new JsonError("Try values (Succeed/Fail) cannot be map keys") - a match - case Success(v) => - refWriteRT[t.tryType.T](cfg, t.tryType.asInstanceOf[RType[t.tryType.T]], v.asInstanceOf[t.tryType.T], sb) - case Failure(_) if cfg.tryFailureHandling == TryOption.AS_NULL => sb.append("null") - case Failure(f) if cfg.tryFailureHandling == TryOption.THROW_EXCEPTION => - throw new JsonError("A try value was Failure with message: " + f.getMessage()) - case Failure(v) => - sb.append('"') - sb.append(v.getMessage) - sb.append('"') + if a == null then sb.append("null") + else + a match + case Success(v) => + refWriteRT[t.tryType.T](cfg, t.tryType.asInstanceOf[RType[t.tryType.T]], v.asInstanceOf[t.tryType.T], sb) + case Failure(_) if cfg.tryFailureHandling == TryOption.AS_NULL => sb.append("null") + case Failure(f) if cfg.tryFailureHandling == TryOption.THROW_EXCEPTION => + throw new JsonError("A try value was Failure with message: " + f.getMessage()) + case Failure(v) => + sb.append('"') + sb.append(v.getMessage) + sb.append('"') case t: TupleRType[?] => if isMapKey then throw new JsonError("Tuples cannot be map keys") @@ -195,7 +261,7 @@ object JsonWriterRT: sb.append('[') val sbLen = sb.length a.asInstanceOf[java.util.Collection[?]].toArray.foreach { elem => - if isOkToWrite(elem, cfg) then refWriteRT[t.elementType.T](cfg, t.elementType.asInstanceOf[RType[t.elementType.T]], elem.asInstanceOf[t.elementType.T], sb) + if JsonWriter.isOkToWrite(elem, cfg) then refWriteRT[t.elementType.T](cfg, t.elementType.asInstanceOf[RType[t.elementType.T]], elem.asInstanceOf[t.elementType.T], sb) } sb.append(',') if sbLen == sb.length then sb.append(']') @@ -207,7 +273,7 @@ object JsonWriterRT: sb.append('{') val sbLen = sb.length a.asInstanceOf[java.util.Map[?, ?]].asScala.foreach { case (key, value) => - if isOkToWrite(value, cfg) then + if JsonWriter.isOkToWrite(value, cfg) then refWriteRT[t.elementType.T](cfg, t.elementType.asInstanceOf[RType[t.elementType.T]], key.asInstanceOf[t.elementType.T], sb, true) sb.append(':') refWriteRT[t.elementType2.T](cfg, t.elementType2.asInstanceOf[RType[t.elementType2.T]], value.asInstanceOf[t.elementType2.T], sb) @@ -232,21 +298,46 @@ object JsonWriterRT: JsonWriterRT.refWriteRT[again.T](cfg, again, a.asInstanceOf[again.T], sb) case t: EnumRType[?] => - val enumAsId = cfg.enumsAsIds match - case '*' => true - case aList: List[String] if aList.contains(t.name) => true - case _ => false - if enumAsId then - val enumVal = t.ordinal(a.toString).getOrElse(throw new JsonError(s"Value $a is not a valid enum value for ${t.name}")) - if isMapKey then + if a == null then sb.append("null") + else + val enumAsId = cfg.enumsAsIds match + case '*' => true + case aList: List[String] if aList.contains(t.name) => true + case _ => false + if enumAsId then + val enumVal = t.ordinal(a.toString).getOrElse(throw new JsonError(s"Value $a is not a valid enum value for ${t.name}")) + if isMapKey then + sb.append('"') + sb.append(enumVal.toString) + sb.append('"') + else sb.append(enumVal.toString) + else sb.append('"') - sb.append(enumVal.toString) + sb.append(a.toString) sb.append('"') - else sb.append(enumVal.toString) + + case t: JavaClassRType[?] => + classesSeen.put(t.typedName, t) + if a == null then sb.append("null") else - sb.append('"') - sb.append(a.toString) - sb.append('"') + sb.append('{') + val sbLen = sb.length + t.fields.foldLeft(sb) { (acc, f) => + val field = f.asInstanceOf[NonConstructorFieldInfo] + val m = a.getClass.getMethod(field.getterLabel) + m.setAccessible(true) + val fieldValue = m.invoke(a).asInstanceOf[field.fieldType.T] + if JsonWriter.isOkToWrite(fieldValue, cfg) then + acc.append('"') + acc.append(field.name) + acc.append('"') + acc.append(':') + JsonWriterRT.refWriteRT[field.fieldType.T](cfg, field.fieldType, a.asInstanceOf[field.fieldType.T], sb)(using scala.collection.mutable.Map.empty[TypedName, RType[?]]) + acc.append(',') + else acc + } + if sbLen == sb.length then sb.append('}') + else sb.setCharAt(sb.length() - 1, '}') case t: ObjectRef => sb.append("\"" + t.name + "\"") @@ -255,16 +346,16 @@ object JsonWriterRT: cfg.undefinedFieldHandling match case UndefinedValueOption.AS_NULL => sb.append("null") case UndefinedValueOption.AS_SYMBOL => sb.append("\"" + t.name + "\"") - case UndefinedValueOption.THROW_EXCEPTION => throw new JsonError("Value " + a.toString + " is of some unknown/unsupported Scala 2 type " + t.name) + case UndefinedValueOption.THROW_EXCEPTION => throw new JsonError("Unknown/unsupported Scala 2 type " + t.name) case t: UnknownRType[?] => cfg.undefinedFieldHandling match case UndefinedValueOption.AS_NULL => sb.append("null") case UndefinedValueOption.AS_SYMBOL => sb.append("\"" + t.name + "\"") - case UndefinedValueOption.THROW_EXCEPTION => throw new JsonError("Value " + a.toString + " is of some unknown/unsupported type " + t.name) + case UndefinedValueOption.THROW_EXCEPTION => throw new JsonError("Unknown/unsupported type " + t.name) case t: TypeSymbolRType => cfg.undefinedFieldHandling match case UndefinedValueOption.AS_NULL => sb.append("null") case UndefinedValueOption.AS_SYMBOL => sb.append("\"" + t.name + "\"") - case UndefinedValueOption.THROW_EXCEPTION => throw new JsonError("Value " + a.toString + " is of some unknown/unsupported type " + t.name + ". (Class didn't fully define all its type parameters.)") + case UndefinedValueOption.THROW_EXCEPTION => throw new JsonError("Unknown/unsupported type " + t.name + ". (Class didn't fully define all its type parameters.)") diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index ba3f4315..c5ef4a4a 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -17,12 +17,14 @@ object RunMe extends App: given json.JsonConfig = json .JsonConfig() .copy(noneAsNull = true) + .copy(writeNonConstructorFields = true) // .copy(enumsAsIds = '*') try - val v = Person("Greg", DIAMOND, Command(15), Foom(3)) + val v = Person("Greg", DIAMOND, Command(15), new Wrapper(-10)) println("HERE: " + ScalaJack.write(v)) + println(RType.of[Wrapper].pretty) // println(RType.of[Person[Int]]) diff --git a/src/main/scala/co.blocke.scalajack/run/Sample.scala b/src/main/scala/co.blocke.scalajack/run/Sample.scala index 8739f15b..dd7a650f 100644 --- a/src/main/scala/co.blocke.scalajack/run/Sample.scala +++ b/src/main/scala/co.blocke.scalajack/run/Sample.scala @@ -1,4 +1,5 @@ -package co.blocke.scalajack.run +package co.blocke.scalajack +package run import neotype.* @@ -9,10 +10,13 @@ case object HEART extends card case object DIAMOND extends card case object SPADE extends card -sealed trait msg[X] +sealed abstract class msg[X] +@TypeHint case class Command[T](item: T) extends msg[T] case class Query[T](item: T) extends msg[T] +class Wrapper(val underlying: Int) extends AnyVal + enum Colors: case Red, Blue, Green @@ -21,7 +25,12 @@ case class Foom[X](x: X) extends Miss[X] // case class Person[Y](name: String, age: Miss[Y], again: Option[Person[Y]]) -case class Person[T](name: String, card: card, msg: msg[T], miss: Miss[T]) +case class Person[T](val name: String, val card: card, val msg: msg[T], meh: Wrapper): + var thingy: String = "wow" + + private var c: Int = 5 + def count: Int = c + def count_=(x: Int) = c = x // type NonEmptyString = NonEmptyString.Type // given NonEmptyString: Newtype[String] with From ee5da3537952eedc7ef543018ade58873e1f1877 Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Tue, 14 Nov 2023 10:21:03 -0600 Subject: [PATCH 23/65] ZIO based parser experiments --- benchmark/README.md | 18 +- benchmark/build.sbt | 12 +- .../src/main/scala/co.blocke/Argonaut.scala | 31 +- .../src/main/scala/co.blocke/Benchmark.scala | 97 +- .../src/main/scala/co.blocke/Circe.scala | 34 + .../src/main/scala/co.blocke/Fabric.scala | 17 + benchmark/src/main/scala/co.blocke/Jawn.scala | 17 + .../src/main/scala/co.blocke/PlayJson.scala | 98 +- benchmark/src/main/scala/co.blocke/Run.scala | 14 +- .../src/main/scala/co.blocke/ScalaJack.scala | 43 + .../src/main/scala/co.blocke/ZIOJson.scala | 37 +- .../scala/co.blocke.scalajack/ScalaJack.scala | 46 +- .../co.blocke.scalajack/json/JsonError.scala | 5 +- .../co.blocke.scalajack/json/JsonParser.scala | 46 +- .../json/JsonParser2.scala | 219 ++++ .../json/JsonParser3.scala | 188 ++++ .../co.blocke.scalajack/json/JsonReader.scala | 77 +- .../json/JsonReader.scalax | 49 + .../json/JsonReaderUtil.scala | 38 +- .../json/ParseInstructions.scalax | 11 + ...ReaderModule.scala => ReaderModule.scalax} | 0 .../json/ReaderModule2.scalax | 35 + .../{ClassReader.scala => ClassReader.scalax} | 0 ...onReader.scala => CollectionReader.scalax} | 50 +- .../{EnumReader.scala => EnumReader.scalax} | 0 .../{MiscReader.scala => MiscReader.scalax} | 0 ...iveReader.scala => PrimitiveReader.scalax} | 157 ++- .../json2/ClassDecoder.scala | 31 + .../json2/FastStringBuilder.scala | 21 + .../json2/FieldKeyDecoder.scala | 50 + .../json2/JsonDecoder.scala | 81 ++ .../json2/JsonParser.scala | 164 +++ .../json2/JsonReader.scala | 86 ++ .../co.blocke.scalajack/json2/Numbers.scala | 990 ++++++++++++++++++ .../json2/StringMatrix.scala | 92 ++ .../co.blocke.scalajack/json2/package.scala | 5 + .../parser/Instruction.scala | 60 ++ .../co.blocke.scalajack/parser/Parser.scala | 40 + .../co.blocke.scalajack/parser/Reader.scalax | 33 + .../scala/co.blocke.scalajack/run/Play.scala | 90 +- .../co.blocke.scalajack/run/Record.scala | 88 ++ .../run/{Sample.scala => Sample.scalax} | 2 + 42 files changed, 2798 insertions(+), 374 deletions(-) create mode 100644 benchmark/src/main/scala/co.blocke/Circe.scala create mode 100644 benchmark/src/main/scala/co.blocke/Fabric.scala create mode 100644 benchmark/src/main/scala/co.blocke/Jawn.scala create mode 100644 benchmark/src/main/scala/co.blocke/ScalaJack.scala create mode 100644 src/main/scala/co.blocke.scalajack/json/JsonParser2.scala create mode 100644 src/main/scala/co.blocke.scalajack/json/JsonParser3.scala create mode 100644 src/main/scala/co.blocke.scalajack/json/JsonReader.scalax create mode 100644 src/main/scala/co.blocke.scalajack/json/ParseInstructions.scalax rename src/main/scala/co.blocke.scalajack/json/{ReaderModule.scala => ReaderModule.scalax} (100%) create mode 100644 src/main/scala/co.blocke.scalajack/json/ReaderModule2.scalax rename src/main/scala/co.blocke.scalajack/json/readers/{ClassReader.scala => ClassReader.scalax} (100%) rename src/main/scala/co.blocke.scalajack/json/readers/{CollectionReader.scala => CollectionReader.scalax} (74%) rename src/main/scala/co.blocke.scalajack/json/readers/{EnumReader.scala => EnumReader.scalax} (100%) rename src/main/scala/co.blocke.scalajack/json/readers/{MiscReader.scala => MiscReader.scalax} (100%) rename src/main/scala/co.blocke.scalajack/json/readers/{PrimitiveReader.scala => PrimitiveReader.scalax} (61%) create mode 100644 src/main/scala/co.blocke.scalajack/json2/ClassDecoder.scala create mode 100644 src/main/scala/co.blocke.scalajack/json2/FastStringBuilder.scala create mode 100644 src/main/scala/co.blocke.scalajack/json2/FieldKeyDecoder.scala create mode 100644 src/main/scala/co.blocke.scalajack/json2/JsonDecoder.scala create mode 100644 src/main/scala/co.blocke.scalajack/json2/JsonParser.scala create mode 100644 src/main/scala/co.blocke.scalajack/json2/JsonReader.scala create mode 100644 src/main/scala/co.blocke.scalajack/json2/Numbers.scala create mode 100644 src/main/scala/co.blocke.scalajack/json2/StringMatrix.scala create mode 100644 src/main/scala/co.blocke.scalajack/json2/package.scala create mode 100644 src/main/scala/co.blocke.scalajack/parser/Instruction.scala create mode 100644 src/main/scala/co.blocke.scalajack/parser/Parser.scala create mode 100644 src/main/scala/co.blocke.scalajack/parser/Reader.scalax create mode 100644 src/main/scala/co.blocke.scalajack/run/Record.scala rename src/main/scala/co.blocke.scalajack/run/{Sample.scala => Sample.scalax} (96%) diff --git a/benchmark/README.md b/benchmark/README.md index f28eaf79..7e8c96c1 100644 --- a/benchmark/README.md +++ b/benchmark/README.md @@ -15,15 +15,15 @@ sbt "jmh:run -i 10 -wi 10 -f 2 -t 1 co.blocke.WritingBenchmark" ## Writing Performance: -| Benchmark | Mode | Count | Score | Error | Units | -|------------------|-------|----:|------------:|-------------:|-------| -| Hand-Tooled | thrpt | 20 | 2,575,393.513 | ± 178731.952 | ops/s | -| Circe | thrpt | 20 | 1,939,339.085 | ± 6279.547 | ops/s | -|**ScalaJack 8** | thrpt | 20 | **1,703,256.521** | ± 12260.518 | ops/s | -| ZIO JSON | thrpt | 20 | 818,228.736 | ± 3070.298 | ops/s | -| Argonaut | thrpt | 20 | 716,228.404 | ± 6241.145 | ops/s | -| Play JSON | thrpt | 20 | 438,538.475 | ± 16319.198 | ops/s | -| ScalaJack 7 | thrpt | 20 | 106,292.338 | ± 330.111 | ops/s | +| Benchmark | Mode | Count | Score | Error | Units | +|------------------|-------|-------:|----------------:|-------------:|-------| +| Hand-Tooled | thrpt | 20 | 2,575,393.513 | ± 178731.952 | ops/s | +| Circe | thrpt | 20 | 1,939,339.085 | ± 6279.547 | ops/s | +|**ScalaJack 8** | thrpt | 20 | **176,867,514.557** | ± 12260.518 | ops/s | +| ZIO JSON | thrpt | 20 | 818,228.736 | ± 3070.298 | ops/s | +| Argonaut | thrpt | 20 | 716,228.404 | ± 6241.145 | ops/s | +| Play JSON | thrpt | 20 | 438,538.475 | ± 16319.198 | ops/s | +| ScalaJack 7 | thrpt | 20 | 106,292.338 | ± 330.111 | ops/s | ### Interpretation diff --git a/benchmark/build.sbt b/benchmark/build.sbt index 2b1ad05b..761843db 100644 --- a/benchmark/build.sbt +++ b/benchmark/build.sbt @@ -7,10 +7,7 @@ val compilerOptions = Seq( "-feature", "-language:existentials", "-language:higherKinds", - "-unchecked", - "-Ywarn-dead-code", - "-Ywarn-numeric-widen", - "-Xfuture" + "-unchecked" ) val circeVersion = "0.15.0-M1" @@ -39,9 +36,14 @@ lazy val benchmark = project libraryDependencies ++= Seq( "org.playframework" %% "play-json" % "3.0.1", "io.argonaut" %% "argonaut" % "6.3.9", - "co.blocke" %% "scalajack" % "826a30_unknown", + // "co.blocke" %% "scalajack" % "826a30_unknown", // Old-New + "co.blocke" %% "scalajack" % "e48b35_unknown", // New-New "co.blocke" %% "scala-reflection" % "sj_fixes_edbef8", "dev.zio" %% "zio-json" % "0.6.1", + "org.typelevel" %% "fabric-core" % "1.12.6", + "org.typelevel" %% "fabric-io" % "1.12.6", + "org.typelevel" %% "jawn-parser" % "1.3.2", + "org.typelevel" %% "jawn-ast" % "1.3.2", // "io.circe" %% "circe-derivation" % "0.15.0-M1", // "io.circe" %% "circe-jackson29" % "0.14.0", // "org.json4s" %% "json4s-jackson" % "4.0.4", diff --git a/benchmark/src/main/scala/co.blocke/Argonaut.scala b/benchmark/src/main/scala/co.blocke/Argonaut.scala index 99424827..7b614f0e 100644 --- a/benchmark/src/main/scala/co.blocke/Argonaut.scala +++ b/benchmark/src/main/scala/co.blocke/Argonaut.scala @@ -1,26 +1,27 @@ package co.blocke -import argonaut._, Argonaut._ import org.openjdk.jmh.annotations._ -implicit val CodecPet: CodecJson[Pet] = - casecodec3(Pet.apply, (a: Pet) => Option((a.name, a.species, a.age)))("name","species","age") +object ArgonautZ: + import argonaut._, Argonaut._ -implicit val CodecFriend: CodecJson[Friend] = - casecodec3(Friend.apply, (a: Friend) => Option((a.name, a.age, a.email)))("name","age","email") + implicit val CodecPet: CodecJson[Pet] = + casecodec3(Pet.apply, (a: Pet) => Option((a.name, a.species, a.age)))("name","species","age") -implicit val CodecAddress: CodecJson[Address] = - casecodec4(Address.apply, (a: Address) => Option((a.street, a.city, a.state, a.postal_code)))("street","city","state","postal_code") + implicit val CodecFriend: CodecJson[Friend] = + casecodec3(Friend.apply, (a: Friend) => Option((a.name, a.age, a.email)))("name","age","email") -implicit val CodecPerson: CodecJson[Person] = - casecodec6(Person.apply, (a: Person) => Option((a.name, a.age, a.address, a.email, a.phone_numbers, a.is_employed)))("name", "age","address","email","phone_numbers","is_employed") + implicit val CodecAddress: CodecJson[Address] = + casecodec4(Address.apply, (a: Address) => Option((a.street, a.city, a.state, a.postal_code)))("street","city","state","postal_code") -implicit val CodecRecord: CodecJson[Record] = - casecodec4(Record.apply, (a: Record) => Option((a.person, a.hobbies, a.friends, a.pets)))("person", "hobbies", "friends", "pets") + implicit val CodecPerson: CodecJson[Person] = + casecodec6(Person.apply, (a: Person) => Option((a.name, a.age, a.address, a.email, a.phone_numbers, a.is_employed)))("name", "age","address","email","phone_numbers","is_employed") + implicit val CodecRecord: CodecJson[Record] = + casecodec4(Record.apply, (a: Record) => Option((a.person, a.hobbies, a.friends, a.pets)))("person", "hobbies", "friends", "pets") -trait ArgonautWritingBenchmark { - @Benchmark - def writeRecordArgonaut = record.asJson -} + trait ArgonautWritingBenchmark { + @Benchmark + def writeRecordArgonaut = record.asJson + } diff --git a/benchmark/src/main/scala/co.blocke/Benchmark.scala b/benchmark/src/main/scala/co.blocke/Benchmark.scala index a5210215..4f6a2110 100644 --- a/benchmark/src/main/scala/co.blocke/Benchmark.scala +++ b/benchmark/src/main/scala/co.blocke/Benchmark.scala @@ -1,52 +1,14 @@ package co.blocke import org.openjdk.jmh.annotations._ - import java.util.concurrent.TimeUnit -import co.blocke.scalajack.* - -import io.circe.syntax.* -import io.circe.* -import io.circe.generic.semiauto.* - -val record = ScalaJack.read[Record](jsData) - -implicit val recordDecoder: Decoder[Record] = deriveDecoder[Record] -implicit val recordEncoder: Encoder[Record] = deriveEncoder[Record] - -implicit val personDecoder: Decoder[Person] = deriveDecoder[Person] -implicit val personEncoder: Encoder[Person] = deriveEncoder[Person] - -implicit val addressDecoder: Decoder[Address] = deriveDecoder[Address] -implicit val addressEncoder: Encoder[Address] = deriveEncoder[Address] - -implicit val friendDecoder: Decoder[Friend] = deriveDecoder[Friend] -implicit val friendEncoder: Encoder[Friend] = deriveEncoder[Friend] - -implicit val petDecoder: Decoder[Pet] = deriveDecoder[Pet] -implicit val petEncoder: Encoder[Pet] = deriveEncoder[Pet] - - - -trait CirceReadingBenchmark{ - val circeJson = record.asJson - def readRecordCirce = circeJson.as[Record] -} - -// trait ScalaJackReadingBenchmark{ -// def readRecordScalaJack = ScalaJack.read[Record](jsData) -// } - -trait CirceWritingBenchmark { - @Benchmark - def writeRecordCirce = record.asJson -} +import ZIOZ.* +import zio.json._ +val record = jsData.fromJson[Record] match + case Right(r) => r + case Left(_) => null.asInstanceOf[Record] -trait ScalaJackWritingBenchmark { - @Benchmark - def writeRecordScalaJack = ScalaJack.write(record) -} trait HandTooledWritingBenchmark { @Benchmark @@ -77,20 +39,45 @@ trait HandTooledWritingBenchmark { sb.toString } -// @State(Scope.Thread) -// @BenchmarkMode(Array(Mode.Throughput)) -// @OutputTimeUnit(TimeUnit.SECONDS) -// class ReadingBenchmark -// extends CirceReadingBenchmark -// with ScalaJackReadingBenchmark +@State(Scope.Thread) +@BenchmarkMode(Array(Mode.Throughput)) +@OutputTimeUnit(TimeUnit.SECONDS) +class ReadingBenchmark + // extends CirceZ.CirceReadingBenchmark + extends ScalaJackZ.ScalaJackReadingBenchmark + // extends ZIOZ.ZIOJsonReadingBenchmark + // extends PlayZ.PlayReadingBenchmark + // extends FabricZ.FabricReadingBenchmark + // extends JawnZ.JawnReadingBenchmark @State(Scope.Thread) @BenchmarkMode(Array(Mode.Throughput)) @OutputTimeUnit(TimeUnit.SECONDS) class WritingBenchmark - extends CirceWritingBenchmark - with ScalaJackWritingBenchmark - with HandTooledWritingBenchmark - with ArgonautWritingBenchmark - with PlayWritingBenchmark - with ZIOJsonWritingBenchmark + // extends CirceZ.CirceWritingBenchmark + extends ScalaJackZ.ScalaJackWritingBenchmark + // with HandTooledWritingBenchmark + // with ArgonautZ.ArgonautWritingBenchmark + // with PlayZ.PlayWritingBenchmark + // with ZIOZ.ZIOJsonWritingBenchmark + + +// "Old-New" ScalaJack +// [info] Benchmark Mode Cnt Score Error Units +// [info] ReadingBenchmark.readRecordScalaJack thrpt 20 30113.982 ± 97.701 ops/s +// [info] New-New ScalaJack thrpt 20 50908.982 ± 97.701 ops/s + +// ScalaJack w/ZIO-based parser 635977.008 +// ZIO (Fast!!) 568123.000 <-- How do they do this?! More than 2x faster than everyone else! +// Circe 279231.646 +// Play 209756.408 + +// Jawn (parse only + AST) 336384.617 +// ScalaJack JsonParser3 (parse only + AST) 279456.523 +// Fabric (new!) (parse only + AST) 270706.567 + + + + +// SJ StringBuffer : 1740040.225 +// SJ FastStringBuffer : \ No newline at end of file diff --git a/benchmark/src/main/scala/co.blocke/Circe.scala b/benchmark/src/main/scala/co.blocke/Circe.scala new file mode 100644 index 00000000..ebb88705 --- /dev/null +++ b/benchmark/src/main/scala/co.blocke/Circe.scala @@ -0,0 +1,34 @@ +package co.blocke + +import org.openjdk.jmh.annotations._ + +object CirceZ: + import io.circe.syntax.* + import io.circe.* + import io.circe.generic.semiauto.* + import io.circe.parser.* + + implicit val recordDecoder: Decoder[Record] = deriveDecoder[Record] + implicit val recordEncoder: Encoder[Record] = deriveEncoder[Record] + + implicit val personDecoder: Decoder[Person] = deriveDecoder[Person] + implicit val personEncoder: Encoder[Person] = deriveEncoder[Person] + + implicit val addressDecoder: Decoder[Address] = deriveDecoder[Address] + implicit val addressEncoder: Encoder[Address] = deriveEncoder[Address] + + implicit val friendDecoder: Decoder[Friend] = deriveDecoder[Friend] + implicit val friendEncoder: Encoder[Friend] = deriveEncoder[Friend] + + implicit val petDecoder: Decoder[Pet] = deriveDecoder[Pet] + implicit val petEncoder: Encoder[Pet] = deriveEncoder[Pet] + + trait CirceReadingBenchmark{ + @Benchmark + def readRecordCirce = parse(jsData).flatMap(_.as[Record]) + } + + trait CirceWritingBenchmark { + @Benchmark + def writeRecordCirce = record.asJson + } diff --git a/benchmark/src/main/scala/co.blocke/Fabric.scala b/benchmark/src/main/scala/co.blocke/Fabric.scala new file mode 100644 index 00000000..18c3fb81 --- /dev/null +++ b/benchmark/src/main/scala/co.blocke/Fabric.scala @@ -0,0 +1,17 @@ +package co.blocke + +import org.openjdk.jmh.annotations._ + +object FabricZ: + import fabric.* + import fabric.io.* + + trait FabricReadingBenchmark{ + @Benchmark + def readRecordFabric = JsonParser(jsData, Format.Json) + } + + // trait CirceWritingBenchmark { + // @Benchmark + // def writeRecordCirce = record.asJson + // } diff --git a/benchmark/src/main/scala/co.blocke/Jawn.scala b/benchmark/src/main/scala/co.blocke/Jawn.scala new file mode 100644 index 00000000..68b7ad8a --- /dev/null +++ b/benchmark/src/main/scala/co.blocke/Jawn.scala @@ -0,0 +1,17 @@ +package co.blocke + +import org.openjdk.jmh.annotations._ + +object JawnZ: + + import org.typelevel.jawn.ast.* + + trait JawnReadingBenchmark{ + @Benchmark + def readRecordFabric = JParser.parseFromString(jsData) + } + + // trait CirceWritingBenchmark { + // @Benchmark + // def writeRecordCirce = record.asJson + // } diff --git a/benchmark/src/main/scala/co.blocke/PlayJson.scala b/benchmark/src/main/scala/co.blocke/PlayJson.scala index 9e1ccd27..0eb58a60 100644 --- a/benchmark/src/main/scala/co.blocke/PlayJson.scala +++ b/benchmark/src/main/scala/co.blocke/PlayJson.scala @@ -1,46 +1,60 @@ package co.blocke -import play.api.libs.json._ -import play.api.libs.json.Reads._ -import play.api.libs.functional.syntax._ import org.openjdk.jmh.annotations._ -implicit val friendWrites: Writes[Friend] = ( - (JsPath \ "name").write[String] and - (JsPath \ "age").write[Int] and - (JsPath \ "email").write[String] -)(unlift((a: Friend) => Option((a.name, a.age, a.email)))) - -implicit val petWrites: Writes[Pet] = ( - (JsPath \ "name").write[String] and - (JsPath \ "species").write[String] and - (JsPath \ "age").write[Int] -)(unlift((a: Pet) => Option((a.name, a.species, a.age)))) - -implicit val addressWrites: Writes[Address] = ( - (JsPath \ "street").write[String] and - (JsPath \ "city").write[String] and - (JsPath \ "state").write[String] and - (JsPath \ "postal_code").write[String] -)(unlift((a: Address) => Option((a.street, a.city, a.state, a.postal_code)))) - -implicit val personWrites: Writes[Person] = ( - (JsPath \ "namet").write[String] and - (JsPath \ "age").write[Int] and - (JsPath \ "address").write[Address] and - (JsPath \ "email").write[String] and - (JsPath \ "phone_numbers").write[List[String]] and - (JsPath \ "is_employed").write[Boolean] -)(unlift((a: Person) => Option((a.name, a.age, a.address, a.email, a.phone_numbers, a.is_employed)))) - -implicit val recordWrites: Writes[Record] = ( - (JsPath \ "person").write[Person] and - (JsPath \ "hobbies").write[List[String]] and - (JsPath \ "friends").write[List[Friend]] and - (JsPath \ "pets").write[List[Pet]] -)(unlift((a: Record) => Option((a.person, a.hobbies, a.friends, a.pets)))) - -trait PlayWritingBenchmark { - @Benchmark - def writeRecordPlay = Json.toJson(record) -} +object PlayZ: + import play.api.libs.json._ + import play.api.libs.json.Reads._ + import play.api.libs.functional.syntax._ + + implicit val friendWrites: Writes[Friend] = ( + (JsPath \ "name").write[String] and + (JsPath \ "age").write[Int] and + (JsPath \ "email").write[String] + )(unlift((a: Friend) => Option((a.name, a.age, a.email)))) + + implicit val petWrites: Writes[Pet] = ( + (JsPath \ "name").write[String] and + (JsPath \ "species").write[String] and + (JsPath \ "age").write[Int] + )(unlift((a: Pet) => Option((a.name, a.species, a.age)))) + + implicit val addressWrites: Writes[Address] = ( + (JsPath \ "street").write[String] and + (JsPath \ "city").write[String] and + (JsPath \ "state").write[String] and + (JsPath \ "postal_code").write[String] + )(unlift((a: Address) => Option((a.street, a.city, a.state, a.postal_code)))) + + implicit val personWrites: Writes[Person] = ( + (JsPath \ "namet").write[String] and + (JsPath \ "age").write[Int] and + (JsPath \ "address").write[Address] and + (JsPath \ "email").write[String] and + (JsPath \ "phone_numbers").write[List[String]] and + (JsPath \ "is_employed").write[Boolean] + )(unlift((a: Person) => Option((a.name, a.age, a.address, a.email, a.phone_numbers, a.is_employed)))) + + implicit val recordWrites: Writes[Record] = ( + (JsPath \ "person").write[Person] and + (JsPath \ "hobbies").write[List[String]] and + (JsPath \ "friends").write[List[Friend]] and + (JsPath \ "pets").write[List[Pet]] + )(unlift((a: Record) => Option((a.person, a.hobbies, a.friends, a.pets)))) + + implicit val friendReads: play.api.libs.json.Reads[co.blocke.Friend] = Json.reads[Friend] + implicit val petReads: play.api.libs.json.Reads[co.blocke.Pet] = Json.reads[Pet] + implicit val addressReads: play.api.libs.json.Reads[co.blocke.Address] = Json.reads[Address] + implicit val personReads: play.api.libs.json.Reads[co.blocke.Person] = Json.reads[Person] + implicit val recordReads: play.api.libs.json.Reads[co.blocke.Record] = Json.reads[Record] + + trait PlayWritingBenchmark { + @Benchmark + def writeRecordPlay = Json.toJson(record) + } + + // val playJS = Json.toJson(record) + trait PlayReadingBenchmark { + @Benchmark + def readRecordPlay = Json.fromJson[Record](Json.parse(jsData)) //Json.fromJson[Record](playJS) + } diff --git a/benchmark/src/main/scala/co.blocke/Run.scala b/benchmark/src/main/scala/co.blocke/Run.scala index 5a1d8a31..dc945391 100644 --- a/benchmark/src/main/scala/co.blocke/Run.scala +++ b/benchmark/src/main/scala/co.blocke/Run.scala @@ -1,8 +1,16 @@ package co.blocke -case class Foo() extends ZIOJsonWritingBenchmark object RunMe extends App: - val f = Foo() - println(f.writeRecordZIOJson) \ No newline at end of file + import ZIOZ.* + import zio.json._ + import co.blocke.scalajack.* + + val f = jsData.fromJson[Record] + println(f) + + println("\n---------") + println(ScalaJack.write(f)) + + println("ZIO Decoder (Address): "+DeriveJsonDecoder.gen[Address]) \ No newline at end of file diff --git a/benchmark/src/main/scala/co.blocke/ScalaJack.scala b/benchmark/src/main/scala/co.blocke/ScalaJack.scala new file mode 100644 index 00000000..0701cbe6 --- /dev/null +++ b/benchmark/src/main/scala/co.blocke/ScalaJack.scala @@ -0,0 +1,43 @@ +package co.blocke + +import org.openjdk.jmh.annotations._ + +object ScalaJackZ: + import co.blocke.scalajack.* + + import json2.* + + implicit val addrDecoder: JsonDecoder[Address] = ClassDecoder( + Array("street", "city", "state", "postal_code"), + Array(JsonDecoder[String], JsonDecoder[String], JsonDecoder[String], JsonDecoder[String]) + ) + implicit val friendDecoder: JsonDecoder[Friend] = ClassDecoder( + Array("name", "age", "email"), + Array(JsonDecoder[String], JsonDecoder[Int], JsonDecoder[String]) + ) + implicit val petDecoder: JsonDecoder[Pet] = ClassDecoder( + Array("name", "species", "age"), + Array(JsonDecoder[String], JsonDecoder[String], JsonDecoder[Int]) + ) + implicit val PersonDecoder: JsonDecoder[Person] = ClassDecoder( + Array("name", "age", "address", "email", "phone_numbers", "is_employed"), + Array(JsonDecoder[String], JsonDecoder[Int], JsonDecoder[Address], JsonDecoder[String], JsonDecoder[List[String]], JsonDecoder[Boolean]) + ) + implicit val RecordDecoder: JsonDecoder[Record] = ClassDecoder( + Array("person", "hobbies", "friends", "pets"), + Array(JsonDecoder[Person], JsonDecoder[List[String]], JsonDecoder[List[Friend]], JsonDecoder[List[Pet]]) + ) + + // val jp = co.blocke.scalajack.json.JsonParser3(jsData) + + trait ScalaJackReadingBenchmark{ + @Benchmark + // def readRecordScalaJack = { jp.reset(); jp.parse() } + // def readRecordScalaJack = ScalaJack.read[Record](jsData) + def readRecordScalaJack = JsonDecoder[Record].decodeJson(jsData) + } + + trait ScalaJackWritingBenchmark { + @Benchmark + def writeRecordScalaJack = ScalaJack.write(record) + } \ No newline at end of file diff --git a/benchmark/src/main/scala/co.blocke/ZIOJson.scala b/benchmark/src/main/scala/co.blocke/ZIOJson.scala index d0462744..9a5b2d97 100644 --- a/benchmark/src/main/scala/co.blocke/ZIOJson.scala +++ b/benchmark/src/main/scala/co.blocke/ZIOJson.scala @@ -1,20 +1,27 @@ package co.blocke -import zio.json._ import org.openjdk.jmh.annotations._ -implicit val decoder1: JsonDecoder[Address] = DeriveJsonDecoder.gen[Address] -implicit val decoder2: JsonDecoder[Pet] = DeriveJsonDecoder.gen[Pet] -implicit val decoder3: JsonDecoder[Friend] = DeriveJsonDecoder.gen[Friend] -implicit val decoder4: JsonDecoder[Person] = DeriveJsonDecoder.gen[Person] -implicit val decoder5: JsonDecoder[Record] = DeriveJsonDecoder.gen[Record] -implicit val encoder1: JsonEncoder[Address] = DeriveJsonEncoder.gen[Address] -implicit val encoder2: JsonEncoder[Pet] = DeriveJsonEncoder.gen[Pet] -implicit val encoder3: JsonEncoder[Friend] = DeriveJsonEncoder.gen[Friend] -implicit val encoder4: JsonEncoder[Person] = DeriveJsonEncoder.gen[Person] -implicit val encoder5: JsonEncoder[Record] = DeriveJsonEncoder.gen[Record] +object ZIOZ: + import zio.json._ -trait ZIOJsonWritingBenchmark { - @Benchmark - def writeRecordZIOJson = record.toJson -} \ No newline at end of file + implicit val decoder1: JsonDecoder[Address] = DeriveJsonDecoder.gen[Address] + implicit val decoder2: JsonDecoder[Pet] = DeriveJsonDecoder.gen[Pet] + implicit val decoder3: JsonDecoder[Friend] = DeriveJsonDecoder.gen[Friend] + implicit val decoder4: JsonDecoder[Person] = DeriveJsonDecoder.gen[Person] + implicit val decoder5: JsonDecoder[Record] = DeriveJsonDecoder.gen[Record] + implicit val encoder1: JsonEncoder[Address] = DeriveJsonEncoder.gen[Address] + implicit val encoder2: JsonEncoder[Pet] = DeriveJsonEncoder.gen[Pet] + implicit val encoder3: JsonEncoder[Friend] = DeriveJsonEncoder.gen[Friend] + implicit val encoder4: JsonEncoder[Person] = DeriveJsonEncoder.gen[Person] + implicit val encoder5: JsonEncoder[Record] = DeriveJsonEncoder.gen[Record] + + trait ZIOJsonWritingBenchmark { + @Benchmark + def writeRecordZIOJson = record.toJson + } + + trait ZIOJsonReadingBenchmark { + @Benchmark + def readRecordZIOJson = jsData.fromJson[Record] + } \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/ScalaJack.scala b/src/main/scala/co.blocke.scalajack/ScalaJack.scala index 7974fb9a..73bac0f8 100644 --- a/src/main/scala/co.blocke.scalajack/ScalaJack.scala +++ b/src/main/scala/co.blocke.scalajack/ScalaJack.scala @@ -3,6 +3,7 @@ package co.blocke.scalajack import co.blocke.scala_reflection.{RTypeRef, TypedName} import co.blocke.scala_reflection.reflect.ReflectOnType import co.blocke.scala_reflection.reflect.rtypeRefs.ClassRef +import parser.ParseError import scala.collection.mutable.{HashMap, Map} import scala.quoted.* import quoted.Quotes @@ -21,22 +22,43 @@ object ScalaJack: // --------------------------------------------------------------------- - inline def read[T](js: String)(using cfg: JsonConfig = JsonConfig()): T = ${ readImpl[T]('js, 'cfg) } + inline def read[T](js: String)(using cfg: JsonConfig = JsonConfig()): Either[ParseError, T] = ${ readImpl[T]('js, 'cfg) } - def readImpl[T: Type](js: Expr[String], cfg: Expr[JsonConfig])(using q: Quotes): Expr[T] = + def readImpl[T: Type](js: Expr[String], cfg: Expr[JsonConfig])(using q: Quotes): Expr[Either[ParseError, T]] = import quotes.reflect.* val classRef = ReflectOnType[T](quotes)(TypeRepr.of[T], true)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) - // Used to trap SelfRef's from going into endless loops and causing Stack Overflow. - val seenBeforeFnCache = HashMap.empty[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]] + val instruction = JsonReader.refRead[T](classRef) - val fn = JsonReader().readerFn[T](classRef)(using quotes, Type.of[T])(using seenBeforeFnCache) - val listifiedCache = Expr.ofList(seenBeforeFnCache.toList.map(t => Expr.ofTuple(t))) - - '{ // run-time - val parser = JsonParser($js, $listifiedCache.toMap) - $fn($cfg, parser) match - case Right(v) => v - case Left(t) => throw t + '{ + val foo = json2.JsonReader($js) + var c = 0 + while c != json2.BUFFER_EXCEEDED do c = foo.read() + Right(null.asInstanceOf[T]) } + + // '{ + // val parser = JsonParser2($js, $cfg) + // parser.parse($instruction).asInstanceOf[Either[ParseError, T]] + // } + + // -------------------->>>> OLD <<---------------------- + + // // Used to trap SelfRef's from going into endless loops and causing Stack Overflow. + // val seenBeforeFnCache = HashMap.empty[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]] + + // val parserE = '{ new JsonParser($js, $cfg) } + // JsonReader().refRead(classRef, parserE, cfg)(using quotes, Type.of[T])(using seenBeforeFnCache) + + // --------------- + + // val fn = JsonReader().readerFn[T](classRef)(using quotes, Type.of[T])(using seenBeforeFnCache) + // val listifiedCache = Expr.ofList(seenBeforeFnCache.toList.map(t => Expr.ofTuple(t))) + + // '{ // run-time + // val parser = JsonParser($js, $listifiedCache.toMap) + // $fn($cfg, parser) match + // case Right(v) => v + // case Left(t) => throw t + // } diff --git a/src/main/scala/co.blocke.scalajack/json/JsonError.scala b/src/main/scala/co.blocke.scalajack/json/JsonError.scala index 076a7388..848c12c9 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonError.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonError.scala @@ -3,6 +3,5 @@ package json class JsonError(msg: String) extends Throwable -abstract class ParseError(message: String) extends Throwable(message) -case class JsonParseError(message: String) extends ParseError(message) -case class CommaExpected(message: String = "") extends ParseError(message) +case class JsonParseError(message: String) extends parser.ParseError(message) +case class CommaExpected(message: String = "") extends parser.ParseError(message) diff --git a/src/main/scala/co.blocke.scalajack/json/JsonParser.scala b/src/main/scala/co.blocke.scalajack/json/JsonParser.scala index 4b558da6..cfbdb80f 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonParser.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonParser.scala @@ -1,11 +1,12 @@ package co.blocke.scalajack package json +import parser.* import scala.util.* import co.blocke.scala_reflection.TypedName import scala.collection.mutable.ListBuffer -case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) => Either[ParseError, ?]]): +case class JsonParser(js: String, cfg: JsonConfig): // , cache: Map[TypedName, (JsonConfig, JsonParser) => Either[ParseError, ?]]): private val jsChars: Array[Char] = js.toCharArray private var i = 0 @@ -61,7 +62,7 @@ case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) // Data Types // ------------------------------ - def expectBoolean(cfg: JsonConfig, p: JsonParser): Either[ParseError, Boolean] = + def expectBoolean(isMapKey: Boolean = false): Either[ParseError, Boolean] = jsChars(i) match case 't' if i + 3 < max && jsChars(i + 1) == 'r' && jsChars(i + 2) == 'u' && jsChars(i + 3) == 'e' => i += 4 @@ -70,15 +71,15 @@ case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) i += 5 Right(false) case x => - if cfg.permissivePrimitives && jsChars(i) == '"' then + if (isMapKey || cfg.permissivePrimitives) && jsChars(i) == '"' then for { _ <- expectQuote - result <- expectBoolean(cfg, p) + result <- expectBoolean() _ <- expectQuote } yield result else Left(JsonParseError(showError(s"Unexpected character '$x' where beginning of boolean value expected at position [$i]"))) - def expectLong(cfg: JsonConfig, p: JsonParser): Either[ParseError, Long] = + def expectLong(isMapKey: Boolean = false): Either[ParseError, Long] = val mark = i var done = false while !done do @@ -88,10 +89,10 @@ case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) Try(js.substring(mark, i).toLong) match case Success(g) => Right(g) case Failure(f) => - if cfg.permissivePrimitives && jsChars(i) == '"' then + if (isMapKey || cfg.permissivePrimitives) && jsChars(i) == '"' then for { _ <- expectQuote - result <- expectLong(cfg, p) + result <- expectLong() _ <- expectQuote } yield result else @@ -101,7 +102,7 @@ case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) i = mark Left(JsonParseError(showError(msg))) - def expectBigLong(cfg: JsonConfig, p: JsonParser): Either[ParseError, BigInt] = + def expectBigLong(isMapKey: Boolean = false): Either[ParseError, BigInt] = nullCheck match case true if cfg.forbidNullsInInput => Left(JsonParseError(showError(s"Forbidden 'null' value received at position [$i]"))) case true => Right(null.asInstanceOf[BigInt]) @@ -115,10 +116,10 @@ case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) Try(BigInt(js.substring(mark, i))) match case Success(g) => Right(g) case Failure(f) => - if cfg.permissivePrimitives && jsChars(i) == '"' then + if (isMapKey || cfg.permissivePrimitives) && jsChars(i) == '"' then for { _ <- expectQuote - result <- expectBigLong(cfg, p) + result <- expectBigLong() _ <- expectQuote } yield result else @@ -128,7 +129,7 @@ case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) i = mark Left(JsonParseError(showError(msg))) - def expectDouble(cfg: JsonConfig, p: JsonParser): Either[ParseError, Double] = + def expectDouble(isMapKey: Boolean = false): Either[ParseError, Double] = val mark = i var done = false while !done do @@ -138,10 +139,10 @@ case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) Try(js.substring(mark, i).toDouble) match case Success(g) => Right(g) case Failure(_) => - if cfg.permissivePrimitives && jsChars(i) == '"' then + if (isMapKey || cfg.permissivePrimitives) && jsChars(i) == '"' then for { _ <- expectQuote - result <- expectDouble(cfg, p) + result <- expectDouble() _ <- expectQuote } yield result else @@ -151,7 +152,7 @@ case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) i = mark Left(JsonParseError(showError(msg))) - def expectBigDouble(cfg: JsonConfig, p: JsonParser): Either[ParseError, BigDecimal] = + def expectBigDouble(isMapKey: Boolean = false): Either[ParseError, BigDecimal] = nullCheck match case true if cfg.forbidNullsInInput => Left(JsonParseError(showError(s"Forbidden 'null' value received at position [$i]"))) case true => Right(null.asInstanceOf[BigDecimal]) @@ -165,10 +166,10 @@ case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) Try(BigDecimal(js.substring(mark, i))) match case Success(g) => Right(g) case Failure(_) => - if cfg.permissivePrimitives && jsChars(i) == '"' then + if (isMapKey || cfg.permissivePrimitives) && jsChars(i) == '"' then for { _ <- expectQuote - result <- expectBigDouble(cfg, p) + result <- expectBigDouble() _ <- expectQuote } yield result else @@ -178,7 +179,7 @@ case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) i = mark Left(JsonParseError(showError(msg))) - def expectString(cfg: JsonConfig, p: JsonParser): Either[ParseError, String] = + def expectString(): Either[ParseError, String] = nullCheck match case true if cfg.forbidNullsInInput => Left(JsonParseError(showError(s"Forbidden 'null' value received at position [$i]"))) case true => Right(null.asInstanceOf[String]) @@ -198,7 +199,7 @@ case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) Right(captured.getOrElse(js.substring(mark, i - 1))) case x => Left(JsonParseError(showError(s"Unexpected character '$x' where beginning of a string expected at position [$i]"))) - def expectList[T](cfg: JsonConfig, expectElement: (JsonConfig, JsonParser) => Either[ParseError, T]): Either[ParseError, List[T]] = + def expectList[T](expectElement: () => Either[ParseError, T]): Either[ParseError, List[T]] = nullCheck match case true if cfg.forbidNullsInInput => Left(JsonParseError(showError(s"Forbidden 'null' value received at position [$i]"))) case true => Right(null.asInstanceOf[List[T]]) @@ -211,7 +212,7 @@ case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) var done: Option[Either[ParseError, List[T]]] = None while done.isEmpty do (for { - el <- expectElement(cfg, this) + el <- expectElement() _ = acc.addOne(el) _ <- expectComma } yield el) match @@ -225,7 +226,6 @@ case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) done.get def expectTuple( - cfg: JsonConfig, tupleFns: List[(JsonConfig, JsonParser) => Either[ParseError, ?]] ): Either[ParseError, List[?]] = if jsChars(i) != '[' then Left(JsonParseError(showError(s"Beginning of tuple expected at position [$i]"))) @@ -252,7 +252,6 @@ case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) case Right(_) => Left(JsonParseError(showError(s"Extra/unexpected tuple fields at position [$i]"))) def expectObject[K, V]( - cfg: JsonConfig, keyElement: (JsonConfig, JsonParser) => Either[ParseError, K], valueElement: (JsonConfig, JsonParser) => Either[ParseError, V] ): Either[ParseError, Map[K, V]] = @@ -281,7 +280,6 @@ case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) // Special case of JSON object where each entry is a field of a class def expectClass( - cfg: JsonConfig, fieldMap: Map[String, (JsonConfig, JsonParser) => Either[ParseError, ?]], fieldValues: scala.collection.mutable.HashMap[String, Any] // pre-set values (Option:None, default values) ): Either[ParseError, Map[String, ?]] = @@ -308,7 +306,7 @@ case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) done.get // Slower String parsing that handles special escaped chars - def _expectString = + private def _expectString = val builder = new java.lang.StringBuilder() while i < max && jsChars(i) != '"' do if jsChars(i) == '\\' then { @@ -365,5 +363,5 @@ case class JsonParser(js: String, cache: Map[TypedName, (JsonConfig, JsonParser) ("..." + js.substring(i - 49), 52) case ep => ("..." + js.substring(ep - 49, ep + 27) + "...", 52) } - msg + "\n" + clip.replaceAll("[\n\t]", "~") + "\n" + ("-" * dashes) + "^" + msg + s" at positio [$i]" + "\n" + clip.replaceAll("[\n\t]", "~") + "\n" + ("-" * dashes) + "^" } diff --git a/src/main/scala/co.blocke.scalajack/json/JsonParser2.scala b/src/main/scala/co.blocke.scalajack/json/JsonParser2.scala new file mode 100644 index 00000000..2922ab5d --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/JsonParser2.scala @@ -0,0 +1,219 @@ +package co.blocke.scalajack +package json + +import parser.* + +import scala.util.* +import co.blocke.scala_reflection.TypedName +import scala.collection.mutable.{ListBuffer, HashMap} + +case class JsonParser2(js: String, cfg: JsonConfig) extends Parser: + + //-------------------------------------- + //-------------------------------------- + // JSON Housekeeping + //-------------------------------------- + //-------------------------------------- + + private val jsChars: Array[Char] = js.toCharArray + private var i = 0 + private val max: Int = jsChars.length + + inline def eatWhitespace = + while i < max && jsChars(i).isWhitespace do i += 1 + + inline def nullCheck: Boolean = + if i + 3 < max && jsChars(i) == 'n' && jsChars(i + 1) == 'u' && jsChars(i + 2) == 'l' && jsChars(i + 3) == 'l' then + i += 4 + true + else + false + + inline def expectComma: Either[ParseError, Unit] = // Note: this consumes whitespace before/after the ',' + eatWhitespace + if jsChars(i) == ',' then + i += 1 + eatWhitespace + Right(()) + else + Left(CommaExpected(showError(s"Comma expected at position [$i]"))) + + inline def expectColon: Either[ParseError, Unit] = // Note: this consumes whitespace before/after the ':' + eatWhitespace + if jsChars(i) == ':' then + i += 1 + eatWhitespace + Right(()) + else + Left(JsonParseError(showError(s"Expected colon at position [$i]"))) + + // JSON label (i.e. object key, which is always a simple string, ie no escaped/special chars) + inline def expectLabel: Either[ParseError, String] = + jsChars(i) match + case '"' => + i += 1 + val mark = i + while i < max && jsChars(i) != '"' do i += 1 + i += 1 + Right(js.substring(mark, i - 1)) + case x => Left(JsonParseError(showError(s"Unexpected character '$x' where beginning of label expected at position [$i]"))) + + //-------------------------------------- + //-------------------------------------- + // Parser Implementation + //-------------------------------------- + //-------------------------------------- + inline def parseBoolean(): Either[ParseError, Boolean] = + jsChars(i) match + case 't' if i + 3 < max && jsChars(i + 1) == 'r' && jsChars(i + 2) == 'u' && jsChars(i + 3) == 'e' => + i += 4 + Right(true) + case 'f' if i + 4 < max && jsChars(i + 1) == 'a' && jsChars(i + 2) == 'l' && jsChars(i + 3) == 's' && jsChars(i + 4) == 'e' => + i += 5 + Right(false) + case x => + // if (isMapKey || cfg.permissivePrimitives) && jsChars(i) == '"' then + // for { + // _ <- expectQuote + // result <- expectBoolean() + // _ <- expectQuote + // } yield result + // else + Left(JsonParseError(showError(s"Unexpected character '$x' where beginning of boolean value expected at position [$i]"))) + + + inline def parseString(): Either[ParseError, String] = + nullCheck match + case true if cfg.forbidNullsInInput => Left(JsonParseError(showError(s"Forbidden 'null' value received at position [$i]"))) + case true => Right(null.asInstanceOf[String]) + case false => + jsChars(i) match + case '"' => + i += 1 + val mark = i // save position in case we need complex string parse + var captured: Option[String] = None + while i < max && jsChars(i) != '"' do + jsChars(i) match + case '\\' => // oops! special char found--do slow string parse + i = mark + captured = Some(_expectString) + case _ => i += 1 + i += 1 + Right(captured.getOrElse(js.substring(mark, i - 1))) + case x => Left(JsonParseError(showError(s"Unexpected character '$x' where beginning of a string expected at position [$i]"))) + + inline def parseLong(): Either[ParseError, Long] = + val mark = i + var done = false + while !done do + jsChars(i) match + case c if (c >= '0' && c <= '9') || c == '-' => i += 1 + case _ => done = true + Try(js.substring(mark, i).toLong) match + case Success(g) => Right(g) + case Failure(f) => + // if (isMapKey || cfg.permissivePrimitives) && jsChars(i) == '"' then + // for { + // _ <- expectQuote + // result <- expectLong() + // _ <- expectQuote + // } yield result + // else + val msg = + if mark == i then s"""Int/Long expected but couldn't parse from "${jsChars(i)}" at position [$i]""" + else s"""Int/Long expected but couldn't parse from "${js.substring(mark, i)}" at position [$i]""" + i = mark + Left(JsonParseError(showError(msg))) + + def parseList(inst: Instruction): Either[ParseError, List[inst.Z]] = + nullCheck match + case true if cfg.forbidNullsInInput => Left(JsonParseError(showError(s"Forbidden 'null' value received at position [$i]"))) + case true => Right(null) + case false => + if jsChars(i) != '[' then Left(JsonParseError(showError(s"Beginning of list expected at position [$i]"))) + else + i += 1 + eatWhitespace + val acc = ListBuffer.empty[inst.Z] + var done: Option[Either[ParseError, List[inst.Z]]] = None + while done.isEmpty do + (for { + el <- parse(inst) + _ = acc.addOne(el) + _ <- expectComma + } yield el) match + case Left(CommaExpected(_)) if jsChars(i) == ']' => + i += 1 + eatWhitespace + done = Some(Right(acc.toList)) + case Left(e) => + done = Some(Left(e)) + case Right(_) => + done.get + + // Special case of JSON object where each entry is a field of a class + def parseClass(inst: Map[String,Instruction], fieldValues: HashMap[String,Any]): Either[ParseError, Map[String,Any]] = + if jsChars(i) != '{' then Left(JsonParseError(showError(s"Beginning of class object expected at position [$i]"))) + else + i += 1 + eatWhitespace + var done: Option[Either[ParseError, Map[String, ?]]] = None + while done.isEmpty do + (for { + fieldLabel <- expectLabel + _ <- expectColon + fieldValue <- inst.get(fieldLabel).map(parse(_)).getOrElse(null) + _ = fieldValues.put(fieldLabel, fieldValue) + _ <- expectComma + } yield fieldValue) match + case Left(CommaExpected(_)) if jsChars(i) == '}' => + i += 1 + eatWhitespace + done = Some(Right(fieldValues.toMap)) + case Left(e) => + done = Some(Left(e)) + case Right(_) => + done.get + + //-------------------------------------- + //-------------------------------------- + // Utility Functions + //-------------------------------------- + //-------------------------------------- + + // Slower String parsing that handles special escaped chars + private def _expectString = + val builder = new java.lang.StringBuilder() + while i < max && jsChars(i) != '"' do + if jsChars(i) == '\\' then { + jsChars(i + 1) match { + case c @ ('"' | '\\' | 'b' | 'f' | 'n' | 'r' | 't') => + builder.append(c) + i += 2 + + case 'u' => + val hexEncoded = js.substring(i + 2, i + 6) + val unicodeChar = Integer.parseInt(hexEncoded, 16).toChar + builder.append(unicodeChar.toString) + i += 6 + + case c => + builder.append(c) + i += 2 + } + } else { + builder.append(jsChars(i)) + i += 1 + } + builder.toString + + def showError(msg: String): String = { + val (clip, dashes) = i match { + case ep if ep <= 50 && max < 80 => (js, ep) + case ep if ep <= 50 => (js.substring(0, 77) + "...", ep) + case ep if ep > 50 && ep + 30 >= max => + ("..." + js.substring(i - 49), 52) + case ep => ("..." + js.substring(ep - 49, ep + 27) + "...", 52) + } + msg + s" at position [$i]" + "\n" + clip.replaceAll("[\n\t]", "~") + "\n" + ("-" * dashes) + "^" + } diff --git a/src/main/scala/co.blocke.scalajack/json/JsonParser3.scala b/src/main/scala/co.blocke.scalajack/json/JsonParser3.scala new file mode 100644 index 00000000..260f800b --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/JsonParser3.scala @@ -0,0 +1,188 @@ +package co.blocke.scalajack +package json + +import scala.util.{Try, Success} +import scala.annotation._ + +case class JsonParser3( js: String ): + + trait Terminal + sealed trait JSON + + case class StringJson( b: Int, e: Int ) extends JSON: + def get: String = js.substring(b,e) + case class BooleanJson( v: Boolean ) extends JSON + case class IntJson( v: Long ) extends JSON + case class FloatJson( v: Double ) extends JSON + case class ObjectJson( v: Map[String, JSON] ) extends JSON + case class ArrayJson( v: List[JSON] ) extends JSON + case class NullJson() extends JSON + + case class TerminateArrayJson() extends JSON with Terminal // marker for end of Array/Object + case class TerminateObjectJson() extends JSON with Terminal // marker for end of Array/Object + + private val jsChars: Array[Char] = js.toCharArray + private var i = 0 + private val max: Int = jsChars.length + + def reset() = i = 0 + + def parse(expectComma: Boolean = false): JSON = + var result: Option[JSON] = None + val commaFound: Option[JSON] = + if expectComma then + var found = if i == max && (jsChars(i-1)==']' || jsChars(i-1)=='}') then true else false + var done = false + while i < max && !done do + (jsChars(i): @switch) match + case ' ' => + i += 1 + case '\n' => + i += 1 + case '\t' => + i += 1 + case '\r' => + i += 1 + case ',' => + i += 1 + found = true + done = true + case ':' => // cheat! Treat ',' and ':' the same, so {"foo","bar","a","b"} == {"foo":"bar","a":"b"} + i += 1 + found = true + done = true + case ']' => + i += 1 + found = true + done = true + case '}' => + i += 1 + found = true + done = true + case c => + done = true + found match + case true if jsChars(i-1) == '}' => Some(TerminateObjectJson()) + case true if jsChars(i-1) == ']' => Some(TerminateArrayJson()) + case true => None // comma found... continue parsing + case _ => throw new JsonParseError("Unterminated array or object") + else + None // no comma involvement--continue parsing + + commaFound.getOrElse{ + while i < max && result.isEmpty do { + (jsChars(i): @switch) match + case '"' => + i += 1 + result = Some(parseString()) + case 't' | 'f' => + result = Some(parseBoolean()) + case 'n' => + result = Some(parseNull()) + case ' ' => + i += 1 + case '\n' => + i += 1 + case '\t' => + i += 1 + case '\r' => + i += 1 + case ']' => + i += 1 + result = Some(TerminateArrayJson()) + case '}' => + i += 1 + result = Some(TerminateObjectJson()) + case '[' => + val acc = scala.collection.mutable.ListBuffer.empty[JSON] + i += 1 + var last: JSON = parse() + while !last.isInstanceOf[TerminateArrayJson] do + acc.addOne(last) + last = parse(true) + result = Some(ArrayJson(acc.toList)) + case '{' => + val acc = scala.collection.mutable.Map.empty[String,JSON] + i += 1 + var done = false + var key: JSON = parse() + while !done do + key match + case k if k.isInstanceOf[StringJson] => + // while i < max && !key.isInstanceOf[TerminateObjectJson] do + val value: JSON = parse(true) + if value.isInstanceOf[Terminal] then + throw new JsonParseError("Malformed object") + acc.put(k.asInstanceOf[StringJson].get, value) + key = parse(true) + case k if k.isInstanceOf[TerminateObjectJson] => + result = Some(ObjectJson(acc.toMap)) + done = true + case _ => + throw new JsonParseError("Malformed object (invalid key value)") + if i == max && key.isInstanceOf[TerminateObjectJson] then result = Some(ObjectJson(acc.toMap)) + case c if (c >= '0' && c <= '9') || c == '-' || c == '.' || c == '+' => + result = Some(parseNumber()) + } + result.getOrElse(throw new JsonParseError("Unterminated array or object")) + } + + inline def parseNull() = + if i + 3 < max && jsChars(i) == 'n' && jsChars(i + 1) == 'u' && jsChars(i + 2) == 'l' && jsChars(i + 3) == 'l' then + i += 4 + NullJson() + else + throw new JsonParseError(s"Unexpected character '${jsChars(i)}'") + + inline def parseBoolean() = + jsChars(i) match + case 't' if i + 3 < max && jsChars(i + 1) == 'r' && jsChars(i + 2) == 'u' && jsChars(i + 3) == 'e' => + i += 4 + BooleanJson(true) + case 'f' if i + 4 < max && jsChars(i + 1) == 'a' && jsChars(i + 2) == 'l' && jsChars(i + 3) == 's' && jsChars(i + 4) == 'e' => + i += 5 + BooleanJson(false) + case _ => + throw new JsonParseError(s"Unexpected character '${jsChars(i)}'") + + inline def parseString() = + val mark = i + while i < max && jsChars(i) != '"' do + if jsChars(i) == '\\' then // skip escaped special characters + i += 1 + i += 1 + if i == max then + i = mark + throw new JsonParseError(s"Unterminated string starting") + i += 1 + StringJson(mark, i-1) + + inline def parseNumber() = + var isFloat = false + var done = false + val mark = i + while i < max && !done do + jsChars(i) match + case c if (c >= '0' && c <= '9') => + i += 1 + case '.' => + isFloat = true + i += 1 + case 'e' | 'E' | '-' | '+' => + i += 1 + case _ => + done = true + if isFloat then + Try(js.substring(mark,i).toDouble) match + case Success(v) => + FloatJson(v) + case _ => + i = mark + throw new JsonParseError(s"Cannot parse number starting") + else + Try(js.substring(mark,i).toLong) match + case Success(v) => + IntJson(v) + case _ => + i = mark + throw new JsonParseError(s"Cannot parse number starting") diff --git a/src/main/scala/co.blocke.scalajack/json/JsonReader.scala b/src/main/scala/co.blocke.scalajack/json/JsonReader.scala index 2b392b1b..cf569c9e 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonReader.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonReader.scala @@ -2,45 +2,44 @@ package co.blocke.scalajack package json import co.blocke.scala_reflection.{RTypeRef, TypedName} +import co.blocke.scala_reflection.reflect.rtypeRefs.* +import parser.* import scala.quoted.* import scala.collection.mutable.HashMap import scala.util.{Failure, Success, Try} - -case class JsonReader() extends ReaderModule: - - val root: ReaderModule = null // Should never be accessed--we're the root! - - // Did the user supply an extension module? - val extension = Try(Class.forName("co.blocke.scalajack.json.ReaderExtension")) match - case Success(c) => Some(c.getDeclaredConstructor().newInstance.asInstanceOf[ReaderModule]) - case Failure(_) => None - - val modules = readers.PrimitiveReader( - readers.ColletionReader( - readers.ClassReader( - readers.EnumReader( - readers.MiscReader( - TerminusReaderModule(extension, root), - root - ), - root - ), - this - ), - this - ), - this - ) - - def readerFn[T](ref: RTypeRef[T], isMapKey: Boolean = false)(using q: Quotes, tt: Type[T])(using cache: HashMap[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]]): Expr[(JsonConfig, JsonParser) => Either[ParseError, T]] = - modules.readerFn[T](ref) - - // TODO: - // * Java Enums - // * Java Classes - // * Non-case Scala classes - // * SealedTraitRef - // * TraitRef - // * Primitive: Any - - // ----------------------------------- +import scala.collection.Factory + +object JsonReader: + + def refRead[T]( + ref: RTypeRef[T] + )(using q: Quotes, tt: Type[T]): Expr[Instruction] = + import quotes.reflect.* + + ref match + case r: PrimitiveRef[?] if r.family == PrimFamily.Boolish => '{ BooleanInstruction() } + case r: PrimitiveRef[?] if r.family == PrimFamily.Stringish => '{ StringInstruction() } + case r: PrimitiveRef[?] if r.family == PrimFamily.Longish => '{ IntInstruction() } + + case r: SeqRef[?] => + r.elementRef.refType match + case '[e] => + r.refType match + case '[t] => + val elementInstruction = refRead[e](r.elementRef.asInstanceOf[RTypeRef[e]]) + '{ SeqInstruction[e, t]($elementInstruction)(using ${ Expr.summon[Factory[e, t]].get }) } + + case r: ScalaClassRef[?] => + r.refType match + case '[t] => + val fieldInstructions = Expr.ofList( + r.fields + .map { f => + f.fieldRef.refType match + case '[e] => + (Expr(f.name), refRead[e](f.fieldRef.asInstanceOf[RTypeRef[e]])) + } + .map(u => Expr.ofTuple(u)) + ) + val instantiator = JsonReaderUtil.classInstantiator[t](r.asInstanceOf[ClassRef[t]]) + '{ ScalaClassInstruction[t]($fieldInstructions.toMap, $instantiator) } diff --git a/src/main/scala/co.blocke.scalajack/json/JsonReader.scalax b/src/main/scala/co.blocke.scalajack/json/JsonReader.scalax new file mode 100644 index 00000000..f9c395c8 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/JsonReader.scalax @@ -0,0 +1,49 @@ +package co.blocke.scalajack +package json + +import co.blocke.scala_reflection.{RTypeRef, TypedName} +import scala.quoted.* +import scala.collection.mutable.HashMap +import scala.util.{Failure, Success, Try} + +trait JsonReader[B, T]: + def apply(in: B): T + +/* +case class JsonReader() extends ReaderModule: + + val root: ReaderModule = this // Should never be accessed--we're the root! + + // Did the user supply an extension module? + val extension = Try(Class.forName("co.blocke.scalajack.json.ReaderExtension")) match + case Success(c) => Some(c.getDeclaredConstructor().newInstance.asInstanceOf[ReaderModule]) + case Failure(_) => None + + val next: Option[ReaderModule] = Some( + readers.PrimitiveReader( + Some(readers.CollectionReader(Some(TerminusReaderModule(extension, root)), root)), + // readers.ColletionReader( + // readers.ClassReader( + // readers.EnumReader( + // readers.MiscReader( + // TerminusReaderModule(extension, root), + // root + // ), + // root + // ), + // this + // ), + // this + // ), + this + ) + ) + + def refRead[T]( + ref: RTypeRef[T], + parserE: Expr[JsonParser], + cfgE: Expr[JsonConfig], + isMapKey: Boolean = false + )(using q: Quotes, tt: Type[T])(using cache: HashMap[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]]): Expr[Either[ParseError, T]] = + next.get.refRead(ref, parserE, cfgE, isMapKey) + */ diff --git a/src/main/scala/co.blocke.scalajack/json/JsonReaderUtil.scala b/src/main/scala/co.blocke.scalajack/json/JsonReaderUtil.scala index 195fd384..1b52e355 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonReaderUtil.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonReaderUtil.scala @@ -24,6 +24,7 @@ object JsonReaderUtil: } } + /* def tupleInstantiator[T: Type](ref: TupleRef[T])(using Quotes): Expr[List[?] => T] = import quotes.reflect.* val sym = TypeRepr.of[T].classSymbol.get @@ -49,22 +50,23 @@ object JsonReaderUtil: tree.asExpr.asExprOf[T] } } + */ - def classParseMap[T: Type](ref: ClassRef[T], root: ReaderModule)(using q: Quotes)(using - cache: scala.collection.mutable.HashMap[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]] - ): Expr[JsonParser => Map[String, (JsonConfig, JsonParser) => Either[ParseError, ?]]] = - import Clazzes.* - '{ (parser: JsonParser) => - val daList = ${ - val fieldList = ref.fields.map(f => - f.fieldRef.refType match - case '[m] => - val fn = root.readerFn[m](f.fieldRef.asInstanceOf[RTypeRef[m]]) - '{ - ${ Expr(f.name) } -> $fn - } - ) - Expr.ofList(fieldList) - } - daList.toMap - } + // def classParseMap[T: Type](ref: ClassRef[T], root: ReaderModule)(using q: Quotes)(using + // cache: scala.collection.mutable.HashMap[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]] + // ): Expr[JsonParser => Map[String, (JsonConfig, JsonParser) => Either[ParseError, ?]]] = + // import Clazzes.* + // '{ (parser: JsonParser) => + // val daList = ${ + // val fieldList = ref.fields.map(f => + // f.fieldRef.refType match + // case '[m] => + // val fn = root.readerFn[m](f.fieldRef.asInstanceOf[RTypeRef[m]]) + // '{ + // ${ Expr(f.name) } -> $fn + // } + // ) + // Expr.ofList(fieldList) + // } + // daList.toMap + // } diff --git a/src/main/scala/co.blocke.scalajack/json/ParseInstructions.scalax b/src/main/scala/co.blocke.scalajack/json/ParseInstructions.scalax new file mode 100644 index 00000000..3051b812 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/ParseInstructions.scalax @@ -0,0 +1,11 @@ +package co.blocke.scalajack +package json + + +sealed trait ParseInstruction + +// A JsonReader converts the JSON type to final Scala/Java type +case class ParseLong[T]( reader: JsonReader[Long,T] ) extends ParseInstruction +case class ParseString[T]( reader: JsonReader[String,T] ) extends ParseInstruction +case class ParseList[T]( reader: JsonReader[List[T],Seq[T]] ) extends ParseInstruction +case class ParseObject[T]( fields: Map[String, JsonReader[Map[String,?],T]] ) extends ParseInstruction \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json/ReaderModule.scala b/src/main/scala/co.blocke.scalajack/json/ReaderModule.scalax similarity index 100% rename from src/main/scala/co.blocke.scalajack/json/ReaderModule.scala rename to src/main/scala/co.blocke.scalajack/json/ReaderModule.scalax diff --git a/src/main/scala/co.blocke.scalajack/json/ReaderModule2.scalax b/src/main/scala/co.blocke.scalajack/json/ReaderModule2.scalax new file mode 100644 index 00000000..172f6e9e --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/ReaderModule2.scalax @@ -0,0 +1,35 @@ +package co.blocke.scalajack +package json + +import co.blocke.scala_reflection.{RTypeRef, TypedName} +import scala.quoted.* +import scala.collection.mutable.HashMap +import scala.util.{Failure, Success, Try} + +trait ReaderModule: + val next: Option[ReaderModule] + val root: ReaderModule + + def refRead[T]( + ref: RTypeRef[T], + parserE: Expr[JsonParser], + cfgE: Expr[JsonConfig], + isMapKey: Boolean = false + )(using q: Quotes, tt: Type[T])(using cache: HashMap[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]]): Expr[Either[ParseError, T]] + +case class TerminusReaderModule(next: Option[ReaderModule], root: ReaderModule) extends ReaderModule: + + def refRead[T]( + ref: RTypeRef[T], + parserE: Expr[JsonParser], + cfgE: Expr[JsonConfig], + isMapKey: Boolean = false + )(using q: Quotes, tt: Type[T])(using cache: HashMap[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]]): Expr[Either[ParseError, T]] = + + val name = Expr(ref.name) + next match + case None => '{ Left(JsonParseError("Unknown (or unsupported) type " + $name)) } + case Some(ext) => + Try(ext.refRead[T](ref, parserE, cfgE, isMapKey)) match + case Success(v) => v + case Failure(_) => '{ Left(JsonParseError("Unknown (or unsupported) type " + $name)) } diff --git a/src/main/scala/co.blocke.scalajack/json/readers/ClassReader.scala b/src/main/scala/co.blocke.scalajack/json/readers/ClassReader.scalax similarity index 100% rename from src/main/scala/co.blocke.scalajack/json/readers/ClassReader.scala rename to src/main/scala/co.blocke.scalajack/json/readers/ClassReader.scalax diff --git a/src/main/scala/co.blocke.scalajack/json/readers/CollectionReader.scala b/src/main/scala/co.blocke.scalajack/json/readers/CollectionReader.scalax similarity index 74% rename from src/main/scala/co.blocke.scalajack/json/readers/CollectionReader.scala rename to src/main/scala/co.blocke.scalajack/json/readers/CollectionReader.scalax index 3831e4fe..ef5322b3 100644 --- a/src/main/scala/co.blocke.scalajack/json/readers/CollectionReader.scala +++ b/src/main/scala/co.blocke.scalajack/json/readers/CollectionReader.scalax @@ -14,23 +14,29 @@ import scala.jdk.CollectionConverters.* import scala.collection.mutable.HashMap import scala.util.{Failure, Success, Try} -case class ColletionReader(next: ReaderModule, root: ReaderModule) extends ReaderModule: +case class CollectionReader(next: Option[ReaderModule], root: ReaderModule) extends ReaderModule: - def readerFn[T](ref: RTypeRef[T], isMapKey: Boolean = false)(using q: Quotes, tt: Type[T])(using cache: HashMap[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]]): Expr[(JsonConfig, JsonParser) => Either[ParseError, T]] = + def refRead[T]( + ref: RTypeRef[T], + parserE: Expr[JsonParser], + cfgE: Expr[JsonConfig], + isMapKey: Boolean = false + )(using q: Quotes, tt: Type[T])(using cache: HashMap[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]]): Expr[Either[ParseError, T]] = import quotes.reflect.* ref match case t: SeqRef[T] => - if isMapKey then throw new JsonError("Seq types cannot be map keys.") - t.refType match - case '[s] => - t.elementRef.refType match - case '[e] => - val subFn = root.readerFn[e](t.elementRef.asInstanceOf[RTypeRef[e]]).asInstanceOf[Expr[(JsonConfig, JsonParser) => Either[ParseError, e]]] - '{ (j: JsonConfig, p: JsonParser) => - p.expectList[e](j, $subFn).map(_.to(${ Expr.summon[Factory[e, T]].get })) // Convert List to whatever the target type should be - } + if isMapKey then '{ Left(new JsonError("Seq types cannot be map keys.")) } + t.elementRef.refType match + case '[e] => + val fn = () => root.refRead[e](t.elementRef.asInstanceOf[RTypeRef[e]], parserE, cfgE) + '{ + $parserE + .expectList[e]($fn) + .map(_.to(${ Expr.summon[Factory[e, T]].get })) // Convert List to whatever the target type should be + } + /* case t: MapRef[T] => if isMapKey then throw new JsonError("Map types cannot be map keys.") t.refType match @@ -99,6 +105,26 @@ case class ColletionReader(next: ReaderModule, root: ReaderModule) extends Reade case Failure(e) => Left(JsonParseError(p.showError(s"Could not instantiate a $cname, with error: " + e))) ) } + */ case t => - next.readerFn[T](t) + next.get.refRead(ref, parserE, cfgE, isMapKey) + + + + /* + + 1. At compile-time, use reflected RTypes to build a set of pre-baked instructions for the parser... an entire set of commands to parse the given object + 2. Pass this command set to the parser at runtime for linear execution + + + Parson(name: String, age: Int, stuff: List[String]) + + ParseObject + fields: + name: ParseString + age: ParseInt + stuff: ParseList + ParseString + + */ \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json/readers/EnumReader.scala b/src/main/scala/co.blocke.scalajack/json/readers/EnumReader.scalax similarity index 100% rename from src/main/scala/co.blocke.scalajack/json/readers/EnumReader.scala rename to src/main/scala/co.blocke.scalajack/json/readers/EnumReader.scalax diff --git a/src/main/scala/co.blocke.scalajack/json/readers/MiscReader.scala b/src/main/scala/co.blocke.scalajack/json/readers/MiscReader.scalax similarity index 100% rename from src/main/scala/co.blocke.scalajack/json/readers/MiscReader.scala rename to src/main/scala/co.blocke.scalajack/json/readers/MiscReader.scalax diff --git a/src/main/scala/co.blocke.scalajack/json/readers/PrimitiveReader.scala b/src/main/scala/co.blocke.scalajack/json/readers/PrimitiveReader.scalax similarity index 61% rename from src/main/scala/co.blocke.scalajack/json/readers/PrimitiveReader.scala rename to src/main/scala/co.blocke.scalajack/json/readers/PrimitiveReader.scalax index d290ac89..40f733ad 100644 --- a/src/main/scala/co.blocke.scalajack/json/readers/PrimitiveReader.scala +++ b/src/main/scala/co.blocke.scalajack/json/readers/PrimitiveReader.scalax @@ -14,130 +14,104 @@ import scala.jdk.CollectionConverters.* import scala.collection.mutable.HashMap import scala.util.{Failure, Success, Try} -case class PrimitiveReader(next: ReaderModule, root: ReaderModule) extends ReaderModule: - - def readerFn[T](ref: RTypeRef[T], isMapKey: Boolean = false)(using q: Quotes, tt: Type[T])(using cache: HashMap[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]]): Expr[(JsonConfig, JsonParser) => Either[ParseError, T]] = - import Clazzes.* +case class IntReader() extends JsonReader[Long, Int]: + def apply(in: Long) = in.toInt + +case class StringReader() extends JsonReader[String, String]: + def apply(in: String) = in + +// Magic line to convert a List[T] to some U <: Seq[?], eg zconvert[Int,Set[?]] +// can use this if I have element type T, and target collection U, which the SeqRef has! +// This line should be executed at runtime. +// +// inline def zconvert[T,U](in:List[T])(using Factory[T, U]) = in.to( summon[Factory[T,U]] ) +// +// case class SeqReader[B, T](toObj: B) extends JsonReader[List[T], Seq[T]]: +// def apply(in: List[T]) = in.to(toObj) +// def apply[s](in: List[T]) = in.to(${ Expr.summon[Factory[T, s]].get }) + +/* +case class PrimitiveReader(next: Option[ReaderModule], root: ReaderModule) extends ReaderModule: + + def refRead[T]( + ref: RTypeRef[T], + parserE: Expr[JsonParser], + cfgE: Expr[JsonConfig], + isMapKey: Boolean = false + )(using q: Quotes, tt: Type[T])(using cache: HashMap[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]]): Expr[Either[ParseError, T]] = import quotes.reflect.* + import Clazzes.* ref match - // // Scala Primitives // case t: PrimitiveRef[T] if t.name == BIG_DECIMAL_CLASS => - if isMapKey then - '{ (j: JsonConfig, p: JsonParser) => - (for { - _ <- p.expectQuote - v <- p.expectBigDouble(j, p).map(_.asInstanceOf[T]) - _ <- p.expectQuote - } yield v) - } - else '{ (j: JsonConfig, p: JsonParser) => p.expectBigDouble(j, p).map(_.asInstanceOf[T]) } + val isMapKeyE = Expr(isMapKey) + '{ $parserE.expectBigDouble($isMapKeyE).map(_.asInstanceOf[T]) } case t: PrimitiveRef[T] if t.name == BIG_INT_CLASS => - if isMapKey then - '{ (j: JsonConfig, p: JsonParser) => - (for { - _ <- p.expectQuote - v <- p.expectBigLong(j, p).map(_.asInstanceOf[T]) - _ <- p.expectQuote - } yield v) - } - else '{ (j: JsonConfig, p: JsonParser) => p.expectBigLong(j, p).map(_.asInstanceOf[T]) } + val isMapKeyE = Expr(isMapKey) + '{ $parserE.expectBigLong($isMapKeyE).map(_.asInstanceOf[T]) } case t: PrimitiveRef[T] if t.name == BOOLEAN_CLASS => - if isMapKey then - '{ (j: JsonConfig, p: JsonParser) => - (for { - _ <- p.expectQuote - v <- p.expectBoolean(j, p).map(_.asInstanceOf[T]) - _ <- p.expectQuote - } yield v) - } - else '{ (j: JsonConfig, p: JsonParser) => p.expectBoolean(j, p).map(_.asInstanceOf[T]) } + val isMapKeyE = Expr(isMapKey) + '{ $parserE.expectBoolean($isMapKeyE).map(_.asInstanceOf[T]) } case t: PrimitiveRef[T] if t.name == BYTE_CLASS => - if isMapKey then - '{ (j: JsonConfig, p: JsonParser) => - (for { - _ <- p.expectQuote - v <- p.expectLong(j, p).map(_.toByte.asInstanceOf[T]) - _ <- p.expectQuote - } yield v) - } - else '{ (j: JsonConfig, p: JsonParser) => p.expectLong(j, p).map(_.toByte.asInstanceOf[T]) } + val isMapKeyE = Expr(isMapKey) + '{ $parserE.expectLong($isMapKeyE).map(_.toByte.asInstanceOf[T]) } case t: PrimitiveRef[T] if t.name == CHAR_CLASS => - '{ (j: JsonConfig, p: JsonParser) => - p.expectString(j, p) + '{ + $parserE + .expectString() .flatMap(s => - if s == null then Left(JsonParseError(p.showError(s"Char typed values cannot be null at position [${p.getPos}]"))) + if s == null then Left(JsonParseError($parserE.showError(s"Char typed values cannot be null"))) else s.toArray.headOption match case Some(c) => Right(c.asInstanceOf[T]) - case None => Left(JsonParseError(p.showError(s"Cannot convert value '$s' into a Char at position [${p.getPos}]"))) + case None => Left(JsonParseError($parserE.showError(s"Cannot convert value '$s' into a Char"))) ) } case t: PrimitiveRef[T] if t.name == DOUBLE_CLASS => - if isMapKey then - '{ (j: JsonConfig, p: JsonParser) => - (for { - _ <- p.expectQuote - v <- p.expectDouble(j, p).map(_.asInstanceOf[T]) - _ <- p.expectQuote - } yield v) - } - else '{ (j: JsonConfig, p: JsonParser) => p.expectDouble(j, p).map(_.asInstanceOf[T]) } + val isMapKeyE = Expr(isMapKey) + '{ $parserE.expectDouble($isMapKeyE).map(_.asInstanceOf[T]) } case t: PrimitiveRef[T] if t.name == FLOAT_CLASS => - if isMapKey then - '{ (j: JsonConfig, p: JsonParser) => - (for { - _ <- p.expectQuote - v <- p.expectDouble(j, p).map(_.toFloat.asInstanceOf[T]) - _ <- p.expectQuote - } yield v) - } - else '{ (j: JsonConfig, p: JsonParser) => p.expectDouble(j, p).map(_.toFloat.asInstanceOf[T]) } + val isMapKeyE = Expr(isMapKey) + '{ $parserE.expectDouble($isMapKeyE).map(_.toFloat.asInstanceOf[T]) } case t: PrimitiveRef[T] if t.name == INT_CLASS => - if isMapKey then - '{ (j: JsonConfig, p: JsonParser) => - (for { - _ <- p.expectQuote - v <- p.expectLong(j, p).map(_.toInt.asInstanceOf[T]) - _ <- p.expectQuote - } yield v) - } - else '{ (j: JsonConfig, p: JsonParser) => p.expectLong(j, p).map(_.toInt.asInstanceOf[T]) } + val isMapKeyE = Expr(isMapKey) + '{ $parserE.expectLong($isMapKeyE).map(_.toInt.asInstanceOf[T]) } case t: PrimitiveRef[T] if t.name == LONG_CLASS => - if isMapKey then - '{ (j: JsonConfig, p: JsonParser) => - (for { - _ <- p.expectQuote - v <- p.expectLong(j, p).map(_.asInstanceOf[T]) - _ <- p.expectQuote - } yield v) - } - else '{ (j: JsonConfig, p: JsonParser) => p.expectLong(j, p).map(_.asInstanceOf[T]) } + val isMapKeyE = Expr(isMapKey) + '{ $parserE.expectLong($isMapKeyE).map(_.asInstanceOf[T]) } case t: PrimitiveRef[T] if t.name == SHORT_CLASS => - if isMapKey then - '{ (j: JsonConfig, p: JsonParser) => - (for { - _ <- p.expectQuote - v <- p.expectLong(j, p).map(_.toShort.asInstanceOf[T]) - _ <- p.expectQuote - } yield v) - } - else '{ (j: JsonConfig, p: JsonParser) => p.expectLong(j, p).map(_.toShort.asInstanceOf[T]) } + val isMapKeyE = Expr(isMapKey) + '{ $parserE.expectLong($isMapKeyE).map(_.toShort.asInstanceOf[T]) } case t: PrimitiveRef[T] if t.name == STRING_CLASS => - '{ (j: JsonConfig, p: JsonParser) => p.expectString(j, p).map(_.asInstanceOf[T]) } + '{ $parserE.expectString().map(_.asInstanceOf[T]) } + + case t => + next.get.refRead(ref, parserE, cfgE, isMapKey) + */ + +/* + def readerFn[T](ref: RTypeRef[T], isMapKey: Boolean = false)(using q: Quotes, tt: Type[T])(using cache: HashMap[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]]): Expr[(JsonConfig, JsonParser) => Either[ParseError, T]] = + import Clazzes.* + import quotes.reflect.* + + ref match + + // + // Scala Primitives + // // // Java Primitives @@ -243,3 +217,4 @@ case class PrimitiveReader(next: ReaderModule, root: ReaderModule) extends Reade case t => next.readerFn[T](t) + */ diff --git a/src/main/scala/co.blocke.scalajack/json2/ClassDecoder.scala b/src/main/scala/co.blocke.scalajack/json2/ClassDecoder.scala new file mode 100644 index 00000000..8b8d1618 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json2/ClassDecoder.scala @@ -0,0 +1,31 @@ +package co.blocke.scalajack +package json2 + +import json.JsonParseError +import scala.annotation._ + +trait ClassDecoder[A] extends JsonDecoder[A]: + self => + def unsafeDecodeField(in: JsonReader): A + +object ClassDecoder: + def apply[A: scala.reflect.ClassTag](fields: Array[String], fieldDecoders: Array[JsonDecoder[_]]) = new JsonDecoder[A] { + val fieldMatrix = new StringMatrix(fields) + // not ideal--use Scala macros, but for now... + val constructor = summon[scala.reflect.ClassTag[A]].runtimeClass.getConstructors().head + + def unsafeDecode(in: JsonReader): A = + val fieldValues = new Array[Any](fields.length) + JsonParser.charWithWS(in, '{') + if JsonParser.firstField(in) then + var done = false + while !done do + val fieldIdx = JsonParser.parseField(in, fieldMatrix) + if fieldIdx < 0 then JsonParser.skipValue(in) + else + val dec = fieldDecoders(fieldIdx) + fieldValues(fieldIdx) = dec.unsafeDecode(in) + if !JsonParser.nextField(in) then done = true + else throw new JsonParseError("Expected fields!") + constructor.newInstance(fieldValues*).asInstanceOf[A] + } \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json2/FastStringBuilder.scala b/src/main/scala/co.blocke.scalajack/json2/FastStringBuilder.scala new file mode 100644 index 00000000..7dacfb01 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json2/FastStringBuilder.scala @@ -0,0 +1,21 @@ +package co.blocke.scalajack +package json2 + +import java.nio.CharBuffer +import java.util.Arrays + +// like StringBuilder but doesn't have any encoding or range checks +final class FastStringBuilder(initial: Int = 16) { + private[this] var chars: Array[Char] = new Array[Char](initial) + private[this] var i: Int = 0 + + def append(c: Char): FastStringBuilder = { + if (i == chars.length) + chars = Arrays.copyOf(chars, chars.length * 2) + chars(i) = c + i += 1 + this + } + + def buffer: CharSequence = CharBuffer.wrap(chars, 0, i) +} \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json2/FieldKeyDecoder.scala b/src/main/scala/co.blocke.scalajack/json2/FieldKeyDecoder.scala new file mode 100644 index 00000000..a174b686 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json2/FieldKeyDecoder.scala @@ -0,0 +1,50 @@ +package co.blocke.scalajack +package json2 + +import json.JsonParseError + +trait FieldKeyDecoder[+A] { + self => + + final def map[B](f: A => B): FieldKeyDecoder[B] = + new FieldKeyDecoder[B] { + def unsafeDecodeField(in: String): B = f(self.unsafeDecodeField(in)) + } + + final def mapOrFail[B](f: A => Either[String, B]): FieldKeyDecoder[B] = + new FieldKeyDecoder[B] { + def unsafeDecodeField(in: String): B = + f(self.unsafeDecodeField(in)) match { + case Left(err) => throw JsonParseError(err) + case Right(b) => b + } + } + + def unsafeDecodeField(in: String): A +} + +object FieldKeyDecoder { + def apply[A](implicit a: FieldKeyDecoder[A]): FieldKeyDecoder[A] = a + + implicit val string: FieldKeyDecoder[String] = new FieldKeyDecoder[String] { + def unsafeDecodeField(in: String): String = in + } + + implicit val int: FieldKeyDecoder[Int] = + FieldKeyDecoder[String].mapOrFail { str => + try { + Right(str.toInt) + } catch { + case n: NumberFormatException => Left(s"Invalid Int: '$str': $n") + } + } + + implicit val long: FieldKeyDecoder[Long] = + FieldKeyDecoder[String].mapOrFail { str => + try { + Right(str.toLong) + } catch { + case n: NumberFormatException => Left(s"Invalid Long: '$str': $n") + } + } +} \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json2/JsonDecoder.scala b/src/main/scala/co.blocke.scalajack/json2/JsonDecoder.scala new file mode 100644 index 00000000..05d638e3 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json2/JsonDecoder.scala @@ -0,0 +1,81 @@ +package co.blocke.scalajack +package json2 + +import json.JsonParseError +import scala.annotation._ + +trait JsonDecoder[A]: + final def decodeJson(str: CharSequence): Either[JsonParseError, A] = + try Right(unsafeDecode(new JsonReader(str))) + catch { + case jpe: JsonParseError => Left(jpe) + } + + def unsafeDecode(in: JsonReader): A + +//------------------------------------------------------------ + +object JsonDecoder extends DecoderLowPriority1: + + def apply[A](implicit a: JsonDecoder[A]): JsonDecoder[A] = a + + // Primitive support... + implicit val string: JsonDecoder[String] = new JsonDecoder[String] { + def unsafeDecode(in: JsonReader): String = + JsonParser.parseString(in).toString + } + + implicit val boolean: JsonDecoder[Boolean] = new JsonDecoder[Boolean] { + def unsafeDecode(in: JsonReader): Boolean = + JsonParser.parseBoolean(in) + } + + implicit val int: JsonDecoder[Int] = number(JsonParser.parseInt, _.intValueExact()) + private def number[A]( + f: (JsonReader) => A, + fromBigDecimal: java.math.BigDecimal => A + ): JsonDecoder[A] = + new JsonDecoder[A] { + def unsafeDecode(in: JsonReader): A = + (in.readSkipWhitespace(): @switch) match { + case '"' => + val i = f(in) + JsonParser.char(in, '"') + i + case _ => + in.retract() + f(in) + } + } + + private[json2] def builder[A, T[_]]( + in: JsonReader, + builder: scala.collection.mutable.Builder[A, T[A]] + )(implicit A: JsonDecoder[A]): T[A] = { + JsonParser.charWithWS(in, '[') + var i: Int = 0 + if (JsonParser.firstArrayElement(in)) while ({ + { + builder += A.unsafeDecode(in) + i += 1 + }; JsonParser.nextArrayElement(in) + }) () + builder.result() + } + +//------------------------------------------------------------ + +private trait DecoderLowPriority1: + this: JsonDecoder.type => + + // TODO: Experiment with other Seq *NOT* explicitly provided as separate "implicit def"s. See if this will + // convert them to the correct subtype. + implicit def seq[A: JsonDecoder]: JsonDecoder[Seq[A]] = new JsonDecoder[Seq[A]] { + def unsafeDecode(in: JsonReader): Seq[A] = + builder(in, scala.collection.immutable.Seq.newBuilder[A]) + } + + implicit def list[A: JsonDecoder]: JsonDecoder[List[A]] = new JsonDecoder[List[A]] { + def unsafeDecode(in: JsonReader): List[A] = + builder(in, new scala.collection.mutable.ListBuffer[A]) + } diff --git a/src/main/scala/co.blocke.scalajack/json2/JsonParser.scala b/src/main/scala/co.blocke.scalajack/json2/JsonParser.scala new file mode 100644 index 00000000..671b6e4b --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json2/JsonParser.scala @@ -0,0 +1,164 @@ +package co.blocke.scalajack +package json2 + +import json.JsonParseError +import scala.annotation._ + +object JsonParser: + + private val ull: Array[Char] = "ull".toCharArray + private val alse: Array[Char] = "alse".toCharArray + private val rue: Array[Char] = "rue".toCharArray + + def parseBoolean(gen: JsonReader): Boolean = + (gen.readSkipWhitespace(): @switch) match { + case 't' => + readChars(gen, rue, "true") + true + case 'f' => + readChars(gen, alse, "false") + false + case c => + throw JsonParseError(s"Expected true or false value") + } + + def parseInt(gen: JsonReader): Int = { + checkNumber(gen) + try { + val i = UnsafeNumbers.int_(gen, false) + gen.retract() + i + } catch { + case UnsafeNumbers.UnsafeNumber => throw JsonParseError("Expected an Int") + } + } + + def parseString(gen: JsonReader): CharSequence = + charWithWS(gen, '"') + val sb = new FastStringBuilder(64) + while true do + val c = gen.readEscapedString() + if (c == END_OF_STRING) + return sb.buffer // mutable thing escapes, but cannot be changed + sb.append(c.toChar) + throw JsonParseError("Invalid string value detected") + + // Returns index of field name read in, or -1 if not found + def parseField(gen: JsonReader, fieldMatrix: StringMatrix): Int = + val f = enumeration(gen, fieldMatrix) + charWithWS(gen, ':') + f + + // True if we got anything besides a ], False for ] + def firstArrayElement(in: JsonReader): Boolean = + (in.readSkipWhitespace(): @switch) match + case ']' => false + case _ => + in.retract() + true + + def nextArrayElement(in: JsonReader): Boolean = + (in.readSkipWhitespace(): @switch) match + case ',' => true + case ']' => false + case c => throw JsonParseError(s"expected ',' or ']' got '$c'") + + // True if we got a string (implies a retraction), False for } + def firstField(in: JsonReader): Boolean = + (in.readSkipWhitespace(): @switch) match { + case '"' => true + case '}' => false + case c => + throw JsonParseError(s"expected string or '}' got '$c'") + } + + // True if we got a comma, and False for } + def nextField(in: JsonReader): Boolean = + (in.readSkipWhitespace(): @switch) match { + case ',' => + charWithWS(in, '"') + true + case '}' => false + case c => + throw JsonParseError(s"expected ',' or '}' got '$c'") + } + + def skipValue(in: JsonReader): Unit = + (in.readSkipWhitespace(): @switch) match { + case 'n' => readChars(in, ull, "null") + case 'f' => readChars(in, alse, "false") + case 't' => readChars(in, rue, "true") + case '{' => + if (firstField(in)) { + while ({ + { + char(in, '"') + skipString(in) + char(in, ':') + skipValue(in) + }; nextField(in) + }) () + } + case '[' => + if (firstArrayElement(in)) { + while ({ skipValue(in); nextArrayElement(in) }) () + } + case '"' => + skipString(in) + case '-' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '.' => + skipNumber(in) + case c => throw JsonParseError(s"Unexpected '$c'") + } + + def skipNumber(in: JsonReader): Unit = { + while (isNumber(in.read())) {} + in.retract() + } + + def skipString(in: JsonReader): Unit = + var i: Int = 0 + while ({ i = in.readEscapedString(); i != -1 }) () + + private def checkNumber(gen: JsonReader): Unit = + (gen.readSkipWhitespace(): @switch) match + case '-' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '.' => () + case c => throw JsonParseError(s"Expected a number, got $c") + gen.retract() + + @inline private[this] def isNumber(c: Char): Boolean = + (c: @switch) match + case '+' | '-' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '.' | 'e' | 'E' => true + case _ => false + + private inline def readChars( + gen: JsonReader, + expect: Array[Char], + errMsg: String + ): Unit = + var i: Int = 0 + while i < expect.length do + if gen.read() != expect(i) then throw JsonParseError(s"Expected ${errMsg}") + i += 1 + + @inline def charWithWS(gen: JsonReader, c: Char): Unit = + val got = gen.readSkipWhitespace() + if got != c then throw JsonParseError(s"Expected '$c' got '$got'") + + @inline def char(gen: JsonReader, c: Char): Unit = + val got = gen.read() + if got != c then throw JsonParseError(s"Expected '$c' got '$got'") + + def enumeration( + gen: JsonReader, + matrix: StringMatrix + ): Int = { + var i: Int = 0 + var bs: Long = matrix.initial + var c: Int = -1 + while ({ c = gen.readEscapedString(); c != END_OF_STRING }) { + bs = matrix.update(bs, i, c) + i += 1 + } + bs = matrix.exact(bs, i) + matrix.first(bs) + } \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json2/JsonReader.scala b/src/main/scala/co.blocke.scalajack/json2/JsonReader.scala new file mode 100644 index 00000000..c0f57bde --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json2/JsonReader.scala @@ -0,0 +1,86 @@ +package co.blocke.scalajack +package json2 + +import scala.annotation._ +import json.JsonParseError + +case class JsonReader( js: CharSequence ): + private var i = 0 + private val max = js.length + + inline def read(): Char = + if i < max then + i += 1 + history(i - 1) + else BUFFER_EXCEEDED + + inline def readSkipWhitespace(): Char = + var c: Char = 0 + while { c = read(); isWhitespace(c) } do () + c + + inline private def history(p: Int): Char = js.charAt(p) + + inline def retract() = i -= 1 + + inline private def isWhitespace(c: Char): Boolean = + (c: @switch) match { + case ' ' => true + case '\r' => true + case '\n' => true + case '\t' => true + case _ => false + } + + // Read, transforming escaped chars and stopping when we hit '"' + inline def readEscapedString(): Char = + read() match + case '\\' => + val c2 = read() + (c2: @switch) match + case '"' | '\\' | '/' => c2 + case 'b' => '\b' + case 'f' => '\f' + case 'n' => '\n' + case 'r' => '\r' + case 't' => '\t' + case 'u' => nextHex4() + case _ => throw JsonParseError(s"Invalid '\\${c2.toChar}' in string") + case '"' => END_OF_STRING + case BUFFER_EXCEEDED => throw new JsonParseError("Unexpected end of buffer") + case c => c + + inline def nextHex4(): Char = + var i: Int = 0 + var accum: Int = 0 + while i < 4 do + var c = read().toInt + if (c == BUFFER_EXCEEDED) + throw JsonParseError("unexpected EOB in string") + c = + if ('0' <= c && c <= '9') c - '0' + else if ('A' <= c && c <= 'F') c - 'A' + 10 + else if ('a' <= c && c <= 'f') c - 'a' + 10 + else + throw JsonParseError("Invalid hex character in string") + accum = accum * 16 + c + i += 1 + accum.toChar + + /* + Learnings: + + 1) Group implicit decoders into prioritized groups, favoring common/simple things like primitive types. Eg. esoteric things like date/time would be lowest. + 2) Spell out specific collection types BEFORE a general "built" colleciton type--saves cycles doing the conversion + 3) Use @switch macro to tell Scala to use a lookup table, not if-then-else in match-case statements + + Unknown--as of yet unknown how classes are handled.... + + TODO: + [ ] Learn how this StringMatrix works + [ ] Classes are *derived* decoders--magic! Clearly some mix of reading fields (set in StringMatrix), using FieldKeyDecoder and map decoder, then + constructed into a class.... + 1. In JsonDecoder, implement the [K,V] decoder. + 2. Manually construct a class decoder using StringMatrix and supply field-specific decoders for each field + 3. Try it! + */ \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json2/Numbers.scala b/src/main/scala/co.blocke.scalajack/json2/Numbers.scala new file mode 100644 index 00000000..40cc69f6 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json2/Numbers.scala @@ -0,0 +1,990 @@ +/* + * Copyright 2019-2022 John A. De Goes and the ZIO Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package co.blocke.scalajack +package json2 + +import java.io._ +import scala.util.control.NoStackTrace + +/** + * Total, fast, number parsing. + * + * The Java and Scala standard libraries throw exceptions when we attempt to + * parse an invalid number. Unfortunately, exceptions are very expensive, and + * untrusted data can be maliciously constructed to DOS a server. + * + * This suite of functions mitigates against such attacks by building up the + * numbers one character at a time, which has been shown through extensive + * benchmarking to be orders of magnitude faster than exception-throwing stdlib + * parsers, for valid and invalid inputs. This approach, proposed by alexknvl, + * was also benchmarked against regexp-based pre-validation. + * + * Note that although the behaviour is identical to the Java stdlib when given + * the canonical form of a primitive (i.e. the .toString) of a number there may + * be differences in behaviour for non-canonical forms. e.g. the Java stdlib + * may reject "1.0" when parsed as an `BigInteger` but we may parse it as a + * `1`, although "1.1" would be rejected. Parsing of `BigDecimal` preserves the + * trailing zeros on the right but not on the left, e.g. "000.00001000" will be + * "1.000e-5", which is useful in cases where the trailing zeros denote + * measurement accuracy. + * + * `BigInteger`, `BigDecimal`, `Float` and `Double` have a configurable bit + * limit on the size of the significand, to avoid OOM style attacks, which is + * 128 bits by default. + * + * Results are contained in a specialisation of Option that avoids boxing. + */ +// TODO hex radix +// TODO octal radix +object SafeNumbers { + import UnsafeNumbers.UnsafeNumber + + def byte(num: String): ByteOption = + try ByteSome(UnsafeNumbers.byte(num)) + catch { case UnsafeNumber => ByteNone } + + def short(num: String): ShortOption = + try ShortSome(UnsafeNumbers.short(num)) + catch { case UnsafeNumber => ShortNone } + + def int(num: String): IntOption = + try IntSome(UnsafeNumbers.int(num)) + catch { case UnsafeNumber => IntNone } + + def long(num: String): LongOption = + try LongSome(UnsafeNumbers.long(num)) + catch { case UnsafeNumber => LongNone } + + def bigInteger( + num: String, + max_bits: Int = 128 + ): Option[java.math.BigInteger] = + try Some(UnsafeNumbers.bigInteger(num, max_bits)) + catch { case UnsafeNumber => None } + + def float(num: String, max_bits: Int = 128): FloatOption = + try FloatSome(UnsafeNumbers.float(num, max_bits)) + catch { case UnsafeNumber => FloatNone } + + def double(num: String, max_bits: Int = 128): DoubleOption = + try DoubleSome(UnsafeNumbers.double(num, max_bits)) + catch { case UnsafeNumber => DoubleNone } + + def bigDecimal( + num: String, + max_bits: Int = 128 + ): Option[java.math.BigDecimal] = + try Some(UnsafeNumbers.bigDecimal(num, max_bits)) + catch { case UnsafeNumber => None } + + // Based on the amazing work of Raffaello Giulietti + // "The Schubfach way to render doubles": https://drive.google.com/file/d/1luHhyQF9zKlM8yJ1nebU0OgVYhfC6CBN/view + // Sources with the license are here: https://github.com/c4f7fcce9cb06515/Schubfach/blob/3c92d3c9b1fead540616c918cdfef432bca53dfa/todec/src/math/DoubleToDecimal.java + def toString(x: Double): String = { + val bits = java.lang.Double.doubleToLongBits(x) + val ieeeExponent = (bits >> 52).toInt & 0x7ff + val ieeeMantissa = bits & 0xfffffffffffffL + if (ieeeExponent == 2047) { + if (x != x) """"NaN"""" + else if (bits < 0) """"-Infinity"""" + else """"Infinity"""" + } else { + val s = new java.lang.StringBuilder(24) + if (bits < 0) s.append('-') + if (x == 0.0f) s.append('0').append('.').append('0') + else { + var e = ieeeExponent - 1075 + var m = ieeeMantissa | 0x10000000000000L + var dv = 0L + var exp = 0 + if (e == 0) dv = m + else if (e >= -52 && e < 0 && m << e == 0) dv = m >> -e + else { + var expShift, expCorr = 0 + var cblShift = 2 + if (ieeeExponent == 0) { + e = -1074 + m = ieeeMantissa + if (ieeeMantissa < 3) { + m *= 10 + expShift = 1 + } + } else if (ieeeMantissa == 0 && ieeeExponent > 1) { + expCorr = 131007 + cblShift = 1 + } + exp = e * 315653 - expCorr >> 20 + val i = exp + 324 << 1 + val g1 = gs(i) + val g0 = gs(i + 1) + val h = (-exp * 108853 >> 15) + e + 2 + val cb = m << 2 + val outm1 = (m.toInt & 0x1) - 1 + val vb = rop(g1, g0, cb << h) + val vbls = rop(g1, g0, cb - cblShift << h) + outm1 + val vbrd = outm1 - rop(g1, g0, cb + 2 << h) + val s = vb >> 2 + if ( + s < 100 || { + dv = s / 10 // FIXME: Use Math.multiplyHigh(s, 1844674407370955168L) instead after dropping JDK 8 support + val sp40 = dv * 40 + val upin = (vbls - sp40).toInt + (((sp40 + vbrd).toInt + 40) ^ upin) >= 0 || { + dv += ~upin >>> 31 + exp += 1 + false + } + } + ) { + val s4 = s << 2 + val uin = (vbls - s4).toInt + dv = (~ { + if ((((s4 + vbrd).toInt + 4) ^ uin) < 0) uin + else (vb.toInt & 0x3) + (s.toInt & 0x1) - 3 + } >>> 31) + s + exp -= expShift + } + } + val len = digitCount(dv) + exp += len - 1 + if (exp < -3 || exp >= 7) { + val dotOff = s.length + 1 + s.append(dv) + var i = s.length - 1 + while (i > dotOff && s.charAt(i) == '0') i -= 1 + s.setLength(i + 1) + s.insert(dotOff, '.').append('E').append(exp) + } else if (exp < 0) { + s.append('0').append('.') + while ({ + exp += 1 + exp != 0 + }) s.append('0') + s.append(dv) + var i = s.length - 1 + while (s.charAt(i) == '0') i -= 1 + s.setLength(i + 1) + s + } else if (exp + 1 < len) { + val dotOff = s.length + exp + 1 + s.append(dv) + var i = s.length - 1 + while (s.charAt(i) == '0') i -= 1 + s.setLength(i + 1) + s.insert(dotOff, '.') + } else s.append(dv).append('.').append('0') + } + }.toString + } + + def toString(x: Float): String = { + val bits = java.lang.Float.floatToIntBits(x) + val ieeeExponent = (bits >> 23) & 0xff + val ieeeMantissa = bits & 0x7fffff + if (ieeeExponent == 255) { + if (x != x) """"NaN"""" + else if (bits < 0) """"-Infinity"""" + else """"Infinity"""" + } else { + val s = new java.lang.StringBuilder(16) + if (bits < 0) s.append('-') + if (x == 0.0f) s.append('0').append('.').append('0') + else { + var e = ieeeExponent - 150 + var m = ieeeMantissa | 0x800000 + var dv, exp = 0 + if (e == 0) dv = m + else if (e >= -23 && e < 0 && m << e == 0) dv = m >> -e + else { + var expShift, expCorr = 0 + var cblShift = 2 + if (ieeeExponent == 0) { + e = -149 + m = ieeeMantissa + if (ieeeMantissa < 8) { + m *= 10 + expShift = 1 + } + } else if (ieeeMantissa == 0 && ieeeExponent > 1) { + expCorr = 131007 + cblShift = 1 + } + exp = e * 315653 - expCorr >> 20 + val g1 = gs(exp + 324 << 1) + 1 + val h = (-exp * 108853 >> 15) + e + 1 + val cb = m << 2 + val outm1 = (m & 0x1) - 1 + val vb = rop(g1, cb << h) + val vbls = rop(g1, cb - cblShift << h) + outm1 + val vbrd = outm1 - rop(g1, cb + 2 << h) + val s = vb >> 2 + if ( + s < 100 || { + dv = (s * 3435973837L >>> 35).toInt // divide a positive int by 10 + val sp40 = dv * 40 + val upin = vbls - sp40 + ((sp40 + vbrd + 40) ^ upin) >= 0 || { + dv += ~upin >>> 31 + exp += 1 + false + } + } + ) { + val s4 = s << 2 + val uin = vbls - s4 + dv = (~ { + if (((s4 + vbrd + 4) ^ uin) < 0) uin + else (vb & 0x3) + (s & 0x1) - 3 + } >>> 31) + s + exp -= expShift + } + } + val len = digitCount(dv.toLong) + exp += len - 1 + if (exp < -3 || exp >= 7) { + val dotOff = s.length + 1 + s.append(dv) + var i = s.length - 1 + while (i > dotOff && s.charAt(i) == '0') i -= 1 + s.setLength(i + 1) + s.insert(dotOff, '.').append('E').append(exp) + } else if (exp < 0) { + s.append('0').append('.') + while ({ + exp += 1 + exp != 0 + }) s.append('0') + s.append(dv) + var i = s.length - 1 + while (s.charAt(i) == '0') i -= 1 + s.setLength(i + 1) + s + } else if (exp + 1 < len) { + val dotOff = s.length + exp + 1 + s.append(dv) + var i = s.length - 1 + while (s.charAt(i) == '0') i -= 1 + s.setLength(i + 1) + s.insert(dotOff, '.') + } else s.append(dv).append('.').append('0') + } + }.toString + } + + private[this] def rop(g1: Long, g0: Long, cp: Long): Long = { + val x1 = multiplyHigh(g0, cp) // FIXME: Use Math.multiplyHigh after dropping JDK 8 support + val z = (g1 * cp >>> 1) + x1 + val y1 = multiplyHigh(g1, cp) // FIXME: Use Math.multiplyHigh after dropping JDK 8 support + (z >>> 63) + y1 | -(z & 0x7fffffffffffffffL) >>> 63 + } + + private[this] def rop(g: Long, cp: Int): Int = { + val x1 = + ((g & 0xffffffffL) * cp >>> 32) + (g >>> 32) * cp // FIXME: Use Math.multiplyHigh after dropping JDK 8 support + (x1 >>> 31).toInt | -x1.toInt >>> 31 + } + + private[this] def multiplyHigh(x: Long, y: Long): Long = { + val x2 = x & 0xffffffffL + val y2 = y & 0xffffffffL + val b = x2 * y2 + val x1 = x >>> 32 + val y1 = y >>> 32 + val a = x1 * y1 + (((b >>> 32) + (x1 + x2) * (y1 + y2) - b - a) >>> 32) + a + } + + // Adoption of a nice trick form Daniel Lemire's blog that works for numbers up to 10^18: + // https://lemire.me/blog/2021/06/03/computing-the-number-of-digits-of-an-integer-even-faster/ + private[this] def digitCount(x: Long): Int = (offsets(java.lang.Long.numberOfLeadingZeros(x)) + x >> 58).toInt + + private final val offsets = Array( + 5088146770730811392L, 5088146770730811392L, 5088146770730811392L, 5088146770730811392L, 5088146770730811392L, + 5088146770730811392L, 5088146770730811392L, 5088146770730811392L, 4889916394579099648L, 4889916394579099648L, + 4889916394579099648L, 4610686018427387904L, 4610686018427387904L, 4610686018427387904L, 4610686018427387904L, + 4323355642275676160L, 4323355642275676160L, 4323355642275676160L, 4035215266123964416L, 4035215266123964416L, + 4035215266123964416L, 3746993889972252672L, 3746993889972252672L, 3746993889972252672L, 3746993889972252672L, + 3458764413820540928L, 3458764413820540928L, 3458764413820540928L, 3170534127668829184L, 3170534127668829184L, + 3170534127668829184L, 2882303760517117440L, 2882303760517117440L, 2882303760517117440L, 2882303760517117440L, + 2594073385265405696L, 2594073385265405696L, 2594073385265405696L, 2305843009203693952L, 2305843009203693952L, + 2305843009203693952L, 2017612633060982208L, 2017612633060982208L, 2017612633060982208L, 2017612633060982208L, + 1729382256910170464L, 1729382256910170464L, 1729382256910170464L, 1441151880758548720L, 1441151880758548720L, + 1441151880758548720L, 1152921504606845976L, 1152921504606845976L, 1152921504606845976L, 1152921504606845976L, + 864691128455135132L, 864691128455135132L, 864691128455135132L, 576460752303423478L, 576460752303423478L, + 576460752303423478L, 576460752303423478L, 576460752303423478L, 576460752303423478L, 576460752303423478L + ) + + private[this] val gs: Array[Long] = Array( + 5696189077778435540L, 6557778377634271669L, 9113902524445496865L, 1269073367360058862L, 7291122019556397492L, + 1015258693888047090L, 5832897615645117993L, 6346230177223303157L, 4666318092516094394L, 8766332956520552849L, + 7466108948025751031L, 8492109508320019073L, 5972887158420600825L, 4949013199285060097L, 4778309726736480660L, + 3959210559428048077L, 7645295562778369056L, 6334736895084876923L, 6116236450222695245L, 3223115108696946377L, + 4892989160178156196L, 2578492086957557102L, 7828782656285049914L, 436238524390181040L, 6263026125028039931L, + 2193665226883099993L, 5010420900022431944L, 9133629810990300641L, 8016673440035891111L, 9079784475471615541L, + 6413338752028712889L, 5419153173006337271L, 5130671001622970311L, 6179996945776024979L, 8209073602596752498L, + 6198646298499729642L, 6567258882077401998L, 8648265853541694037L, 5253807105661921599L, 1384589460720489745L, + 8406091369059074558L, 5904691951894693915L, 6724873095247259646L, 8413102376257665455L, 5379898476197807717L, + 4885807493635177203L, 8607837561916492348L, 438594360332462878L, 6886270049533193878L, 4040224303007880625L, + 5509016039626555102L, 6921528257148214824L, 8814425663402488164L, 3695747581953323071L, 7051540530721990531L, + 4801272472933613619L, 5641232424577592425L, 1996343570975935733L, 9025971879324147880L, 3194149713561497173L, + 7220777503459318304L, 2555319770849197738L, 5776622002767454643L, 3888930224050313352L, 4621297602213963714L, + 6800492993982161005L, 7394076163542341943L, 5346765568258592123L, 5915260930833873554L, 7966761269348784022L, + 4732208744667098843L, 8218083422849982379L, 7571533991467358150L, 2080887032334240837L, 6057227193173886520L, + 1664709625867392670L, 4845781754539109216L, 1331767700693914136L, 7753250807262574745L, 7664851543223128102L, + 6202600645810059796L, 6131881234578502482L, 4962080516648047837L, 3060830580291846824L, 7939328826636876539L, + 6742003335837910079L, 6351463061309501231L, 7238277076041283225L, 5081170449047600985L, 3945947253462071419L, + 8129872718476161576L, 6313515605539314269L, 6503898174780929261L, 3206138077060496254L, 5203118539824743409L, + 720236054277441842L, 8324989663719589454L, 4841726501585817270L, 6659991730975671563L, 5718055608639608977L, + 5327993384780537250L, 8263793301653597505L, 8524789415648859601L, 3998697245790980200L, 6819831532519087681L, + 1354283389261828999L, 5455865226015270144L, 8462124340893283845L, 8729384361624432231L, 8005375723316388668L, + 6983507489299545785L, 4559626171282155773L, 5586805991439636628L, 3647700937025724618L, 8938889586303418605L, + 3991647091870204227L, 7151111669042734884L, 3193317673496163382L, 5720889335234187907L, 4399328546167885867L, + 9153422936374700651L, 8883600081239572549L, 7322738349099760521L, 5262205657620702877L, 5858190679279808417L, + 2365090118725607140L, 4686552543423846733L, 7426095317093351197L, 7498484069478154774L, 813706063123630946L, + 5998787255582523819L, 2495639257869859918L, 4799029804466019055L, 3841185813666843096L, 7678447687145630488L, + 6145897301866948954L, 6142758149716504390L, 8606066656235469486L, 4914206519773203512L, 6884853324988375589L, + 7862730431637125620L, 3637067690497580296L, 6290184345309700496L, 2909654152398064237L, 5032147476247760397L, + 483048914547496228L, 8051435961996416635L, 2617552670646949126L, 6441148769597133308L, 2094042136517559301L, + 5152919015677706646L, 5364582523955957764L, 8244670425084330634L, 4893983223587622099L, 6595736340067464507L, + 5759860986241052841L, 5276589072053971606L, 918539974250931950L, 8442542515286354569L, 7003687180914356604L, + 6754034012229083655L, 7447624152102440445L, 5403227209783266924L, 5958099321681952356L, 8645163535653227079L, + 3998935692578258285L, 6916130828522581663L, 5043822961433561789L, 5532904662818065330L, 7724407183888759755L, + 8852647460508904529L, 3135679457367239799L, 7082117968407123623L, 4353217973264747001L, 5665694374725698898L, + 7171923193353707924L, 9065110999561118238L, 407030665140201709L, 7252088799648894590L, 4014973346854071690L, + 5801671039719115672L, 3211978677483257352L, 4641336831775292537L, 8103606164099471367L, 7426138930840468060L, + 5587072233075333540L, 5940911144672374448L, 4469657786460266832L, 4752728915737899558L, 7265075043910123789L, + 7604366265180639294L, 556073626030467093L, 6083493012144511435L, 2289533308195328836L, 4866794409715609148L, + 1831626646556263069L, 7786871055544974637L, 1085928227119065748L, 6229496844435979709L, 6402765803808118083L, + 4983597475548783767L, 6966887050417449628L, 7973755960878054028L, 3768321651184098759L, 6379004768702443222L, + 6704006135689189330L, 5103203814961954578L, 1673856093809441141L, 8165126103939127325L, 833495342724150664L, + 6532100883151301860L, 666796274179320531L, 5225680706521041488L, 533437019343456425L, 8361089130433666380L, + 8232196860433350926L, 6688871304346933104L, 6585757488346680741L, 5351097043477546483L, 7113280398048299755L, + 8561755269564074374L, 313202192651548637L, 6849404215651259499L, 2095236161492194072L, 5479523372521007599L, + 3520863336564710419L, 8767237396033612159L, 99358116390671185L, 7013789916826889727L, 1924160900483492110L, + 5611031933461511781L, 7073351942499659173L, 8977651093538418850L, 7628014293257544353L, 7182120874830735080L, + 6102411434606035483L, 5745696699864588064L, 4881929147684828386L, 9193114719783340903L, 2277063414182859933L, + 7354491775826672722L, 5510999546088198270L, 5883593420661338178L, 719450822128648293L, 4706874736529070542L, + 4264909472444828957L, 7530999578446512867L, 8668529563282681493L, 6024799662757210294L, 3245474835884234871L, + 4819839730205768235L, 4441054276078343059L, 7711743568329229176L, 7105686841725348894L, 6169394854663383341L, + 3839875066009323953L, 4935515883730706673L, 1227225645436504001L, 7896825413969130677L, 118886625327451240L, + 6317460331175304541L, 5629132522374826477L, 5053968264940243633L, 2658631610528906020L, 8086349223904389813L, + 2409136169475294470L, 6469079379123511850L, 5616657750322145900L, 5175263503298809480L, 4493326200257716720L, + 8280421605278095168L, 7189321920412346751L, 6624337284222476135L, 217434314217011916L, 5299469827377980908L, + 173947451373609533L, 8479151723804769452L, 7657013551681595899L, 6783321379043815562L, 2436262026603366396L, + 5426657103235052449L, 7483032843395558602L, 8682651365176083919L, 6438829327320028278L, 6946121092140867135L, + 6995737869226977784L, 5556896873712693708L, 5596590295381582227L, 8891034997940309933L, 7109870065239576402L, + 7112827998352247947L, 153872830078795637L, 5690262398681798357L, 5657121486175901994L, 9104419837890877372L, + 1672696748397622544L, 7283535870312701897L, 6872180620830963520L, 5826828696250161518L, 1808395681922860493L, + 4661462957000129214L, 5136065360280198718L, 7458340731200206743L, 2683681354335452463L, 5966672584960165394L, + 5836293898210272294L, 4773338067968132315L, 6513709525939172997L, 7637340908749011705L, 1198563204647900987L, + 6109872726999209364L, 958850563718320789L, 4887898181599367491L, 2611754858345611793L, 7820637090558987986L, + 489458958611068546L, 6256509672447190388L, 7770264796372675483L, 5005207737957752311L, 682188614985274902L, + 8008332380732403697L, 6625525006089305327L, 6406665904585922958L, 1611071190129533939L, 5125332723668738366L, + 4978205766845537474L, 8200532357869981386L, 4275780412210949635L, 6560425886295985109L, 1575949922397804547L, + 5248340709036788087L, 3105434345289198799L, 8397345134458860939L, 6813369359833673240L, 6717876107567088751L, + 7295369895237893754L, 5374300886053671001L, 3991621508819359841L, 8598881417685873602L, 2697245599369065423L, + 6879105134148698881L, 7691819701608117823L, 5503284107318959105L, 4308781353915539097L, 8805254571710334568L, + 6894050166264862555L, 7044203657368267654L, 9204588947753800367L, 5635362925894614123L, 9208345565573995455L, + 9016580681431382598L, 3665306460692661759L, 7213264545145106078L, 6621593983296039730L, 5770611636116084862L, + 8986624001378742108L, 4616489308892867890L, 3499950386361083363L, 7386382894228588624L, 5599920618177733380L, + 5909106315382870899L, 6324610901913141866L, 4727285052306296719L, 6904363128901468655L, 7563656083690074751L, + 5512957784129484362L, 6050924866952059801L, 2565691819932632328L, 4840739893561647841L, 207879048575150701L, + 7745183829698636545L, 5866629699833106606L, 6196147063758909236L, 4693303759866485285L, 4956917651007127389L, + 1909968600522233067L, 7931068241611403822L, 6745298575577483229L, 6344854593289123058L, 1706890045720076260L, + 5075883674631298446L, 5054860851317971332L, 8121413879410077514L, 4398428547366843807L, 6497131103528062011L, + 5363417245264430207L, 5197704882822449609L, 2446059388840589004L, 8316327812515919374L, 7603043836886852730L, + 6653062250012735499L, 7927109476880437346L, 5322449800010188399L, 8186361988875305038L, 8515919680016301439L, + 7564155960087622576L, 6812735744013041151L, 7895999175441053223L, 5450188595210432921L, 4472124932981887417L, + 8720301752336692674L, 3466051078029109543L, 6976241401869354139L, 4617515269794242796L, 5580993121495483311L, + 5538686623206349399L, 8929588994392773298L, 5172549782388248714L, 7143671195514218638L, 7827388640652509295L, + 5714936956411374911L, 727887690409141951L, 9143899130258199857L, 6698643526767492606L, 7315119304206559886L, + 1669566006672083762L, 5852095443365247908L, 8714350434821487656L, 4681676354692198327L, 1437457125744324640L, + 7490682167507517323L, 4144605808561874585L, 5992545734006013858L, 7005033461591409992L, 4794036587204811087L, + 70003547160262509L, 7670458539527697739L, 1956680082827375175L, 6136366831622158191L, 3410018473632855302L, + 4909093465297726553L, 883340371535329080L, 7854549544476362484L, 8792042223940347174L, 6283639635581089987L, + 8878308186523232901L, 5026911708464871990L, 3413297734476675998L, 8043058733543795184L, 5461276375162681596L, + 6434446986835036147L, 6213695507501100438L, 5147557589468028918L, 1281607591258970028L, 8236092143148846269L, + 205897738643396882L, 6588873714519077015L, 2009392598285672668L, 5271098971615261612L, 1607514078628538134L, + 8433758354584418579L, 4416696933176616176L, 6747006683667534863L, 5378031953912248102L, 5397605346934027890L, + 7991774377871708805L, 8636168555094444625L, 3563466967739958280L, 6908934844075555700L, 2850773574191966624L, + 5527147875260444560L, 2280618859353573299L, 8843436600416711296L, 3648990174965717279L, 7074749280333369037L, + 1074517732601618662L, 5659799424266695229L, 6393637408194160414L, 9055679078826712367L, 4695796630997791177L, + 7244543263061369894L, 67288490056322619L, 5795634610449095915L, 1898505199416013257L, 4636507688359276732L, + 1518804159532810606L, 7418412301374842771L, 4274761062623452130L, 5934729841099874217L, 1575134442727806543L, + 4747783872879899373L, 6794130776295110719L, 7596454196607838997L, 9025934834701221989L, 6077163357286271198L, + 3531399053019067268L, 4861730685829016958L, 6514468057157164137L, 7778769097326427133L, 8578474484080507458L, + 6223015277861141707L, 1328756365151540482L, 4978412222288913365L, 6597028314234097870L, 7965459555662261385L, + 1331873265919780784L, 6372367644529809108L, 1065498612735824627L, 5097894115623847286L, 4541747704930570025L, + 8156630584998155658L, 3577447513147001717L, 6525304467998524526L, 6551306825259511697L, 5220243574398819621L, + 3396371052836654196L, 8352389719038111394L, 1744844869796736390L, 6681911775230489115L, 3240550303208344274L, + 5345529420184391292L, 2592440242566675419L, 8552847072295026067L, 5992578795477635832L, 6842277657836020854L, + 1104714221640198342L, 5473822126268816683L, 2728445784683113836L, 8758115402030106693L, 2520838848122026975L, + 7006492321624085354L, 5706019893239531903L, 5605193857299268283L, 6409490321962580684L, 8968310171678829253L, + 8410510107769173933L, 7174648137343063403L, 1194384864102473662L, 5739718509874450722L, 4644856706023889253L, + 9183549615799121156L, 53073100154402158L, 7346839692639296924L, 7421156109607342373L, 5877471754111437539L, + 7781599295056829060L, 4701977403289150031L, 8069953843416418410L, 7523163845262640050L, 9222577334724359132L, + 6018531076210112040L, 7378061867779487306L, 4814824860968089632L, 5902449494223589845L, 7703719777548943412L, + 2065221561273923105L, 6162975822039154729L, 7186200471132003969L, 4930380657631323783L, 7593634784276558337L, + 7888609052210118054L, 1081769210616762369L, 6310887241768094443L, 2710089775864365057L, 5048709793414475554L, + 5857420635433402369L, 8077935669463160887L, 3837849794580578305L, 6462348535570528709L, 8604303057777328129L, + 5169878828456422967L, 8728116853592817665L, 8271806125530276748L, 6586289336264687617L, 6617444900424221398L, + 8958380283753660417L, 5293955920339377119L, 1632681004890062849L, 8470329472543003390L, 6301638422566010881L, + 6776263578034402712L, 5041310738052808705L, 5421010862427522170L, 343699775700336641L, 8673617379884035472L, + 549919641120538625L, 6938893903907228377L, 5973958935009296385L, 5551115123125782702L, 1089818333265526785L, + 8881784197001252323L, 3588383740595798017L, 7105427357601001858L, 6560055807218548737L, 5684341886080801486L, + 8937393460516749313L, 9094947017729282379L, 1387108685230112769L, 7275957614183425903L, 2954361355555045377L, + 5820766091346740722L, 6052837899185946625L, 4656612873077392578L, 1152921504606846977L, 7450580596923828125L, 1L, + 5960464477539062500L, 1L, 4768371582031250000L, 1L, 7629394531250000000L, 1L, 6103515625000000000L, 1L, + 4882812500000000000L, 1L, 7812500000000000000L, 1L, 6250000000000000000L, 1L, 5000000000000000000L, 1L, + 8000000000000000000L, 1L, 6400000000000000000L, 1L, 5120000000000000000L, 1L, 8192000000000000000L, 1L, + 6553600000000000000L, 1L, 5242880000000000000L, 1L, 8388608000000000000L, 1L, 6710886400000000000L, 1L, + 5368709120000000000L, 1L, 8589934592000000000L, 1L, 6871947673600000000L, 1L, 5497558138880000000L, 1L, + 8796093022208000000L, 1L, 7036874417766400000L, 1L, 5629499534213120000L, 1L, 9007199254740992000L, 1L, + 7205759403792793600L, 1L, 5764607523034234880L, 1L, 4611686018427387904L, 1L, 7378697629483820646L, + 3689348814741910324L, 5902958103587056517L, 1106804644422573097L, 4722366482869645213L, 6419466937650923963L, + 7555786372591432341L, 8426472692870523179L, 6044629098073145873L, 4896503746925463381L, 4835703278458516698L, + 7606551812282281028L, 7737125245533626718L, 1102436455425918676L, 6189700196426901374L, 4571297979082645264L, + 4951760157141521099L, 5501712790637071373L, 7922816251426433759L, 3268717242906448711L, 6338253001141147007L, + 4459648201696114131L, 5070602400912917605L, 9101741783469756789L, 8112963841460668169L, 5339414816696835055L, + 6490371073168534535L, 6116206260728423206L, 5192296858534827628L, 4892965008582738565L, 8307674973655724205L, + 5984069606361426541L, 6646139978924579364L, 4787255685089141233L, 5316911983139663491L, 5674478955442268148L, + 8507059173023461586L, 5389817513965718714L, 6805647338418769269L, 2467179603801619810L, 5444517870735015415L, + 3818418090412251009L, 8711228593176024664L, 6109468944659601615L, 6968982874540819731L, 6732249563098636453L, + 5575186299632655785L, 3541125243107954001L, 8920298079412249256L, 5665800388972726402L, 7136238463529799405L, + 2687965903807225960L, 5708990770823839524L, 2150372723045780768L, 9134385233318143238L, 7129945171615159552L, + 7307508186654514591L, 169932915179262157L, 5846006549323611672L, 7514643961627230372L, 4676805239458889338L, + 2322366354559873974L, 7482888383134222941L, 1871111759924843197L, 5986310706507378352L, 8875587037423695204L, + 4789048565205902682L, 3411120815197045840L, 7662477704329444291L, 7302467711686228506L, 6129982163463555433L, + 3997299761978027643L, 4903985730770844346L, 6887188624324332438L, 7846377169233350954L, 7330152984177021577L, + 6277101735386680763L, 7708796794712572423L, 5021681388309344611L, 633014213657192454L, 8034690221294951377L, + 6546845963964373411L, 6427752177035961102L, 1548127956429588405L, 5142201741628768881L, 6772525587256536209L, + 8227522786606030210L, 7146692124868547611L, 6582018229284824168L, 5717353699894838089L, 5265614583427859334L, + 8263231774657780795L, 8424983333484574935L, 7687147617339583786L, 6739986666787659948L, 6149718093871667029L, + 5391989333430127958L, 8609123289839243947L, 8627182933488204734L, 2706550819517059345L, 6901746346790563787L, + 4009915062984602637L, 5521397077432451029L, 8741955272500547595L, 8834235323891921647L, 8453105213888010667L, + 7067388259113537318L, 3073135356368498210L, 5653910607290829854L, 6147857099836708891L, 9046256971665327767L, + 4302548137625868741L, 7237005577332262213L, 8976061732213560478L, 5789604461865809771L, 1646826163657982898L, + 4631683569492647816L, 8696158560410206965L, 7410693711188236507L, 1001132845059645012L, 5928554968950589205L, + 6334929498160581494L, 4742843975160471364L, 5067943598528465196L, 7588550360256754183L, 2574686535532678828L, + 6070840288205403346L, 5749098043168053386L, 4856672230564322677L, 2754604027163487547L, 7770675568902916283L, + 6252040850832535236L, 6216540455122333026L, 8690981495407938512L, 4973232364097866421L, 5108110788955395648L, + 7957171782556586274L, 4483628447586722714L, 6365737426045269019L, 5431577165440333333L, 5092589940836215215L, + 6189936139723221828L, 8148143905337944345L, 680525786702379117L, 6518515124270355476L, 544420629361903293L, + 5214812099416284380L, 7814234132973343281L, 8343699359066055009L, 3279402575902573442L, 6674959487252844007L, + 4468196468093013915L, 5339967589802275205L, 9108580396587276617L, 8543948143683640329L, 5350356597684866779L, + 6835158514946912263L, 6124959685518848585L, 5468126811957529810L, 8589316563156989191L, 8749002899132047697L, + 4519534464196406897L, 6999202319305638157L, 9149650793469991003L, 5599361855444510526L, 3630371820034082479L, + 8958978968711216842L, 2119246097312621643L, 7167183174968973473L, 7229420099962962799L, 5733746539975178779L, + 249512857857504755L, 9173994463960286046L, 4088569387313917931L, 7339195571168228837L, 1426181102480179183L, + 5871356456934583069L, 6674968104097008831L, 4697085165547666455L, 7184648890648562227L, 7515336264876266329L, + 2272066188182923754L, 6012269011901013063L, 3662327357917294165L, 4809815209520810450L, 6619210701075745655L, + 7695704335233296721L, 1367365084866417240L, 6156563468186637376L, 8472589697376954439L, 4925250774549309901L, + 4933397350530608390L, 7880401239278895842L, 4204086946107063100L, 6304320991423116673L, 8897292778998515965L, + 5043456793138493339L, 1583811001085947287L, 8069530869021589342L, 6223446416479425982L, 6455624695217271474L, + 1289408318441630463L, 5164499756173817179L, 2876201062124259532L, 8263199609878107486L, 8291270514140725574L, + 6610559687902485989L, 4788342003941625298L, 5288447750321988791L, 5675348010524255400L, 8461516400515182066L, + 5391208002096898316L, 6769213120412145653L, 2468291994306563491L, 5415370496329716522L, 5663982410187161116L, + 8664592794127546436L, 1683674226815637140L, 6931674235302037148L, 8725637010936330358L, 5545339388241629719L, + 1446486386636198802L, 8872543021186607550L, 6003727033359828406L, 7098034416949286040L, 4802981626687862725L, + 5678427533559428832L, 3842385301350290180L, 9085484053695086131L, 7992490889531419449L, 7268387242956068905L, + 4549318304254180398L, 5814709794364855124L, 3639454643403344318L, 4651767835491884099L, 4756238122093630616L, + 7442828536787014559L, 2075957773236943501L, 5954262829429611647L, 3505440625960509963L, 4763410263543689317L, + 8338375722881273455L, 7621456421669902908L, 5962703527126216881L, 6097165137335922326L, 8459511636442883828L, + 4877732109868737861L, 4922934901783351901L, 7804371375789980578L, 4187347028111452718L, 6243497100631984462L, + 7039226437231072498L, 4994797680505587570L, 1942032335042947675L, 7991676288808940112L, 3107251736068716280L, + 6393341031047152089L, 8019824610967838509L, 5114672824837721671L, 8260534096145225969L, 8183476519740354675L, + 304133702235675419L, 6546781215792283740L, 243306961788540335L, 5237424972633826992L, 194645569430832268L, + 8379879956214123187L, 2156107318460286790L, 6703903964971298549L, 7258909076881094917L, 5363123171977038839L, + 7651801668875831096L, 8580997075163262143L, 6708859448088464268L, 6864797660130609714L, 9056436373212681737L, + 5491838128104487771L, 9089823505941100552L, 8786941004967180435L, 1630996757909074751L, 7029552803973744348L, + 1304797406327259801L, 5623642243178995478L, 4733186739803718164L, 8997827589086392765L, 5728424376314993901L, + 7198262071269114212L, 4582739501051995121L, 5758609657015291369L, 9200214822954461581L, 9213775451224466191L, + 9186320494614273045L, 7371020360979572953L, 5504381988320463275L, 5896816288783658362L, 8092854405398280943L, + 4717453031026926690L, 2784934709576714431L, 7547924849643082704L, 4455895535322743090L, 6038339879714466163L, + 5409390835629149634L, 4830671903771572930L, 8016861483245230030L, 7729075046034516689L, 3603606336337592240L, + 6183260036827613351L, 4727559476441028954L, 4946608029462090681L, 1937373173781868001L, 7914572847139345089L, + 8633820300163854287L, 6331658277711476071L, 8751730647502038591L, 5065326622169180857L, 5156710110630675711L, + 8104522595470689372L, 872038547525260492L, 6483618076376551497L, 6231654060133073878L, 5186894461101241198L, + 1295974433364548779L, 8299031137761985917L, 228884686012322885L, 6639224910209588733L, 5717130970922723793L, + 5311379928167670986L, 8263053591480089358L, 8498207885068273579L, 308164894771456841L, 6798566308054618863L, + 2091206323188120634L, 5438853046443695090L, 5362313873292406831L, 8702164874309912144L, 8579702197267850929L, + 6961731899447929715L, 8708436165185235905L, 5569385519558343772L, 6966748932148188724L, 8911016831293350036L, + 3768100661953281312L, 7128813465034680029L, 1169806122191669888L, 5703050772027744023L, 2780519305124291072L, + 9124881235244390437L, 2604156480827910553L, 7299904988195512349L, 7617348406775193928L, 5839923990556409879L, + 7938553132791110304L, 4671939192445127903L, 8195516913603843405L, 7475102707912204646L, 2044780617540418478L, + 5980082166329763716L, 9014522123516155429L, 4784065733063810973L, 5366943291441969181L, 7654505172902097557L, + 6742434858936195528L, 6123604138321678046L, 1704599072407046100L, 4898883310657342436L, 8742376887409457526L, + 7838213297051747899L, 1075082168258445910L, 6270570637641398319L, 2704740141977711890L, 5016456510113118655L, + 4008466520953124674L, 8026330416180989848L, 6413546433524999478L, 6421064332944791878L, 8820185961561909905L, + 5136851466355833503L, 1522125547136662440L, 8218962346169333605L, 590726468047704741L, 6575169876935466884L, + 472581174438163793L, 5260135901548373507L, 2222739346921486196L, 8416217442477397611L, 5401057362445333075L, + 6732973953981918089L, 2476171482585311299L, 5386379163185534471L, 3825611593439204201L, 8618206661096855154L, + 2431629734760816398L, 6894565328877484123L, 3789978195179608280L, 5515652263101987298L, 6721331370885596947L, + 8825043620963179677L, 8909455786045999954L, 7060034896770543742L, 3438215814094889640L, 5648027917416434993L, + 8284595873388777197L, 9036844667866295990L, 2187306953196312545L, 7229475734293036792L, 1749845562557050036L, + 5783580587434429433L, 6933899672158505514L, 4626864469947543547L, 13096515613938926L, 7402983151916069675L, + 1865628832353257443L, 5922386521532855740L, 1492503065882605955L, 4737909217226284592L, 1194002452706084764L, + 7580654747562055347L, 3755078331700690783L, 6064523798049644277L, 8538085887473418112L, 4851619038439715422L, + 3141119895236824166L, 7762590461503544675L, 6870466239749873827L, 6210072369202835740L, 5496372991799899062L, + 4968057895362268592L, 4397098393439919250L, 7948892632579629747L, 8880031836874825961L, 6359114106063703798L, + 3414676654757950445L, 5087291284850963038L, 6421090138548270680L, 8139666055761540861L, 8429069814306277926L, + 6511732844609232689L, 4898581444074067179L, 5209386275687386151L, 5763539562630208905L, 8335018041099817842L, + 5532314485466423924L, 6668014432879854274L, 736502773631228816L, 5334411546303883419L, 2433876626275938215L, + 8535058474086213470L, 7583551416783411467L, 6828046779268970776L, 6066841133426729173L, 5462437423415176621L, + 3008798499370428177L, 8739899877464282594L, 1124728784250774760L, 6991919901971426075L, 2744457434771574970L, + 5593535921577140860L, 2195565947817259976L, 8949657474523425376L, 3512905516507615961L, 7159725979618740301L, + 965650005835137607L, 5727780783694992240L, 8151217634151930732L, 9164449253911987585L, 3818576177788313364L, + 7331559403129590068L, 3054860942230650691L, 5865247522503672054L, 6133237568526430876L, 4692198018002937643L, + 6751264462192099863L, 7507516828804700229L, 8957348732136404618L, 6006013463043760183L, 9010553393080078856L, + 4804810770435008147L, 1674419492351197600L, 7687697232696013035L, 4523745595132871322L, 6150157786156810428L, + 3618996476106297057L, 4920126228925448342L, 6584545995626947969L, 7872201966280717348L, 3156575963519296104L, + 6297761573024573878L, 6214609585557347207L, 5038209258419659102L, 8661036483187788089L, 8061134813471454564L, + 6478960743616640295L, 6448907850777163651L, 7027843002264267398L, 5159126280621730921L, 3777599994440458757L, + 8254602048994769474L, 2354811176362823687L, 6603681639195815579L, 3728523348461214111L, 5282945311356652463L, + 4827493086139926451L, 8452712498170643941L, 5879314530452927160L, 6762169998536515153L, 2858777216991386566L, + 5409735998829212122L, 5976370588335019576L, 8655577598126739396L, 2183495311852210675L, 6924462078501391516L, + 9125493878965589187L, 5539569662801113213L, 5455720695801516188L, 8863311460481781141L, 6884478705911470739L, + 7090649168385424913L, 3662908557358221429L, 5672519334708339930L, 6619675660628487467L, 9076030935533343889L, + 1368109020150804139L, 7260824748426675111L, 2939161623491598473L, 5808659798741340089L, 506654891422323617L, + 4646927838993072071L, 2249998320508814055L, 7435084542388915313L, 9134020534926967972L, 5948067633911132251L, + 1773193205828708893L, 4758454107128905800L, 8797252194146787761L, 7613526571406249281L, 4852231473780084609L, + 6090821257124999425L, 2037110771653112526L, 4872657005699999540L, 1629688617322490021L, 7796251209119999264L, + 2607501787715984033L, 6237000967295999411L, 3930675837543742388L, 4989600773836799529L, 1299866262664038749L, + 7983361238138879246L, 5769134835004372321L, 6386688990511103397L, 2770633460632542696L, 5109351192408882717L, + 7750529990618899641L, 8174961907854212348L, 5022150355506418780L, 6539969526283369878L, 7707069099147045347L, + 5231975621026695903L, 631632057204770793L, 8371160993642713444L, 8389308921011453915L, 6696928794914170755L, + 8556121544180118293L, 5357543035931336604L, 6844897235344094635L, 8572068857490138567L, 5417812354437685931L, + 6857655085992110854L, 644901068808238421L, 5486124068793688683L, 2360595262417545899L, 8777798510069901893L, + 1932278012497118276L, 7022238808055921514L, 5235171224739604944L, 5617791046444737211L, 6032811387162639117L, + 8988465674311579538L, 5963149404718312264L, 7190772539449263630L, 8459868338516560134L, 5752618031559410904L, + 6767894670813248108L, 9204188850495057447L, 5294608251188331487L + ) +} + +// specialised Options to avoid boxing. Prefer .isEmpty guarded access to .value +// for higher performance: pattern matching is slightly slower. + +sealed abstract class ByteOption { + def isEmpty: Boolean + def value: Byte +} +case object ByteNone extends ByteOption { + def isEmpty = true + def value: Byte = throw new java.util.NoSuchElementException +} +case class ByteSome(value: Byte) extends ByteOption { + def isEmpty = false +} + +sealed abstract class ShortOption { + def isEmpty: Boolean + def value: Short +} +case object ShortNone extends ShortOption { + def isEmpty = true + def value: Short = throw new java.util.NoSuchElementException +} +case class ShortSome(value: Short) extends ShortOption { + def isEmpty = false +} + +sealed abstract class IntOption { + def isEmpty: Boolean + def value: Int +} +case object IntNone extends IntOption { + def isEmpty = true + def value: Int = throw new java.util.NoSuchElementException +} +case class IntSome(value: Int) extends IntOption { + def isEmpty = false +} + +sealed abstract class LongOption { + def isEmpty: Boolean + def value: Long +} +case object LongNone extends LongOption { + def isEmpty = true + def value: Long = throw new java.util.NoSuchElementException +} +case class LongSome(value: Long) extends LongOption { + def isEmpty = false +} + +sealed abstract class FloatOption { + def isEmpty: Boolean + def value: Float +} +case object FloatNone extends FloatOption { + def isEmpty = true + def value: Float = throw new java.util.NoSuchElementException +} +case class FloatSome(value: Float) extends FloatOption { + def isEmpty = false +} + +sealed abstract class DoubleOption { + def isEmpty: Boolean + def value: Double +} +case object DoubleNone extends DoubleOption { + def isEmpty = true + def value: Double = throw new java.util.NoSuchElementException +} +case class DoubleSome(value: Double) extends DoubleOption { + def isEmpty = false +} + +// The underlying implementation uses an exception that has no stack trace for +// the failure case, which is 20x faster than retaining stack traces. Therefore, +// we require no boxing of the results on the happy path. This slows down the +// unhappy path a little bit, but it's still on the same order of magnitude as +// the happy path. +// +// This API should only be used by people who know what they are doing. Note +// that JsonReader implementations consume one character beyond the number that is +// parsed, because there is no terminator character. +object UnsafeNumbers { + + // should never escape into user code + case object UnsafeNumber + extends Exception( + "if you see this a dev made a mistake using UnsafeNumbers" + ) + with NoStackTrace + + def byte(num: String): Byte = + byte_(new JsonReader(num), true) + def byte_(in: JsonReader, consume: Boolean): Byte = + long__(in, Byte.MinValue, Byte.MaxValue, consume).toByte + + def short(num: String): Short = + short_(new JsonReader(num), true) + def short_(in: JsonReader, consume: Boolean): Short = + long__(in, Short.MinValue, Short.MaxValue, consume).toShort + + def int(num: String): Int = + int_(new JsonReader(num), true) + def int_(in: JsonReader, consume: Boolean): Int = + long__(in, Int.MinValue, Int.MaxValue, consume).toInt + + def long(num: String): Long = + long_(new JsonReader(num), true) + def long_(in: JsonReader, consume: Boolean): Long = + long__(in, Long.MinValue, Long.MaxValue, consume) + + def bigInteger(num: String, max_bits: Int): java.math.BigInteger = + bigInteger_(new JsonReader(num), true, max_bits) + def bigInteger_( + in: JsonReader, + consume: Boolean, + max_bits: Int + ): java.math.BigInteger = { + var current: Int = in.read() + var negative = false + + if (current == '-') { + negative = true + current = in.read() + } else if (current == '+') + current = in.read() + if (current == -1) throw UnsafeNumber + + bigDecimal__(in, consume, negative, current, true, max_bits).unscaledValue + } + + // measured faster than Character.isDigit + @inline private[this] def isDigit(i: Int): Boolean = + '0' <= i && i <= '9' + + // is it worth keeping this custom long__ instead of using bigInteger since it + // is approximately double the performance. + def long__(in: JsonReader, lower: Long, upper: Long, consume: Boolean): Long = { + var current: Int = 0 + + current = in.read() + if (current == -1) throw UnsafeNumber + var negative = false + if (current == '-') { + negative = true + current = in.read() + if (current == -1) throw UnsafeNumber + } else if (current == '+') { + current = in.read() + if (current == -1) throw UnsafeNumber + } + + if (!isDigit(current)) + throw UnsafeNumber + + var accum: Long = 0L + while ({ + { + val c = current - '0' + if (accum <= longunderflow) + if (accum < longunderflow) + throw UnsafeNumber + else if (accum == longunderflow && c == 9) + throw UnsafeNumber + // count down, not up, because it is larger + accum = accum * 10 - c // should never underflow + current = in.read() + }; current != -1 && isDigit(current) + }) () + + if (consume && current != -1) throw UnsafeNumber + + if (negative) + if (accum < lower || upper < accum) throw UnsafeNumber + else accum + else if (accum == Long.MinValue) + throw UnsafeNumber + else { + accum = -accum + if (accum < lower || upper < accum) throw UnsafeNumber + else accum + } + } + + def float(num: String, max_bits: Int): Float = + float_(new JsonReader(num), true, max_bits) + + def float_(in: JsonReader, consume: Boolean, max_bits: Int): Float = { + var current: Int = in.read() + var negative = false + + def readAll(s: String): Unit = { + var i = 0 + val len = s.length + + while (i < len) { + current = in.read() + if (current != s(i)) throw UnsafeNumber + i += 1 + } + + current = in.read() // to be consistent read the terminator + + if (consume && current != -1) + throw UnsafeNumber + } + + if (current == 'N') { + readAll("aN") + return Float.NaN + } + + if (current == '-') { + negative = true + current = in.read() + } else if (current == '+') { + current = in.read() + } + + if (current == 'I') { + readAll("nfinity") + + if (negative) return Float.NegativeInfinity + else return Float.PositiveInfinity + } + + if (current == -1) + throw UnsafeNumber + + val res = bigDecimal__(in, consume, negative = negative, initial = current, int_only = false, max_bits = max_bits) + + if (negative && res.unscaledValue == java.math.BigInteger.ZERO) -0.0f + else res.floatValue + } + + def double(num: String, max_bits: Int): Double = + double_(new JsonReader(num), true, max_bits) + + def double_(in: JsonReader, consume: Boolean, max_bits: Int): Double = { + var current: Int = in.read() + var negative = false + + def readall(s: String): Unit = { + var i = 0 + val len = s.length + while (i < len) { + current = in.read() + if (current != s(i)) throw UnsafeNumber + i += 1 + } + current = in.read() // to be consistent read the terminator + if (consume && current != -1) throw UnsafeNumber + } + + if (current == 'N') { + readall("aN") + return Double.NaN + } + + if (current == '-') { + negative = true + current = in.read() + } else if (current == '+') + current = in.read() + + if (current == 'I') { + readall("nfinity") + if (negative) return Double.NegativeInfinity + else return Double.PositiveInfinity + } + + if (current == -1) throw UnsafeNumber + + // we could avoid going via BigDecimal if we wanted to do something like + // https://github.com/plokhotnyuk/jsoniter-scala/blob/56ff2a60e28aa27bd4788caf3b1557a558c00fa1/jsoniter-scala-core/jvm/src/main/scala/com/github/plokhotnyuk/jsoniter_scala/core/JsonJsonReader.scala#L1395-L1425 + // based on + // https://www.reddit.com/r/rust/comments/a6j5j1/making_rust_float_parsing_fast_and_correct + // + // the fallback of .doubleValue tends to call out to parseDouble which + // ultimately uses strtod from the system libraries and they may loop until + // the answer converges + // https://github.com/rust-lang/rust/pull/27307/files#diff-fe6c36003393c49bf7e5c413458d6d9cR43-R84 + val res = bigDecimal__(in, consume, negative, current, false, max_bits) + // BigDecimal doesn't have a negative zero, so we need to apply manually + if (negative && res.unscaledValue == java.math.BigInteger.ZERO) -0.0 + // TODO implement Algorithm M or Bigcomp and avoid going via BigDecimal + else res.doubleValue + } + + def bigDecimal(num: String, max_bits: Int): java.math.BigDecimal = + bigDecimal_(new JsonReader(num), true, max_bits) + def bigDecimal_( + in: JsonReader, + consume: Boolean, + max_bits: Int + ): java.math.BigDecimal = { + var current: Int = in.read() + var negative = false + + if (current == '-') { + negative = true + current = in.read() + } else if (current == '+') + current = in.read() + if (current == -1) throw UnsafeNumber + + bigDecimal__(in, consume, negative, current, false, max_bits) + } + + def bigDecimal__( + in: JsonReader, + consume: Boolean, + negative: Boolean, + initial: Int, + int_only: Boolean, + max_bits: Int + ): java.math.BigDecimal = { + var current: Int = initial + // record the significand as Long until it overflows, then swap to BigInteger + var sig: Long = -1 // -1 means it hasn't been seen yet + var sig_ : java.math.BigInteger = null // non-null wins over sig + var dot: Int = 0 // counts from the right + var exp: Int = 0 // implied + + def advance(): Boolean = { + current = in.read() + current != -1 + } + + // skip trailing zero on the left + while (current == '0') { + sig = 0 + if (!advance()) + return java.math.BigDecimal.ZERO + } + + def push_sig(): Unit = { + val c = current - '0' + // would be nice if there was a fused instruction... + if (sig_ != null) { + sig_ = sig_ + .multiply(java.math.BigInteger.TEN) + .add(bigIntegers(c)) + // arbitrary limit on BigInteger size to avoid OOM attacks + if (sig_.bitLength >= max_bits) + throw UnsafeNumber + } else if (sig >= longoverflow) + sig_ = java.math.BigInteger + .valueOf(sig) + .multiply(java.math.BigInteger.TEN) + .add(bigIntegers(c)) + else if (sig < 0) sig = c.toLong + else sig = sig * 10 + c + } + + def significand() = + if (sig <= 0) java.math.BigDecimal.ZERO + else { + val res = + if (sig_ != null) + new java.math.BigDecimal(sig_) + else + new java.math.BigDecimal(sig) + if (negative) res.negate else res + } + + while (isDigit(current)) { + push_sig() + if (!advance()) + return significand() + } + + if (int_only) { + if (consume && current != -1) + throw UnsafeNumber + return significand() + } + + if (current == '.') { + if (sig < 0) sig = 0 // e.g. ".1" is shorthand for "0.1" + if (!advance()) + return significand() + while (isDigit(current)) { + dot += 1 + if (sig > 0 || current != '0') + push_sig() + // overflowed... + if (dot < 0) throw UnsafeNumber + advance() + } + } + + if (sig < 0) throw UnsafeNumber // no significand + + if (current == 'E' || current == 'e') + exp = int_(in, consume) + else if (consume && current != -1) + throw UnsafeNumber + + val scale = if (dot < 1) exp else exp - dot + val res = significand() + if (scale != 0) + res.scaleByPowerOfTen(scale) + else + res + } + // note that bigDecimal does not have a negative zero + private[this] val bigIntegers: Array[java.math.BigInteger] = + (0L to 9L).map(java.math.BigInteger.valueOf).toArray + private[this] val longunderflow: Long = Long.MinValue / 10L + private[this] val longoverflow: Long = Long.MaxValue / 10L +} \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json2/StringMatrix.scala b/src/main/scala/co.blocke.scalajack/json2/StringMatrix.scala new file mode 100644 index 00000000..320d826a --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json2/StringMatrix.scala @@ -0,0 +1,92 @@ +package co.blocke.scalajack +package json2 + +// A data structure encoding a simple algorithm for Trie pruning: Given a list +// of strings, and a sequence of incoming characters, find the strings that +// match, by manually maintaining a bitset. Empty strings are not allowed. +// +// +final class StringMatrix(val xs: Array[String], aliases: Array[(String, Int)] = Array.empty) { + require(xs.forall(_.nonEmpty)) + require(xs.nonEmpty) + require(xs.length + aliases.length < 64) + require(aliases.forall(_._1.nonEmpty)) + require(aliases.forall(p => p._2 >= 0 && p._2 < xs.length)) + + val width = xs.length + aliases.length + val height: Int = xs.map(_.length).max max (if (aliases.isEmpty) 0 else aliases.map(_._1.length).max) + val lengths: Array[Int] = xs.map(_.length) ++ aliases.map(_._1.length) + val initial: Long = (0 until width).foldLeft(0L)((bs, r) => bs | (1L << r)) + + private val matrix: Array[Int] = { + val m = Array.fill[Int](width * height)(-1) + var string: Int = 0 + while (string < width) { + val s = if (string < xs.length) xs(string) else aliases(string - xs.length)._1 + val len = s.length + var char: Int = 0 + while (char < len) { + m(width * char + string) = s.codePointAt(char) + char += 1 + } + string += 1 + } + m + } + + private val resolve: Array[Int] = { + val r = Array.tabulate[Int](xs.length + aliases.length)(identity) + aliases.zipWithIndex.foreach { case ((_, pi), i) => r(xs.length + i) = pi } + r + } + + // must be called with increasing `char` (starting with bitset obtained from a + // call to 'initial', char = 0) + def update(bitset: Long, char: Int, c: Int): Long = + if (char >= height) 0L // too long + else if (bitset == 0L) 0L // everybody lost + else { + var latest: Long = bitset + val base: Int = width * char + + if (bitset == initial) { // special case when it is dense since it is simple + var string: Int = 0 + while (string < width) { + if (matrix(base + string) != c) + latest = latest ^ (1L << string) + string += 1 + } + } else { + var remaining: Long = bitset + while (remaining != 0L) { + val string: Int = java.lang.Long.numberOfTrailingZeros(remaining) + val bit: Long = 1L << string + if (matrix(base + string) != c) + latest = latest ^ bit + remaining = remaining ^ bit + } + } + + latest + } + + // excludes entries that are not the given exact length + def exact(bitset: Long, length: Int): Long = + if (length > height) 0L // too long + else { + var latest: Long = bitset + var remaining: Long = bitset + while (remaining != 0L) { + val string: Int = java.lang.Long.numberOfTrailingZeros(remaining) + val bit: Long = 1L << string + if (lengths(string) != length) + latest = latest ^ bit + remaining = remaining ^ bit + } + latest + } + + def first(bitset: Long): Int = + if (bitset == 0L) -1 + else resolve(java.lang.Long.numberOfTrailingZeros(bitset)) // never returns 64 +} \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json2/package.scala b/src/main/scala/co.blocke.scalajack/json2/package.scala new file mode 100644 index 00000000..3bb815c2 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json2/package.scala @@ -0,0 +1,5 @@ +package co.blocke.scalajack +package json2 + +val BUFFER_EXCEEDED: Char = 7 // Old "BELL" ASCII value, used as a marker when we've run off the end of the known world +val END_OF_STRING: Char = 3 \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/parser/Instruction.scala b/src/main/scala/co.blocke.scalajack/parser/Instruction.scala new file mode 100644 index 00000000..479eab24 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/parser/Instruction.scala @@ -0,0 +1,60 @@ +package co.blocke.scalajack +package parser + +import scala.collection.Factory + +enum Expects{ + case ExpectBoolean, ExpectString, ExpectLong, ExpectBigLong, ExpectBigDouble, ExpectDouble, ExpectList, ExpectClass, ExpectObject, ExpectTuple +} + +// Don't need Expects for: +// expectList: ListInstruction is the only acceptable instruction with arity-1 (1 paramter) +// expectObject: ObjectInstruction is the only taker here: 2 params [K,V] +// expectClass: ClassInstrunction is the only applicable value + + +sealed trait Instruction: + val expect: Expects + type Z + +// Expects function requires no parameters +abstract class SimpleInstruction[I,O]() extends Instruction: + type Z = O + val expect: Expects + def transform(in: Either[ParseError, I]): Either[ParseError, O] + +// Expects function requires a single parameter +abstract class ListInstruction[E,T](val elementInstruction: Instruction) extends Instruction: + type Z = T + def transform(in: Either[ParseError, List[elementInstruction.Z]]): Either[ParseError, T] + +abstract class ClassInstruction[T](val fieldInstructions: Map[String,Instruction]) extends Instruction: + type Z = T + def transform(in: Either[ParseError, Map[String,Any]]): Either[ParseError, T] + +//------------------------------------------------------------- + +case class BooleanInstruction() extends SimpleInstruction[Boolean,Boolean]: + val expect: Expects = Expects.ExpectBoolean + def transform(in: Either[ParseError, Boolean]): Either[ParseError, Boolean] = in + +case class CharInstruction() extends SimpleInstruction[String,Char]: + val expect: Expects = Expects.ExpectString + def transform(in: Either[ParseError, String]): Either[ParseError, Char] = in.map(_.head) + +case class IntInstruction() extends SimpleInstruction[Long,Int]: + val expect: Expects = Expects.ExpectLong + def transform(in: Either[ParseError, Long]): Either[ParseError, Int] = in.map(_.intValue) + +case class StringInstruction() extends SimpleInstruction[String,String]: + val expect: Expects = Expects.ExpectString + def transform(in: Either[ParseError, String]): Either[ParseError, String] = in + +case class SeqInstruction[E,T](override val elementInstruction: Instruction)(using Factory[E,T]) extends ListInstruction[E,T](elementInstruction): + val expect: Expects = Expects.ExpectList + inline def collectionConvert[X,Y](in:List[X])(using Factory[X,Y]) = in.to( summon[Factory[X,Y]] ) + def transform(in: Either[ParseError, List[elementInstruction.Z]]): Either[ParseError, T] = in.map(e => collectionConvert[E,T](e.asInstanceOf[List[E]])) + +case class ScalaClassInstruction[T](override val fieldInstructions: Map[String,Instruction], instantiator: Map[String,?]=>T) extends ClassInstruction[T](fieldInstructions): + val expect: Expects = Expects.ExpectClass + def transform(in: Either[ParseError, Map[String,Any]]): Either[ParseError,T] = in.map(instantiator(_)) diff --git a/src/main/scala/co.blocke.scalajack/parser/Parser.scala b/src/main/scala/co.blocke.scalajack/parser/Parser.scala new file mode 100644 index 00000000..97e9c8fd --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/parser/Parser.scala @@ -0,0 +1,40 @@ +package co.blocke.scalajack +package parser + +import scala.collection.mutable.HashMap + +abstract class ParseError(message: String) extends Throwable(message) + + +trait Parser: + + inline def parse(inst: Instruction): Either[ParseError, inst.Z] = + (inst.expect match + case Expects.ExpectString => inst.asInstanceOf[SimpleInstruction[String,?]].transform(parseString()) + case Expects.ExpectLong => inst.asInstanceOf[SimpleInstruction[Long,?]].transform(parseLong()) + case Expects.ExpectBoolean => parseBoolean() + case Expects.ExpectList => + val z = inst.asInstanceOf[ListInstruction[?,inst.Z]] + z.transform(parseList(z.elementInstruction)) + case Expects.ExpectClass => + val z = inst.asInstanceOf[ScalaClassInstruction[inst.Z]] + z.transform(parseClass(z.fieldInstructions, HashMap.empty[String,Any])) // TODO: HashMap of default values, option:None, etc + case Expects.ExpectObject => Left(new json.JsonParseError("Unupported")) + case Expects.ExpectTuple => Left(new json.JsonParseError("Unupported")) + case Expects.ExpectDouble => Left(new json.JsonParseError("Unupported")) + case Expects.ExpectBigLong => Left(new json.JsonParseError("Unupported")) + case Expects.ExpectBigDouble => Left(new json.JsonParseError("Unupported")) + ).asInstanceOf[Either[ParseError,inst.Z]] + + + //----------------------------------------------------- + + // Parse the raw primitives + inline def parseBoolean(): Either[ParseError, Boolean] + inline def parseString(): Either[ParseError, String] + inline def parseLong(): Either[ParseError, Long] + def parseList(inst: Instruction): Either[ParseError, List[inst.Z]] + def parseClass(inst: Map[String,Instruction], fieldValues: HashMap[String,Any]): Either[ParseError, Map[String,Any]] + + //----------------------------------------------------- + diff --git a/src/main/scala/co.blocke.scalajack/parser/Reader.scalax b/src/main/scala/co.blocke.scalajack/parser/Reader.scalax new file mode 100644 index 00000000..ef89c4bf --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/parser/Reader.scalax @@ -0,0 +1,33 @@ +package co.blocke.scalajack +package parser + +import scala.collection.Factory + +trait Parser + +/** + * A Reader's job is simply this: to accept as input raw data from a Parser in one of the basic parser data types + * and produce a well-typed result fit for consumption. Raw Parser types are: + * - String + * - Long + * - BigLong + * - Double + * - BigDouble + * - Object (differs from map in that the values, representing fields, will likely have different types) + * - List[T] + * - Map[K,V] + */ +trait Reader + +class CharReader() extends Reader: + inline def read(p: Parser, data: String): Char = data.head + +class StringReader() extends Reader: + inline def read(p: Parser, data: String): String = data + +case class IntReader() extends Reader: + inline def read(p: Parser, data: Long): Int = data.intValue + +case class ListReader() extends Reader: + inline def collectionConvert[T,U](in:List[T])(using Factory[T, U]) = in.to( summon[Factory[T,U]] ) + inline def read[E,T](p: Parser, data: List[E])(using Factory[E,T]): T = collectionConvert[E,T](data) diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index c5ef4a4a..04d069b7 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -6,14 +6,6 @@ import scala.jdk.CollectionConverters.* object RunMe extends App: - /* - val f = () => parser.expectLong - val f2 = () => parser.expectList[Long](() => parser.expectLong) - val r = parser.expectList[List[Long]](f2) - - println("R: " + r) - */ - given json.JsonConfig = json .JsonConfig() .copy(noneAsNull = true) @@ -21,37 +13,73 @@ object RunMe extends App: // .copy(enumsAsIds = '*') try - val v = Person("Greg", DIAMOND, Command(15), new Wrapper(-10)) - println("HERE: " + ScalaJack.write(v)) - println(RType.of[Wrapper].pretty) + import json2.* + import json.JsonParseError + + /* StringMatrix experiments.... + */ + + /* + val matrix = new StringMatrix(List("foo", "bar", "baz").toArray, Array(("boom", 0))) + val r = JsonReader("boom\" asdf ") + var i: Int = 0 + var bs: Long = matrix.initial + var c: Int = -1 + while { c = r.readEscapedString(); c != END_OF_STRING } do { + bs = matrix.update(bs, i, c) + i += 1 + } + bs = matrix.exact(bs, i) + println("HERE: " + matrix.first(bs)) + */ - // println(RType.of[Person[Int]]) + // val numList = """[["1","2,3"],["4","5"]]""" + // val dec = JsonDecoder[List[List[String]]] + // println(dec.decodeJson(numList)) - // println("HERE: " + ScalaJack.write(Person("Greg", Foom('z'), Some(Person("Lili", Foom('x'), None))))) + implicit val addrDecoder: JsonDecoder[Address] = ClassDecoder( + Array("street", "city", "state", "postal_code"), + Array(JsonDecoder[String], JsonDecoder[String], JsonDecoder[String], JsonDecoder[String]) + ) + implicit val friendDecoder: JsonDecoder[Friend] = ClassDecoder( + Array("name", "age", "email"), + Array(JsonDecoder[String], JsonDecoder[Int], JsonDecoder[String]) + ) + implicit val petDecoder: JsonDecoder[Pet] = ClassDecoder( + Array("name", "species", "age"), + Array(JsonDecoder[String], JsonDecoder[String], JsonDecoder[Int]) + ) + implicit val PersonDecoder: JsonDecoder[Person] = ClassDecoder( + Array("name", "age", "address", "email", "phone_numbers", "is_employed"), + Array(JsonDecoder[String], JsonDecoder[Int], JsonDecoder[Address], JsonDecoder[String], JsonDecoder[List[String]], JsonDecoder[Boolean]) + ) + implicit val RecordDecoder: JsonDecoder[Record] = ClassDecoder( + Array("person", "hobbies", "friends", "pets"), + Array(JsonDecoder[Person], JsonDecoder[List[String]], JsonDecoder[List[Friend]], JsonDecoder[List[Pet]]) + ) - // val now = System.currentTimeMillis() - // for i <- 1 to 10000000 do ScalaJack.write(List(List(1, 2, 3), List(4, 5, 6), List(7, 8, 9))) - // val now2 = System.currentTimeMillis() - // println("Macro-based: " + (now2 - now)) + val addr = """{"street":"319 Hampton Ct","city":"Coppell","state":"TX","postal_code":"75019"}""" + val dec2 = JsonDecoder[Address] + println(JsonDecoder[Record].decodeJson(jsData)) - // val person = Person("David", 16) - // val minor = MinorPerson.make(person) match - // case Right(r) => r - // case _ => throw new Exception("boom") + /* + import parser.* + import json.* - // val p = SampleNeo(NonEmptyString("Greg"), "MVP", minor) - // val js = ScalaJack.write(x) - // println(js) + // val inst = SeqInstruction[Int, List[Int]](IntInstruction()) + // val inst2 = SeqInstruction[List[Int], Set[Seq[Int]]](inst) + val js = """{"hey":"wowowow","age":57,"other":[1,2,3]}""" + // 0123456789012345678901234567890123456789012234567890 - /* - val x = Blah("foo", WeekDay.Fri) - val js = ScalaJack.write(x) - println(js) + println(ScalaJack.read[Record](jsData)) - val inst = ScalaJack.read[Blah](js) - println(inst) - */ + val jp = json.JsonParser3(jsData) + println(">>> " + jp.parse()) + + // val p = JsonParser2(js, JsonConfig()) + // println("Result: " + p.parse(inst2)) + */ catch { case t: Throwable => diff --git a/src/main/scala/co.blocke.scalajack/run/Record.scala b/src/main/scala/co.blocke.scalajack/run/Record.scala new file mode 100644 index 00000000..76b3f79f --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/run/Record.scala @@ -0,0 +1,88 @@ +package co.blocke.scalajack +package run + +case class Person( + name: String, + age: Int, + address: Address, + email: String, + phone_numbers: List[String], + is_employed: Boolean +) + +case class Address( + street: String, + city: String, + state: String, + postal_code: String +) + +case class Friend( + name: String, + age: Int, + email: String +) + +case class Pet( + name: String, + species: String, + age: Int +) + +case class Record( + person: Person, + hobbies: List[String], + friends: List[Friend], + pets: List[Pet] +) + +case class Foo(name: String, age:Int) + +val jsData = + """{ + "person": { + "name": "John Doe", + "age": 30, + "address": { + "street": "123 Main Street", + "city": "Anytown", + "state": "CA", + "postal_code": "12345" + }, + "email": "john.doe@example.com", + "phone_numbers": [ + "555-555-5555", + "555-123-4567" + ], + "is_employed": true + }, + "hobbies": [ + "reading", + "swimming", + "traveling" + ], + "friends": [ + { + "name": "Jane Smith", + "age": 28, + "email": "jane.smith@example.com" + }, + { + "name": "Bob Johnson", + "age": 32, + "email": "bob.johnson@example.com" + } + ], + "pets": [ + { + "name": "Fido", + "species": "Dog", + "age": 5 + }, + { + "name": "Whiskers", + "species": "Cat", + "age": 3 + } + ] + }""" \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/run/Sample.scala b/src/main/scala/co.blocke.scalajack/run/Sample.scalax similarity index 96% rename from src/main/scala/co.blocke.scalajack/run/Sample.scala rename to src/main/scala/co.blocke.scalajack/run/Sample.scalax index dd7a650f..1a67dd21 100644 --- a/src/main/scala/co.blocke.scalajack/run/Sample.scala +++ b/src/main/scala/co.blocke.scalajack/run/Sample.scalax @@ -25,6 +25,8 @@ case class Foom[X](x: X) extends Miss[X] // case class Person[Y](name: String, age: Miss[Y], again: Option[Person[Y]]) +case class Greg(hey: String, age: Int) + case class Person[T](val name: String, val card: card, val msg: msg[T], meh: Wrapper): var thingy: String = "wow" From caaa5e3d1c739f19e95c79c78439a9a4bdcb6da1 Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Wed, 15 Nov 2023 10:14:42 -0600 Subject: [PATCH 24/65] checkpoint --- LICENSE | 24 +- benchmark/README.md | 68 +- benchmark/build.sbt | 2 + .../src/main/scala/co.blocke/Argonaut.scala | 6 + .../src/main/scala/co.blocke/Benchmark.scala | 32 +- .../src/main/scala/co.blocke/Fabric.scala | 17 - .../src/main/scala/co.blocke/Fabric.scalax | 24 + benchmark/src/main/scala/co.blocke/Jawn.scala | 17 - .../src/main/scala/co.blocke/Jawn.scalax | 17 + .../src/main/scala/co.blocke/Jsoniter.scala | 19 + benchmark/src/main/scala/co.blocke/Run.scala | 15 +- .../scala/co.blocke.scalajack/ScalaJack.scala | 48 +- .../json/ClassDecoder.scala | 32 + .../{json2 => json}/FastStringBuilder.scala | 9 +- .../FieldKeyDecoder.scalax} | 16 +- .../json/JsonDecoder.scala | 87 ++ .../JsonDecoder.scalax} | 46 +- .../co.blocke.scalajack/json/JsonError.scala | 21 +- .../co.blocke.scalajack/json/JsonParser.scala | 499 +++------ .../json/JsonParser.scalax | 367 +++++++ .../json/JsonParser2.scala | 219 ---- .../json/JsonParser3.scala | 188 ---- .../co.blocke.scalajack/json/JsonReader.scala | 76 +- .../json/JsonReader.scalax | 49 - .../json/JsonReaderUtil.scala | 26 +- .../co.blocke.scalajack/json/JsonSource.scala | 69 ++ .../co.blocke.scalajack/json/Numbers.scala | 829 +++++++++++++++ .../json/ParseInstructions.scalax | 11 - .../json/ReaderModule.scalax | 24 - .../json/ReaderModule2.scalax | 35 - .../json/StringMatrix.scala | 84 ++ .../{json2 => json}/package.scala | 4 +- .../json2/ClassDecoder.scala | 31 - .../json2/JsonParser.scala | 164 --- .../json2/JsonReader.scala | 86 -- .../co.blocke.scalajack/json2/Numbers.scala | 990 ------------------ .../json2/StringMatrix.scala | 92 -- .../parser/Instruction.scala | 60 -- .../co.blocke.scalajack/parser/Parser.scala | 40 - .../co.blocke.scalajack/parser/Reader.scalax | 33 - .../scala/co.blocke.scalajack/run/Play.scala | 27 +- .../co.blocke.scalajack/run/Record.scala | 46 +- 42 files changed, 1956 insertions(+), 2593 deletions(-) delete mode 100644 benchmark/src/main/scala/co.blocke/Fabric.scala create mode 100644 benchmark/src/main/scala/co.blocke/Fabric.scalax delete mode 100644 benchmark/src/main/scala/co.blocke/Jawn.scala create mode 100644 benchmark/src/main/scala/co.blocke/Jawn.scalax create mode 100644 benchmark/src/main/scala/co.blocke/Jsoniter.scala create mode 100644 src/main/scala/co.blocke.scalajack/json/ClassDecoder.scala rename src/main/scala/co.blocke.scalajack/{json2 => json}/FastStringBuilder.scala (74%) rename src/main/scala/co.blocke.scalajack/{json2/FieldKeyDecoder.scala => json/FieldKeyDecoder.scalax} (90%) create mode 100644 src/main/scala/co.blocke.scalajack/json/JsonDecoder.scala rename src/main/scala/co.blocke.scalajack/{json2/JsonDecoder.scala => json/JsonDecoder.scalax} (65%) create mode 100644 src/main/scala/co.blocke.scalajack/json/JsonParser.scalax delete mode 100644 src/main/scala/co.blocke.scalajack/json/JsonParser2.scala delete mode 100644 src/main/scala/co.blocke.scalajack/json/JsonParser3.scala delete mode 100644 src/main/scala/co.blocke.scalajack/json/JsonReader.scalax create mode 100644 src/main/scala/co.blocke.scalajack/json/JsonSource.scala create mode 100644 src/main/scala/co.blocke.scalajack/json/Numbers.scala delete mode 100644 src/main/scala/co.blocke.scalajack/json/ParseInstructions.scalax delete mode 100644 src/main/scala/co.blocke.scalajack/json/ReaderModule.scalax delete mode 100644 src/main/scala/co.blocke.scalajack/json/ReaderModule2.scalax create mode 100644 src/main/scala/co.blocke.scalajack/json/StringMatrix.scala rename src/main/scala/co.blocke.scalajack/{json2 => json}/package.scala (77%) delete mode 100644 src/main/scala/co.blocke.scalajack/json2/ClassDecoder.scala delete mode 100644 src/main/scala/co.blocke.scalajack/json2/JsonParser.scala delete mode 100644 src/main/scala/co.blocke.scalajack/json2/JsonReader.scala delete mode 100644 src/main/scala/co.blocke.scalajack/json2/Numbers.scala delete mode 100644 src/main/scala/co.blocke.scalajack/json2/StringMatrix.scala delete mode 100644 src/main/scala/co.blocke.scalajack/parser/Instruction.scala delete mode 100644 src/main/scala/co.blocke.scalajack/parser/Parser.scala delete mode 100644 src/main/scala/co.blocke.scalajack/parser/Reader.scalax diff --git a/LICENSE b/LICENSE index 80629b49..38b4d1c5 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -MIT License +## MIT License Copyright (c) 2020 Greg Zoller @@ -19,3 +19,25 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +## ZIO-Json Attribution + +Parts of JSON reading software were used either directly or derived from the +[ZIO-Json project](https://github.com/zio/zio-json) +licensed under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0). +In most cases, "derived" means removal of features not needed by ScalaJack or +other changes needed to adapt the code to the ScalaJack ecosystem. Regardless, +they are credited here as materially the same as those in the ZIO-Json project. + +The files used directly or derived are: +* FastStringBuilder.scala +* FieldKeyDecoder.scala +* JsonDecoder.scala +* JsonParser.scala +* JsonReader.scala +* Numbers.scala +* StringMatrix.scala + +The terms, privileges, and restrictions provided by the Apache License 2.0 +fully apply to these files, where these differ from the MIT license, which +applies to the rest of ScalaJack's code. \ No newline at end of file diff --git a/benchmark/README.md b/benchmark/README.md index 7e8c96c1..bb407c4b 100644 --- a/benchmark/README.md +++ b/benchmark/README.md @@ -10,37 +10,63 @@ of its life, again to be a more realistic use case. Run benchmark from the ScalaJack/benchmark directory (not the main ScalaJack project directory): ``` -sbt "jmh:run -i 10 -wi 10 -f 2 -t 1 co.blocke.WritingBenchmark" +sbt "jmh:run -i 10 -wi 10 -f 2 -t 1 co.blocke.*" ``` +## Reading Performance: + +| Benchmark | Mode | Count | Score | Error | Units | +|------------------|-------|-------:|----------------:|-------------:|-------| +| Jsoniter | thrpt | 20 | 987991.329 | ± 6645.992 | ops/s | +| **ScalaJack 8** | thrpt | 20 | **633764.943**| ± 10394.860 | ops/s | +| ZIOJson | thrpt | 20 | 586716.228 | ± 2542.783 | ops/s | +| Circe | thrpt | 20 | 266568.198 | ± 5695.754 | ops/s | +| Play | thrpt | 20 | 207737.560 | ± 842.108 | ops/s | +| Argonaut | thrpt | 20 | 197876.777 | ± 11181.751 | ops/s | + ## Writing Performance: | Benchmark | Mode | Count | Score | Error | Units | |------------------|-------|-------:|----------------:|-------------:|-------| -| Hand-Tooled | thrpt | 20 | 2,575,393.513 | ± 178731.952 | ops/s | -| Circe | thrpt | 20 | 1,939,339.085 | ± 6279.547 | ops/s | -|**ScalaJack 8** | thrpt | 20 | **176,867,514.557** | ± 12260.518 | ops/s | -| ZIO JSON | thrpt | 20 | 818,228.736 | ± 3070.298 | ops/s | -| Argonaut | thrpt | 20 | 716,228.404 | ± 6241.145 | ops/s | -| Play JSON | thrpt | 20 | 438,538.475 | ± 16319.198 | ops/s | -| ScalaJack 7 | thrpt | 20 | 106,292.338 | ± 330.111 | ops/s | +| Jsoniter | thrpt | 20 | 2843150.452 | ± 21478.503 | ops/s | +| Hand-Tooled | thrpt | 20 | 2732571.374 | ± 15129.007 | ops/s | +| Circe | thrpt | 20 | 1958244.437 | ± 23965.817 | ops/s | +|**ScalaJack 8** | thrpt | 20 | **1729426.328** | ± 4484.721 | ops/s | +| ZIO JSON | thrpt | 20 | 794352.301 | ± 32336.852 | ops/s | +| Argonaut | thrpt | 20 | 690269.697 | ± 6348.882 | ops/s | +| Play JSON | thrpt | 20 | 438650.022 | ± 23800.221 | ops/s | ### Interpretation -The Hand-Tooled case is creating JSON manually in code. I included it to show the presumed -upper-bound of achievable performance. No serializer, with whatever logic it must do, can be faster -than hand-tooled code that is hard-wired in its output and requires zero logic. +Performance for ScalaJack has been... a journey. As I've explored the population of serializers +available for Scala, of which this is a sample of popular choices, I've observed "generations" +of designs. ScalaJack 8 has grown through each successive generation. + +Focusing originally on write performance, my original design was very attuned to the internal +structure of ScalaJack. The code was clean and beautiful--and slow! I was able to get a write +score of only 30-50000, vs Circe, my original benchmark, which was just under 2 million. Not a +good showing. After an extensive overhaul and re-think, performance peaked at the 1.7 million +mark, which I was happy with. That put ScalaJack ahead of everyone else except Circe. Then +something unexpected happened... + +I tried Jsoniter, and was dumbstruck when it outperformed hand-tooled code, which I expected +would be a natural theoretical maximum write performance. How can a full serializer, having +whatever logic, be *faster* than hand-tooled code with zero logic?! This breakout level of +performance for Jsoniter continued on the read tests, being roughly 4x faster than most and +2x the level of ZIO-Json, the previous front-runner. How?! -We see in these results that both Circe and ScalaJack are very close in performance--close to each -other and very close to the performance of hand-tooled code. +I observed the older serializers processed JSON to/from an AST and used conventional JSON +parsing techniques; basically fortified editions of a simple JSON parser. ZIO-Json's +impressive read performance wasn't achieved by any one thing, but rather a collection of well- +applied techniques, including *not* using an intermediate AST. So naturally I incorporated +some of ZIO-Json's approach (and a bit of their code), stripped, refitted, and adapted to +ScalaJack, and read performance jumped to 633K. Nice! -Circe is the gold standard for JSON serializers due to its many features, excellent performance, and -widespread adoption. The one cost Circe imposes is the same one virtually all other serializers -require: boilerplate must be provided to define encoders/decoders to aid the serializion. Circe's -boilerplate is actually not terrible. Others require a fair bit of extra code per class serialized. +Jsoniter, it turns out, achieves its neck-breaking speed by going deep--very deep. They +use a lot of low level byte arrays and bitwise operators, much as you'd expect in a C program, +to improve on the standard library functions everyone else uses. It works. -ScalaJack's focus is first and foremost to be frictionless--no drama to the user. The very slight -difference in maximal performance is a worthy expense--its still blazing fast. ScalaJack requires +ScalaJack's focus is first and foremost to be frictionless--no drama to the user. ScalaJack requires zero boilerplate--you can throw any Scala object (or even a Java object) at it with no pre-preparation -and it will serialize it. You'll notice the enormous performange improvement ScalaJack 8 has over -ScalaJack 7, due to moving everything possible into compile-time macros for speed. +and it will serialize it. For its intended use-cases, ScalaJack offers performance equal +to, or exceeding, several widely-used alternative choices. diff --git a/benchmark/build.sbt b/benchmark/build.sbt index 761843db..e563f5a0 100644 --- a/benchmark/build.sbt +++ b/benchmark/build.sbt @@ -44,6 +44,8 @@ lazy val benchmark = project "org.typelevel" %% "fabric-io" % "1.12.6", "org.typelevel" %% "jawn-parser" % "1.3.2", "org.typelevel" %% "jawn-ast" % "1.3.2", + "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core" % "2.24.4", + "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-macros" % "2.24.4" % "compile-internal", // "io.circe" %% "circe-derivation" % "0.15.0-M1", // "io.circe" %% "circe-jackson29" % "0.14.0", // "org.json4s" %% "json4s-jackson" % "4.0.4", diff --git a/benchmark/src/main/scala/co.blocke/Argonaut.scala b/benchmark/src/main/scala/co.blocke/Argonaut.scala index 7b614f0e..7ca40696 100644 --- a/benchmark/src/main/scala/co.blocke/Argonaut.scala +++ b/benchmark/src/main/scala/co.blocke/Argonaut.scala @@ -21,6 +21,12 @@ object ArgonautZ: implicit val CodecRecord: CodecJson[Record] = casecodec4(Record.apply, (a: Record) => Option((a.person, a.hobbies, a.friends, a.pets)))("person", "hobbies", "friends", "pets") + + trait ArgonautReadingBenchmark { + @Benchmark + def readRecordArgonaut = Parse.decodeEither[Record](jsData) + } + trait ArgonautWritingBenchmark { @Benchmark def writeRecordArgonaut = record.asJson diff --git a/benchmark/src/main/scala/co.blocke/Benchmark.scala b/benchmark/src/main/scala/co.blocke/Benchmark.scala index 4f6a2110..40c61085 100644 --- a/benchmark/src/main/scala/co.blocke/Benchmark.scala +++ b/benchmark/src/main/scala/co.blocke/Benchmark.scala @@ -43,24 +43,24 @@ trait HandTooledWritingBenchmark { @BenchmarkMode(Array(Mode.Throughput)) @OutputTimeUnit(TimeUnit.SECONDS) class ReadingBenchmark - // extends CirceZ.CirceReadingBenchmark - extends ScalaJackZ.ScalaJackReadingBenchmark - // extends ZIOZ.ZIOJsonReadingBenchmark - // extends PlayZ.PlayReadingBenchmark - // extends FabricZ.FabricReadingBenchmark - // extends JawnZ.JawnReadingBenchmark + extends CirceZ.CirceReadingBenchmark + with ScalaJackZ.ScalaJackReadingBenchmark + with JsoniterZ.JsoniterReadingBenchmark + with ZIOZ.ZIOJsonReadingBenchmark + with PlayZ.PlayReadingBenchmark + with ArgonautZ.ArgonautReadingBenchmark @State(Scope.Thread) @BenchmarkMode(Array(Mode.Throughput)) @OutputTimeUnit(TimeUnit.SECONDS) class WritingBenchmark - // extends CirceZ.CirceWritingBenchmark - extends ScalaJackZ.ScalaJackWritingBenchmark - // with HandTooledWritingBenchmark - // with ArgonautZ.ArgonautWritingBenchmark - // with PlayZ.PlayWritingBenchmark - // with ZIOZ.ZIOJsonWritingBenchmark - + extends HandTooledWritingBenchmark + with CirceZ.CirceWritingBenchmark + with ScalaJackZ.ScalaJackWritingBenchmark + with JsoniterZ.JsoniterWritingBenchmark + with ZIOZ.ZIOJsonWritingBenchmark + with PlayZ.PlayWritingBenchmark + with ArgonautZ.ArgonautWritingBenchmark // "Old-New" ScalaJack // [info] Benchmark Mode Cnt Score Error Units @@ -75,9 +75,3 @@ class WritingBenchmark // Jawn (parse only + AST) 336384.617 // ScalaJack JsonParser3 (parse only + AST) 279456.523 // Fabric (new!) (parse only + AST) 270706.567 - - - - -// SJ StringBuffer : 1740040.225 -// SJ FastStringBuffer : \ No newline at end of file diff --git a/benchmark/src/main/scala/co.blocke/Fabric.scala b/benchmark/src/main/scala/co.blocke/Fabric.scala deleted file mode 100644 index 18c3fb81..00000000 --- a/benchmark/src/main/scala/co.blocke/Fabric.scala +++ /dev/null @@ -1,17 +0,0 @@ -package co.blocke - -import org.openjdk.jmh.annotations._ - -object FabricZ: - import fabric.* - import fabric.io.* - - trait FabricReadingBenchmark{ - @Benchmark - def readRecordFabric = JsonParser(jsData, Format.Json) - } - - // trait CirceWritingBenchmark { - // @Benchmark - // def writeRecordCirce = record.asJson - // } diff --git a/benchmark/src/main/scala/co.blocke/Fabric.scalax b/benchmark/src/main/scala/co.blocke/Fabric.scalax new file mode 100644 index 00000000..06bf7e40 --- /dev/null +++ b/benchmark/src/main/scala/co.blocke/Fabric.scalax @@ -0,0 +1,24 @@ +package co.blocke + +import org.openjdk.jmh.annotations._ + +object FabricZ: + import fabric.* + import fabric.io.* + import fabric.rw.* + + implicit val rw: RW[Record] = RW.gen + + trait FabricReadingBenchmark{ + @Benchmark + def readRecordFabric = rw.write(JsonParser(jsData, Format.Json)) + //JsonParser(jsData, Format.Json) + } + + trait FabricWritingBenchmark{ + @Benchmark + def writeRecordFabric = rw.read(record) + } + + // No Fabric write test. Fabric has a different model... not simple serialization. + // Kinda like a query captured and compiled. diff --git a/benchmark/src/main/scala/co.blocke/Jawn.scala b/benchmark/src/main/scala/co.blocke/Jawn.scala deleted file mode 100644 index 68b7ad8a..00000000 --- a/benchmark/src/main/scala/co.blocke/Jawn.scala +++ /dev/null @@ -1,17 +0,0 @@ -package co.blocke - -import org.openjdk.jmh.annotations._ - -object JawnZ: - - import org.typelevel.jawn.ast.* - - trait JawnReadingBenchmark{ - @Benchmark - def readRecordFabric = JParser.parseFromString(jsData) - } - - // trait CirceWritingBenchmark { - // @Benchmark - // def writeRecordCirce = record.asJson - // } diff --git a/benchmark/src/main/scala/co.blocke/Jawn.scalax b/benchmark/src/main/scala/co.blocke/Jawn.scalax new file mode 100644 index 00000000..ef82d38b --- /dev/null +++ b/benchmark/src/main/scala/co.blocke/Jawn.scalax @@ -0,0 +1,17 @@ +package co.blocke + +import org.openjdk.jmh.annotations._ + +object JawnZ: + + import org.typelevel.jawn.ast.* + + trait JawnReadingBenchmark{ + @Benchmark + def readRecordJawn = JParser.parseFromString(jsData) + } + + trait JawnWritingBenchmark { + @Benchmark + def writeRecordJawn = record.asJson + } diff --git a/benchmark/src/main/scala/co.blocke/Jsoniter.scala b/benchmark/src/main/scala/co.blocke/Jsoniter.scala new file mode 100644 index 00000000..f045dd15 --- /dev/null +++ b/benchmark/src/main/scala/co.blocke/Jsoniter.scala @@ -0,0 +1,19 @@ +package co.blocke + +import org.openjdk.jmh.annotations._ + +object JsoniterZ: + + import com.github.plokhotnyuk.jsoniter_scala.core._ + import com.github.plokhotnyuk.jsoniter_scala.macros._ + + given codec: JsonValueCodec[Record] = JsonCodecMaker.make + trait JsoniterReadingBenchmark{ + @Benchmark + def readRecordJsoniter = readFromString[Record](jsData) + } + + trait JsoniterWritingBenchmark{ + @Benchmark + def writeRecordJsoniter = writeToString(record) + } diff --git a/benchmark/src/main/scala/co.blocke/Run.scala b/benchmark/src/main/scala/co.blocke/Run.scala index dc945391..bc1ddea9 100644 --- a/benchmark/src/main/scala/co.blocke/Run.scala +++ b/benchmark/src/main/scala/co.blocke/Run.scala @@ -1,16 +1,13 @@ package co.blocke +import com.github.plokhotnyuk.jsoniter_scala.core._ +import com.github.plokhotnyuk.jsoniter_scala.macros._ object RunMe extends App: - import ZIOZ.* - import zio.json._ - import co.blocke.scalajack.* + given codec: JsonValueCodec[Record] = JsonCodecMaker.make + println(readFromString[Record](jsData)) - val f = jsData.fromJson[Record] - println(f) + println(writeToString(record)) - println("\n---------") - println(ScalaJack.write(f)) - - println("ZIO Decoder (Address): "+DeriveJsonDecoder.gen[Address]) \ No newline at end of file + println("\nDone") \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/ScalaJack.scala b/src/main/scala/co.blocke.scalajack/ScalaJack.scala index 73bac0f8..e6d4f5bc 100644 --- a/src/main/scala/co.blocke.scalajack/ScalaJack.scala +++ b/src/main/scala/co.blocke.scalajack/ScalaJack.scala @@ -3,13 +3,12 @@ package co.blocke.scalajack import co.blocke.scala_reflection.{RTypeRef, TypedName} import co.blocke.scala_reflection.reflect.ReflectOnType import co.blocke.scala_reflection.reflect.rtypeRefs.ClassRef -import parser.ParseError import scala.collection.mutable.{HashMap, Map} import scala.quoted.* import quoted.Quotes import json.* -object ScalaJack: +object sj: // Shorter and "lighter" than "ScalaJack" everywhere. inline def write[T](a: T)(using cfg: JsonConfig = JsonConfig()): String = ${ writeImpl[T]('a, 'cfg) } @@ -27,38 +26,15 @@ object ScalaJack: def readImpl[T: Type](js: Expr[String], cfg: Expr[JsonConfig])(using q: Quotes): Expr[Either[ParseError, T]] = import quotes.reflect.* - val classRef = ReflectOnType[T](quotes)(TypeRepr.of[T], true)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) - - val instruction = JsonReader.refRead[T](classRef) - - '{ - val foo = json2.JsonReader($js) - var c = 0 - while c != json2.BUFFER_EXCEEDED do c = foo.read() - Right(null.asInstanceOf[T]) + try { + val classRef = ReflectOnType[T](quotes)(TypeRepr.of[T], true)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) + val decoder = JsonReader.refRead[T](classRef) + '{ + $decoder.decodeJson($js) + } + } catch { + case t: Throwable => + val error = Expr(t.getClass.getName()) + val msg = Expr(t.getMessage()) + '{ Left(ParseError($error + " was thrown with message " + $msg)) } } - - // '{ - // val parser = JsonParser2($js, $cfg) - // parser.parse($instruction).asInstanceOf[Either[ParseError, T]] - // } - - // -------------------->>>> OLD <<---------------------- - - // // Used to trap SelfRef's from going into endless loops and causing Stack Overflow. - // val seenBeforeFnCache = HashMap.empty[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]] - - // val parserE = '{ new JsonParser($js, $cfg) } - // JsonReader().refRead(classRef, parserE, cfg)(using quotes, Type.of[T])(using seenBeforeFnCache) - - // --------------- - - // val fn = JsonReader().readerFn[T](classRef)(using quotes, Type.of[T])(using seenBeforeFnCache) - // val listifiedCache = Expr.ofList(seenBeforeFnCache.toList.map(t => Expr.ofTuple(t))) - - // '{ // run-time - // val parser = JsonParser($js, $listifiedCache.toMap) - // $fn($cfg, parser) match - // case Right(v) => v - // case Left(t) => throw t - // } diff --git a/src/main/scala/co.blocke.scalajack/json/ClassDecoder.scala b/src/main/scala/co.blocke.scalajack/json/ClassDecoder.scala new file mode 100644 index 00000000..32a6d635 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/ClassDecoder.scala @@ -0,0 +1,32 @@ +package co.blocke.scalajack +package json + +import scala.annotation.* + +object ClassDecoder: + // def apply[A: scala.reflect.ClassTag](fields: Array[String], fieldDecoders: Array[JsonDecoder[_]], instantiator: Array[?] => A) = new JsonDecoder[A] { + def apply[A]( + fields: Array[String], + fieldDecoders: Array[JsonDecoder[_]], + instantiator: Array[?] => A, + fieldValues: Array[Any] + ) = new JsonDecoder[A] { + + val fieldMatrix = new StringMatrix(fields) + + def unsafeDecode(in: JsonSource): A = + JsonParser.charWithWS(in, '{') + if JsonParser.firstField(in) then + var done = false + while !done do + val fieldIdx = JsonParser.parseField(in, fieldMatrix) + if fieldIdx < 0 then JsonParser.skipValue(in) + else + val dec = fieldDecoders(fieldIdx) + fieldValues(fieldIdx) = dec.unsafeDecode(in) + if !JsonParser.nextField(in) then done = true + else throw new JsonParseError("Expected fields!", in) + + // Construct the new object + instantiator(fieldValues) + } diff --git a/src/main/scala/co.blocke.scalajack/json2/FastStringBuilder.scala b/src/main/scala/co.blocke.scalajack/json/FastStringBuilder.scala similarity index 74% rename from src/main/scala/co.blocke.scalajack/json2/FastStringBuilder.scala rename to src/main/scala/co.blocke.scalajack/json/FastStringBuilder.scala index 7dacfb01..f027bf1f 100644 --- a/src/main/scala/co.blocke.scalajack/json2/FastStringBuilder.scala +++ b/src/main/scala/co.blocke.scalajack/json/FastStringBuilder.scala @@ -1,5 +1,5 @@ package co.blocke.scalajack -package json2 +package json import java.nio.CharBuffer import java.util.Arrays @@ -7,15 +7,14 @@ import java.util.Arrays // like StringBuilder but doesn't have any encoding or range checks final class FastStringBuilder(initial: Int = 16) { private[this] var chars: Array[Char] = new Array[Char](initial) - private[this] var i: Int = 0 + private[this] var i: Int = 0 def append(c: Char): FastStringBuilder = { - if (i == chars.length) - chars = Arrays.copyOf(chars, chars.length * 2) + if i == chars.length then chars = Arrays.copyOf(chars, chars.length * 2) chars(i) = c i += 1 this } def buffer: CharSequence = CharBuffer.wrap(chars, 0, i) -} \ No newline at end of file +} diff --git a/src/main/scala/co.blocke.scalajack/json2/FieldKeyDecoder.scala b/src/main/scala/co.blocke.scalajack/json/FieldKeyDecoder.scalax similarity index 90% rename from src/main/scala/co.blocke.scalajack/json2/FieldKeyDecoder.scala rename to src/main/scala/co.blocke.scalajack/json/FieldKeyDecoder.scalax index a174b686..c3addcee 100644 --- a/src/main/scala/co.blocke.scalajack/json2/FieldKeyDecoder.scala +++ b/src/main/scala/co.blocke.scalajack/json/FieldKeyDecoder.scalax @@ -1,7 +1,5 @@ package co.blocke.scalajack -package json2 - -import json.JsonParseError +package json trait FieldKeyDecoder[+A] { self => @@ -16,7 +14,7 @@ trait FieldKeyDecoder[+A] { def unsafeDecodeField(in: String): B = f(self.unsafeDecodeField(in)) match { case Left(err) => throw JsonParseError(err) - case Right(b) => b + case Right(b) => b } } @@ -32,19 +30,19 @@ object FieldKeyDecoder { implicit val int: FieldKeyDecoder[Int] = FieldKeyDecoder[String].mapOrFail { str => - try { + try Right(str.toInt) - } catch { + catch { case n: NumberFormatException => Left(s"Invalid Int: '$str': $n") } } implicit val long: FieldKeyDecoder[Long] = FieldKeyDecoder[String].mapOrFail { str => - try { + try Right(str.toLong) - } catch { + catch { case n: NumberFormatException => Left(s"Invalid Long: '$str': $n") } } -} \ No newline at end of file +} diff --git a/src/main/scala/co.blocke.scalajack/json/JsonDecoder.scala b/src/main/scala/co.blocke.scalajack/json/JsonDecoder.scala new file mode 100644 index 00000000..4b37232a --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/JsonDecoder.scala @@ -0,0 +1,87 @@ +package co.blocke.scalajack +package json + +import scala.annotation.* + +trait JsonDecoder[A]: + self => + final def decodeJson(str: CharSequence): Either[JsonParseError, A] = + try Right(unsafeDecode(new JsonSource(str))) + catch { + case jpe: JsonParseError => Left(jpe) + } + + final def map[B](f: A => B): JsonDecoder[B] = + new JsonDecoder[B] { + def unsafeDecode(in: JsonSource): B = + f(self.unsafeDecode(in)) + } + + def unsafeDecode(in: JsonSource): A + +//------------------------------------------------------------ + +object JsonDecoder extends DecoderLowPriority1: + +// def apply[A](implicit a: JsonDecoder[A]): JsonDecoder[A] = a + + // Primitive support... + implicit val string: JsonDecoder[String] = new JsonDecoder[String] { + def unsafeDecode(in: JsonSource): String = + JsonParser.parseString(in).toString + } + + implicit val boolean: JsonDecoder[Boolean] = new JsonDecoder[Boolean] { + def unsafeDecode(in: JsonSource): Boolean = + JsonParser.parseBoolean(in) + } + + implicit val int: JsonDecoder[Int] = number(JsonParser.parseInt, _.intValueExact()) + private def number[A]( + f: (JsonSource) => A, + fromBigDecimal: java.math.BigDecimal => A + ): JsonDecoder[A] = + new JsonDecoder[A] { + def unsafeDecode(in: JsonSource): A = + (in.readSkipWhitespace(): @switch) match { + case '"' => + val i = f(in) + JsonParser.char(in, '"') + i + case _ => + in.retract() + f(in) + } + } + + private[json] def builder[A, T[_]]( + in: JsonSource, + elemDecoder: JsonDecoder[A], + builder: scala.collection.mutable.Builder[A, T[A]] + ): T[A] = { + JsonParser.charWithWS(in, '[') + var i: Int = 0 + if JsonParser.firstArrayElement(in) then + while { + { + builder += elemDecoder.unsafeDecode(in) + i += 1 + }; JsonParser.nextArrayElement(in) + } do () + builder.result() + } + +//------------------------------------------------------------ + +private trait DecoderLowPriority1: + this: JsonDecoder.type => + + def seq[A](elemDecoder: JsonDecoder[A]): JsonDecoder[Seq[A]] = new JsonDecoder[Seq[A]] { + def unsafeDecode(in: JsonSource): Seq[A] = + builder(in, elemDecoder, scala.collection.immutable.Seq.newBuilder[A]) + } + +// implicit def list[A: JsonDecoder]: JsonDecoder[List[A]] = new JsonDecoder[List[A]] { +// def unsafeDecode(in: JsonSource): List[A] = +// builder(in, elemDecoder, new scala.collection.mutable.ListBuffer[A]) +// } diff --git a/src/main/scala/co.blocke.scalajack/json2/JsonDecoder.scala b/src/main/scala/co.blocke.scalajack/json/JsonDecoder.scalax similarity index 65% rename from src/main/scala/co.blocke.scalajack/json2/JsonDecoder.scala rename to src/main/scala/co.blocke.scalajack/json/JsonDecoder.scalax index 05d638e3..a1cd7e26 100644 --- a/src/main/scala/co.blocke.scalajack/json2/JsonDecoder.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonDecoder.scalax @@ -1,17 +1,16 @@ package co.blocke.scalajack -package json2 +package json -import json.JsonParseError -import scala.annotation._ +import scala.annotation.* trait JsonDecoder[A]: final def decodeJson(str: CharSequence): Either[JsonParseError, A] = - try Right(unsafeDecode(new JsonReader(str))) + try Right(unsafeDecode(new JsonSource(str))) catch { case jpe: JsonParseError => Left(jpe) } - def unsafeDecode(in: JsonReader): A + def unsafeDecode(in: JsonSource): A //------------------------------------------------------------ @@ -20,23 +19,23 @@ object JsonDecoder extends DecoderLowPriority1: def apply[A](implicit a: JsonDecoder[A]): JsonDecoder[A] = a // Primitive support... - implicit val string: JsonDecoder[String] = new JsonDecoder[String] { - def unsafeDecode(in: JsonReader): String = + implicit val string: JsonDecoder[String] = new JsonDecoder[String] { + def unsafeDecode(in: JsonSource): String = JsonParser.parseString(in).toString } implicit val boolean: JsonDecoder[Boolean] = new JsonDecoder[Boolean] { - def unsafeDecode(in: JsonReader): Boolean = + def unsafeDecode(in: JsonSource): Boolean = JsonParser.parseBoolean(in) } - implicit val int: JsonDecoder[Int] = number(JsonParser.parseInt, _.intValueExact()) + implicit val int: JsonDecoder[Int] = number(JsonParser.parseInt, _.intValueExact()) private def number[A]( - f: (JsonReader) => A, - fromBigDecimal: java.math.BigDecimal => A + f: (JsonSource) => A, + fromBigDecimal: java.math.BigDecimal => A ): JsonDecoder[A] = new JsonDecoder[A] { - def unsafeDecode(in: JsonReader): A = + def unsafeDecode(in: JsonSource): A = (in.readSkipWhitespace(): @switch) match { case '"' => val i = f(in) @@ -48,18 +47,19 @@ object JsonDecoder extends DecoderLowPriority1: } } - private[json2] def builder[A, T[_]]( - in: JsonReader, - builder: scala.collection.mutable.Builder[A, T[A]] + private[json] def builder[A, T[_]]( + in: JsonSource, + builder: scala.collection.mutable.Builder[A, T[A]] )(implicit A: JsonDecoder[A]): T[A] = { JsonParser.charWithWS(in, '[') var i: Int = 0 - if (JsonParser.firstArrayElement(in)) while ({ - { - builder += A.unsafeDecode(in) - i += 1 - }; JsonParser.nextArrayElement(in) - }) () + if JsonParser.firstArrayElement(in) then + while { + { + builder += A.unsafeDecode(in) + i += 1 + }; JsonParser.nextArrayElement(in) + } do () builder.result() } @@ -71,11 +71,11 @@ private trait DecoderLowPriority1: // TODO: Experiment with other Seq *NOT* explicitly provided as separate "implicit def"s. See if this will // convert them to the correct subtype. implicit def seq[A: JsonDecoder]: JsonDecoder[Seq[A]] = new JsonDecoder[Seq[A]] { - def unsafeDecode(in: JsonReader): Seq[A] = + def unsafeDecode(in: JsonSource): Seq[A] = builder(in, scala.collection.immutable.Seq.newBuilder[A]) } implicit def list[A: JsonDecoder]: JsonDecoder[List[A]] = new JsonDecoder[List[A]] { - def unsafeDecode(in: JsonReader): List[A] = + def unsafeDecode(in: JsonSource): List[A] = builder(in, new scala.collection.mutable.ListBuffer[A]) } diff --git a/src/main/scala/co.blocke.scalajack/json/JsonError.scala b/src/main/scala/co.blocke.scalajack/json/JsonError.scala index 848c12c9..22c481e0 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonError.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonError.scala @@ -3,5 +3,22 @@ package json class JsonError(msg: String) extends Throwable -case class JsonParseError(message: String) extends parser.ParseError(message) -case class CommaExpected(message: String = "") extends parser.ParseError(message) +class ParseError(val msg: String) extends Throwable(msg): + val show: String = "" + +// Thrown at compile-time only! +case class JsonTypeError(override val msg: String) extends ParseError(msg): + override val show: String = "" + +// Thrown at runtime only! +case class JsonParseError(override val msg: String, context: JsonSource) extends ParseError(msg + " at position " + context.pos): + override val show: String = + val js = context.js.toString + val (clip, dashes) = context.pos match { + case ep if ep <= 50 && context.max < 80 => (js, ep) + case ep if ep <= 50 => (js.substring(0, 77) + "...", ep) + case ep if ep > 50 && ep + 30 >= context.max => + ("..." + js.substring(context.pos - 49), 52) + case ep => ("..." + js.substring(ep - 49, ep + 27) + "...", 52) + } + msg + s" at positio [${context.pos}]" + "\n" + clip.replaceAll("[\n\t]", "~") + "\n" + ("-" * dashes) + "^" diff --git a/src/main/scala/co.blocke.scalajack/json/JsonParser.scala b/src/main/scala/co.blocke.scalajack/json/JsonParser.scala index cfbdb80f..2c8a62f2 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonParser.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonParser.scala @@ -1,367 +1,162 @@ package co.blocke.scalajack package json -import parser.* -import scala.util.* -import co.blocke.scala_reflection.TypedName -import scala.collection.mutable.ListBuffer - -case class JsonParser(js: String, cfg: JsonConfig): // , cache: Map[TypedName, (JsonConfig, JsonParser) => Either[ParseError, ?]]): +import scala.annotation.* + +object JsonParser: + + private val ull: Array[Char] = "ull".toCharArray + private val alse: Array[Char] = "alse".toCharArray + private val rue: Array[Char] = "rue".toCharArray + + def parseBoolean(in: JsonSource): Boolean = + (in.readSkipWhitespace(): @switch) match { + case 't' => + readChars(in, rue, "true") + true + case 'f' => + readChars(in, alse, "false") + false + case c => + throw JsonParseError(s"Expected true or false value", in) + } - private val jsChars: Array[Char] = js.toCharArray - private var i = 0 - private val max: Int = jsChars.length + def parseInt(in: JsonSource): Int = { + checkNumber(in) + try { + val i = UnsafeNumbers.int_(in, false) + in.retract() + i + } catch { + case UnsafeNumbers.UnsafeNumber => throw JsonParseError("Expected an Int", in) + } + } - def getPos = i + def parseString(in: JsonSource): CharSequence = + charWithWS(in, '"') + val sb = new FastStringBuilder(64) + while true do + val c = in.readEscapedString() + if c == END_OF_STRING then return sb.buffer // mutable thing escapes, but cannot be changed + sb.append(c.toChar) + throw JsonParseError("Invalid string value detected", in) + + // Returns index of field name read in, or -1 if not found + def parseField(in: JsonSource, fieldMatrix: StringMatrix): Int = + val f = enumeration(in, fieldMatrix) + charWithWS(in, ':') + f + + // True if we got anything besides a ], False for ] + def firstArrayElement(in: JsonSource): Boolean = + (in.readSkipWhitespace(): @switch) match + case ']' => false + case _ => + in.retract() + true + + def nextArrayElement(in: JsonSource): Boolean = + (in.readSkipWhitespace(): @switch) match + case ',' => true + case ']' => false + case c => throw JsonParseError(s"expected ',' or ']' got '$c'", in) + + // True if we got a string (implies a retraction), False for } + def firstField(in: JsonSource): Boolean = + (in.readSkipWhitespace(): @switch) match { + case '"' => true + case '}' => false + case c => + throw JsonParseError(s"expected string or '}' got '$c'", in) + } - // Housekeeping - // ------------------------------ - @inline def nullCheck: Boolean = - val res = i + 3 < max && jsChars(i) == 'n' && jsChars(i + 1) == 'u' && jsChars(i + 2) == 'l' && jsChars(i + 3) == 'l' - if res then i += 4 - res - @inline def eatWhitespace: Either[ParseError, Unit] = - while i < max && jsChars(i).isWhitespace do i += 1 - Right(()) - @inline def expectQuote: Either[ParseError, Unit] = - if jsChars(i) == '\"' then - i += 1 - Right(()) - else Left(JsonParseError(showError(s"Quote expected at position [$i]"))) - @inline def expectComma: Either[ParseError, Unit] = // Note: this consumes whitespace before/after the ',' - for { - _ <- eatWhitespace - r <- - if jsChars(i) == ',' then - i += 1 - eatWhitespace - Right(()) - else Left(CommaExpected(showError(s"Comma expected at position [$i]"))) - } yield r - @inline def expectColon: Either[ParseError, Unit] = // Note: this consumes whitespace before/after the ':' - for { - _ <- eatWhitespace - r <- - if jsChars(i) == ':' then - i += 1 - eatWhitespace - Right(()) - else Left(JsonParseError(showError(s"Expected colon at position [$i]"))) - } yield r + // True if we got a comma, and False for } + def nextField(in: JsonSource): Boolean = + (in.readSkipWhitespace(): @switch) match { + case ',' => + charWithWS(in, '"') + true + case '}' => false + case c => + throw JsonParseError(s"expected ',' or '}' got '$c'", in) + } - // JSON label (i.e. object key, which is always a simple string, ie no escaped/special chars) - def expectLabel: Either[ParseError, String] = - jsChars(i) match + def skipValue(in: JsonSource): Unit = + (in.readSkipWhitespace(): @switch) match { + case 'n' => readChars(in, ull, "null") + case 'f' => readChars(in, alse, "false") + case 't' => readChars(in, rue, "true") + case '{' => + if firstField(in) then { + while { + { + char(in, '"') + skipString(in) + char(in, ':') + skipValue(in) + }; nextField(in) + } do () + } + case '[' => + if firstArrayElement(in) then { + while { skipValue(in); nextArrayElement(in) } do () + } case '"' => - i += 1 - val mark = i - while i < max && jsChars(i) != '"' do i += 1 - i += 1 - Right(js.substring(mark, i - 1)) - case x => Left(JsonParseError(showError(s"Unexpected character '$x' where beginning of label expected at position [$i]"))) - - // Data Types - // ------------------------------ - def expectBoolean(isMapKey: Boolean = false): Either[ParseError, Boolean] = - jsChars(i) match - case 't' if i + 3 < max && jsChars(i + 1) == 'r' && jsChars(i + 2) == 'u' && jsChars(i + 3) == 'e' => - i += 4 - Right(true) - case 'f' if i + 4 < max && jsChars(i + 1) == 'a' && jsChars(i + 2) == 'l' && jsChars(i + 3) == 's' && jsChars(i + 4) == 'e' => - i += 5 - Right(false) - case x => - if (isMapKey || cfg.permissivePrimitives) && jsChars(i) == '"' then - for { - _ <- expectQuote - result <- expectBoolean() - _ <- expectQuote - } yield result - else Left(JsonParseError(showError(s"Unexpected character '$x' where beginning of boolean value expected at position [$i]"))) - - def expectLong(isMapKey: Boolean = false): Either[ParseError, Long] = - val mark = i - var done = false - while !done do - jsChars(i) match - case c if (c >= '0' && c <= '9') || c == '-' => i += 1 - case _ => done = true - Try(js.substring(mark, i).toLong) match - case Success(g) => Right(g) - case Failure(f) => - if (isMapKey || cfg.permissivePrimitives) && jsChars(i) == '"' then - for { - _ <- expectQuote - result <- expectLong() - _ <- expectQuote - } yield result - else - val msg = - if mark == i then s"""Int/Long expected but couldn't parse from "${jsChars(i)}" at position [$i]""" - else s"""Int/Long expected but couldn't parse from "${js.substring(mark, i)}" at position [$i]""" - i = mark - Left(JsonParseError(showError(msg))) - - def expectBigLong(isMapKey: Boolean = false): Either[ParseError, BigInt] = - nullCheck match - case true if cfg.forbidNullsInInput => Left(JsonParseError(showError(s"Forbidden 'null' value received at position [$i]"))) - case true => Right(null.asInstanceOf[BigInt]) - case false => - val mark = i - var done = false - while !done do - jsChars(i) match - case c if (c >= '0' && c <= '9') || c == '-' => i += 1 - case _ => done = true - Try(BigInt(js.substring(mark, i))) match - case Success(g) => Right(g) - case Failure(f) => - if (isMapKey || cfg.permissivePrimitives) && jsChars(i) == '"' then - for { - _ <- expectQuote - result <- expectBigLong() - _ <- expectQuote - } yield result - else - val msg = - if mark == i then s"""Int/Long expected but couldn't parse from "${jsChars(i)}" at position [$i]""" - else s"""Int/Long expected but couldn't parse from "${js.substring(mark, i)}" at position [$i]""" - i = mark - Left(JsonParseError(showError(msg))) - - def expectDouble(isMapKey: Boolean = false): Either[ParseError, Double] = - val mark = i - var done = false - while !done do - jsChars(i) match - case c if (c >= '0' && c <= '9') || c == '-' || c == '.' || c == 'e' || c == 'E' || c == '+' => i += 1 - case _ => done = true - Try(js.substring(mark, i).toDouble) match - case Success(g) => Right(g) - case Failure(_) => - if (isMapKey || cfg.permissivePrimitives) && jsChars(i) == '"' then - for { - _ <- expectQuote - result <- expectDouble() - _ <- expectQuote - } yield result - else - val msg = - if mark == i then s"Float/Double expected but couldn't parse from \"${jsChars(i)}\" at position [$i]" - else s"Float/Double expected but couldn't parse from \"${js.substring(mark, i)}\" at position [$i]" - i = mark - Left(JsonParseError(showError(msg))) - - def expectBigDouble(isMapKey: Boolean = false): Either[ParseError, BigDecimal] = - nullCheck match - case true if cfg.forbidNullsInInput => Left(JsonParseError(showError(s"Forbidden 'null' value received at position [$i]"))) - case true => Right(null.asInstanceOf[BigDecimal]) - case false => - val mark = i - var done = false - while !done do - jsChars(i) match - case c if (c >= '0' && c <= '9') || c == '-' || c == '.' || c == 'e' || c == 'E' || c == '+' => i += 1 - case _ => done = true - Try(BigDecimal(js.substring(mark, i))) match - case Success(g) => Right(g) - case Failure(_) => - if (isMapKey || cfg.permissivePrimitives) && jsChars(i) == '"' then - for { - _ <- expectQuote - result <- expectBigDouble() - _ <- expectQuote - } yield result - else - val msg = - if mark == i then s"Float/Double expected but couldn't parse from \"${jsChars(i)}\" at position [$i]" - else s"Float/Double expected but couldn't parse from \"${js.substring(mark, i)}\" at position [$i]" - i = mark - Left(JsonParseError(showError(msg))) - - def expectString(): Either[ParseError, String] = - nullCheck match - case true if cfg.forbidNullsInInput => Left(JsonParseError(showError(s"Forbidden 'null' value received at position [$i]"))) - case true => Right(null.asInstanceOf[String]) - case false => - jsChars(i) match - case '"' => - i += 1 - val mark = i // save position in case we need complex string parse - var captured: Option[String] = None - while i < max && jsChars(i) != '"' do - jsChars(i) match - case '\\' => // oops! special char found--do slow string parse - i = mark - captured = Some(_expectString) - case _ => i += 1 - i += 1 - Right(captured.getOrElse(js.substring(mark, i - 1))) - case x => Left(JsonParseError(showError(s"Unexpected character '$x' where beginning of a string expected at position [$i]"))) - - def expectList[T](expectElement: () => Either[ParseError, T]): Either[ParseError, List[T]] = - nullCheck match - case true if cfg.forbidNullsInInput => Left(JsonParseError(showError(s"Forbidden 'null' value received at position [$i]"))) - case true => Right(null.asInstanceOf[List[T]]) - case false => - if jsChars(i) != '[' then Left(JsonParseError(showError(s"Beginning of list expected at position [$i]"))) - else - i += 1 - eatWhitespace - val acc = ListBuffer.empty[T] - var done: Option[Either[ParseError, List[T]]] = None - while done.isEmpty do - (for { - el <- expectElement() - _ = acc.addOne(el) - _ <- expectComma - } yield el) match - case Left(CommaExpected(_)) if jsChars(i) == ']' => - i += 1 - eatWhitespace - done = Some(Right(acc.toList)) - case Left(e) => - done = Some(Left(e)) - case Right(_) => - done.get + skipString(in) + case '-' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '.' => + skipNumber(in) + case c => throw JsonParseError(s"Unexpected '$c'", in) + } - def expectTuple( - tupleFns: List[(JsonConfig, JsonParser) => Either[ParseError, ?]] - ): Either[ParseError, List[?]] = - if jsChars(i) != '[' then Left(JsonParseError(showError(s"Beginning of tuple expected at position [$i]"))) - else - i += 1 - eatWhitespace - val buf = ListBuffer.empty[Any] - tupleFns - .foldLeft(Right(buf).asInstanceOf[Either[ParseError, ListBuffer[Any]]]) { (acc, fn) => - acc.flatMap(accumulator => - for { - el <- fn(cfg, this) - newAcc = accumulator.addOne(el) - _ <- expectComma - } yield newAcc - ) - } match - case Left(CommaExpected(_)) if buf.size == tupleFns.size && jsChars(i) == ']' => - i += 1 - eatWhitespace - Right(buf.toList) - case Left(CommaExpected(_)) if jsChars(i) == ']' => Left(JsonParseError(showError(s"Missing required elements in tuple at position [$i]"))) - case Left(e: ParseError) => Left(e) - case Right(_) => Left(JsonParseError(showError(s"Extra/unexpected tuple fields at position [$i]"))) + def skipNumber(in: JsonSource): Unit = { + while isNumber(in.read()) do {} + in.retract() + } - def expectObject[K, V]( - keyElement: (JsonConfig, JsonParser) => Either[ParseError, K], - valueElement: (JsonConfig, JsonParser) => Either[ParseError, V] - ): Either[ParseError, Map[K, V]] = - if jsChars(i) != '{' then Left(JsonParseError(showError(s"Beginning of object expected at position [$i]"))) - else + def skipString(in: JsonSource): Unit = + var i: Int = 0 + while { i = in.readEscapedString(); i != -1 } do () + + private def checkNumber(in: JsonSource): Unit = + (in.readSkipWhitespace(): @switch) match + case '-' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '.' => () + case c => throw JsonParseError(s"Expected a number, got $c", in) + in.retract() + + @inline private[this] def isNumber(c: Char): Boolean = + (c: @switch) match + case '+' | '-' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '.' | 'e' | 'E' => true + case _ => false + + private inline def readChars( + in: JsonSource, + expect: Array[Char], + errMsg: String + ): Unit = + var i: Int = 0 + while i < expect.length do + if in.read() != expect(i) then throw JsonParseError(s"Expected $errMsg", in) i += 1 - eatWhitespace - val acc = scala.collection.mutable.Map.empty[K, V] - var done: Option[Either[ParseError, Map[K, V]]] = None - while done.isEmpty do - (for { - keyLabel <- keyElement(cfg, this) - _ <- expectColon - mapVal <- valueElement(cfg, this) - _ = acc.put(keyLabel, mapVal) - _ <- expectComma - } yield (keyLabel, mapVal)) match - case Left(CommaExpected(_)) if jsChars(i) == '}' => - i += 1 - eatWhitespace - done = Some(Right(acc.toMap)) - case Left(e) => - done = Some(Left(e)) - case Right(_) => - done.get - // Special case of JSON object where each entry is a field of a class - def expectClass( - fieldMap: Map[String, (JsonConfig, JsonParser) => Either[ParseError, ?]], - fieldValues: scala.collection.mutable.HashMap[String, Any] // pre-set values (Option:None, default values) - ): Either[ParseError, Map[String, ?]] = - if jsChars(i) != '{' then Left(JsonParseError(showError(s"Beginning of class object expected at position [$i]"))) - else + @inline def charWithWS(in: JsonSource, c: Char): Unit = + val got = in.readSkipWhitespace() + if got != c then throw JsonParseError(s"Expected '$c' got '$got'", in) + + @inline def char(in: JsonSource, c: Char): Unit = + val got = in.read() + if got != c then throw JsonParseError(s"Expected '$c' got '$got'", in) + + def enumeration( + gen: JsonSource, + matrix: StringMatrix + ): Int = { + var i: Int = 0 + var bs: Long = matrix.initial + var c: Int = -1 + while { c = gen.readEscapedString(); c != END_OF_STRING } do { + bs = matrix.update(bs, i, c) i += 1 - eatWhitespace - var done: Option[Either[ParseError, Map[String, ?]]] = None - while done.isEmpty do - (for { - fieldLabel <- expectLabel - _ <- expectColon - fieldValue <- fieldMap(fieldLabel)(cfg, this) - _ = fieldValues.put(fieldLabel, fieldValue) - _ <- expectComma - } yield fieldValue) match - case Left(CommaExpected(_)) if jsChars(i) == '}' => - i += 1 - eatWhitespace - done = Some(Right(fieldValues.toMap)) - case Left(e) => - done = Some(Left(e)) - case Right(_) => - done.get - - // Slower String parsing that handles special escaped chars - private def _expectString = - val builder = new java.lang.StringBuilder() - while i < max && jsChars(i) != '"' do - if jsChars(i) == '\\' then { - jsChars(i + 1) match { - case '"' => - builder.append('\"') - i += 2 - - case '\\' => - builder.append('\\') - i += 2 - - case 'b' => - builder.append('\b') - i += 2 - - case 'f' => - builder.append('\f') - i += 2 - - case 'n' => - builder.append('\n') - i += 2 - - case 'r' => - builder.append('\r') - i += 2 - - case 't' => - builder.append('\t') - i += 2 - - case 'u' => - val hexEncoded = js.substring(i + 2, i + 6) - val unicodeChar = Integer.parseInt(hexEncoded, 16).toChar - builder.append(unicodeChar.toString) - i += 6 - - case c => - builder.append(c) - i += 2 - } - } else { - builder.append(jsChars(i)) - i += 1 - } - builder.toString - - def showError(msg: String): String = { - val (clip, dashes) = i match { - case ep if ep <= 50 && max < 80 => (js, ep) - case ep if ep <= 50 => (js.substring(0, 77) + "...", ep) - case ep if ep > 50 && ep + 30 >= max => - ("..." + js.substring(i - 49), 52) - case ep => ("..." + js.substring(ep - 49, ep + 27) + "...", 52) } - msg + s" at positio [$i]" + "\n" + clip.replaceAll("[\n\t]", "~") + "\n" + ("-" * dashes) + "^" + bs = matrix.exact(bs, i) + matrix.first(bs) } diff --git a/src/main/scala/co.blocke.scalajack/json/JsonParser.scalax b/src/main/scala/co.blocke.scalajack/json/JsonParser.scalax new file mode 100644 index 00000000..cfbdb80f --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/JsonParser.scalax @@ -0,0 +1,367 @@ +package co.blocke.scalajack +package json + +import parser.* +import scala.util.* +import co.blocke.scala_reflection.TypedName +import scala.collection.mutable.ListBuffer + +case class JsonParser(js: String, cfg: JsonConfig): // , cache: Map[TypedName, (JsonConfig, JsonParser) => Either[ParseError, ?]]): + + private val jsChars: Array[Char] = js.toCharArray + private var i = 0 + private val max: Int = jsChars.length + + def getPos = i + + // Housekeeping + // ------------------------------ + @inline def nullCheck: Boolean = + val res = i + 3 < max && jsChars(i) == 'n' && jsChars(i + 1) == 'u' && jsChars(i + 2) == 'l' && jsChars(i + 3) == 'l' + if res then i += 4 + res + @inline def eatWhitespace: Either[ParseError, Unit] = + while i < max && jsChars(i).isWhitespace do i += 1 + Right(()) + @inline def expectQuote: Either[ParseError, Unit] = + if jsChars(i) == '\"' then + i += 1 + Right(()) + else Left(JsonParseError(showError(s"Quote expected at position [$i]"))) + @inline def expectComma: Either[ParseError, Unit] = // Note: this consumes whitespace before/after the ',' + for { + _ <- eatWhitespace + r <- + if jsChars(i) == ',' then + i += 1 + eatWhitespace + Right(()) + else Left(CommaExpected(showError(s"Comma expected at position [$i]"))) + } yield r + @inline def expectColon: Either[ParseError, Unit] = // Note: this consumes whitespace before/after the ':' + for { + _ <- eatWhitespace + r <- + if jsChars(i) == ':' then + i += 1 + eatWhitespace + Right(()) + else Left(JsonParseError(showError(s"Expected colon at position [$i]"))) + } yield r + + // JSON label (i.e. object key, which is always a simple string, ie no escaped/special chars) + def expectLabel: Either[ParseError, String] = + jsChars(i) match + case '"' => + i += 1 + val mark = i + while i < max && jsChars(i) != '"' do i += 1 + i += 1 + Right(js.substring(mark, i - 1)) + case x => Left(JsonParseError(showError(s"Unexpected character '$x' where beginning of label expected at position [$i]"))) + + // Data Types + // ------------------------------ + def expectBoolean(isMapKey: Boolean = false): Either[ParseError, Boolean] = + jsChars(i) match + case 't' if i + 3 < max && jsChars(i + 1) == 'r' && jsChars(i + 2) == 'u' && jsChars(i + 3) == 'e' => + i += 4 + Right(true) + case 'f' if i + 4 < max && jsChars(i + 1) == 'a' && jsChars(i + 2) == 'l' && jsChars(i + 3) == 's' && jsChars(i + 4) == 'e' => + i += 5 + Right(false) + case x => + if (isMapKey || cfg.permissivePrimitives) && jsChars(i) == '"' then + for { + _ <- expectQuote + result <- expectBoolean() + _ <- expectQuote + } yield result + else Left(JsonParseError(showError(s"Unexpected character '$x' where beginning of boolean value expected at position [$i]"))) + + def expectLong(isMapKey: Boolean = false): Either[ParseError, Long] = + val mark = i + var done = false + while !done do + jsChars(i) match + case c if (c >= '0' && c <= '9') || c == '-' => i += 1 + case _ => done = true + Try(js.substring(mark, i).toLong) match + case Success(g) => Right(g) + case Failure(f) => + if (isMapKey || cfg.permissivePrimitives) && jsChars(i) == '"' then + for { + _ <- expectQuote + result <- expectLong() + _ <- expectQuote + } yield result + else + val msg = + if mark == i then s"""Int/Long expected but couldn't parse from "${jsChars(i)}" at position [$i]""" + else s"""Int/Long expected but couldn't parse from "${js.substring(mark, i)}" at position [$i]""" + i = mark + Left(JsonParseError(showError(msg))) + + def expectBigLong(isMapKey: Boolean = false): Either[ParseError, BigInt] = + nullCheck match + case true if cfg.forbidNullsInInput => Left(JsonParseError(showError(s"Forbidden 'null' value received at position [$i]"))) + case true => Right(null.asInstanceOf[BigInt]) + case false => + val mark = i + var done = false + while !done do + jsChars(i) match + case c if (c >= '0' && c <= '9') || c == '-' => i += 1 + case _ => done = true + Try(BigInt(js.substring(mark, i))) match + case Success(g) => Right(g) + case Failure(f) => + if (isMapKey || cfg.permissivePrimitives) && jsChars(i) == '"' then + for { + _ <- expectQuote + result <- expectBigLong() + _ <- expectQuote + } yield result + else + val msg = + if mark == i then s"""Int/Long expected but couldn't parse from "${jsChars(i)}" at position [$i]""" + else s"""Int/Long expected but couldn't parse from "${js.substring(mark, i)}" at position [$i]""" + i = mark + Left(JsonParseError(showError(msg))) + + def expectDouble(isMapKey: Boolean = false): Either[ParseError, Double] = + val mark = i + var done = false + while !done do + jsChars(i) match + case c if (c >= '0' && c <= '9') || c == '-' || c == '.' || c == 'e' || c == 'E' || c == '+' => i += 1 + case _ => done = true + Try(js.substring(mark, i).toDouble) match + case Success(g) => Right(g) + case Failure(_) => + if (isMapKey || cfg.permissivePrimitives) && jsChars(i) == '"' then + for { + _ <- expectQuote + result <- expectDouble() + _ <- expectQuote + } yield result + else + val msg = + if mark == i then s"Float/Double expected but couldn't parse from \"${jsChars(i)}\" at position [$i]" + else s"Float/Double expected but couldn't parse from \"${js.substring(mark, i)}\" at position [$i]" + i = mark + Left(JsonParseError(showError(msg))) + + def expectBigDouble(isMapKey: Boolean = false): Either[ParseError, BigDecimal] = + nullCheck match + case true if cfg.forbidNullsInInput => Left(JsonParseError(showError(s"Forbidden 'null' value received at position [$i]"))) + case true => Right(null.asInstanceOf[BigDecimal]) + case false => + val mark = i + var done = false + while !done do + jsChars(i) match + case c if (c >= '0' && c <= '9') || c == '-' || c == '.' || c == 'e' || c == 'E' || c == '+' => i += 1 + case _ => done = true + Try(BigDecimal(js.substring(mark, i))) match + case Success(g) => Right(g) + case Failure(_) => + if (isMapKey || cfg.permissivePrimitives) && jsChars(i) == '"' then + for { + _ <- expectQuote + result <- expectBigDouble() + _ <- expectQuote + } yield result + else + val msg = + if mark == i then s"Float/Double expected but couldn't parse from \"${jsChars(i)}\" at position [$i]" + else s"Float/Double expected but couldn't parse from \"${js.substring(mark, i)}\" at position [$i]" + i = mark + Left(JsonParseError(showError(msg))) + + def expectString(): Either[ParseError, String] = + nullCheck match + case true if cfg.forbidNullsInInput => Left(JsonParseError(showError(s"Forbidden 'null' value received at position [$i]"))) + case true => Right(null.asInstanceOf[String]) + case false => + jsChars(i) match + case '"' => + i += 1 + val mark = i // save position in case we need complex string parse + var captured: Option[String] = None + while i < max && jsChars(i) != '"' do + jsChars(i) match + case '\\' => // oops! special char found--do slow string parse + i = mark + captured = Some(_expectString) + case _ => i += 1 + i += 1 + Right(captured.getOrElse(js.substring(mark, i - 1))) + case x => Left(JsonParseError(showError(s"Unexpected character '$x' where beginning of a string expected at position [$i]"))) + + def expectList[T](expectElement: () => Either[ParseError, T]): Either[ParseError, List[T]] = + nullCheck match + case true if cfg.forbidNullsInInput => Left(JsonParseError(showError(s"Forbidden 'null' value received at position [$i]"))) + case true => Right(null.asInstanceOf[List[T]]) + case false => + if jsChars(i) != '[' then Left(JsonParseError(showError(s"Beginning of list expected at position [$i]"))) + else + i += 1 + eatWhitespace + val acc = ListBuffer.empty[T] + var done: Option[Either[ParseError, List[T]]] = None + while done.isEmpty do + (for { + el <- expectElement() + _ = acc.addOne(el) + _ <- expectComma + } yield el) match + case Left(CommaExpected(_)) if jsChars(i) == ']' => + i += 1 + eatWhitespace + done = Some(Right(acc.toList)) + case Left(e) => + done = Some(Left(e)) + case Right(_) => + done.get + + def expectTuple( + tupleFns: List[(JsonConfig, JsonParser) => Either[ParseError, ?]] + ): Either[ParseError, List[?]] = + if jsChars(i) != '[' then Left(JsonParseError(showError(s"Beginning of tuple expected at position [$i]"))) + else + i += 1 + eatWhitespace + val buf = ListBuffer.empty[Any] + tupleFns + .foldLeft(Right(buf).asInstanceOf[Either[ParseError, ListBuffer[Any]]]) { (acc, fn) => + acc.flatMap(accumulator => + for { + el <- fn(cfg, this) + newAcc = accumulator.addOne(el) + _ <- expectComma + } yield newAcc + ) + } match + case Left(CommaExpected(_)) if buf.size == tupleFns.size && jsChars(i) == ']' => + i += 1 + eatWhitespace + Right(buf.toList) + case Left(CommaExpected(_)) if jsChars(i) == ']' => Left(JsonParseError(showError(s"Missing required elements in tuple at position [$i]"))) + case Left(e: ParseError) => Left(e) + case Right(_) => Left(JsonParseError(showError(s"Extra/unexpected tuple fields at position [$i]"))) + + def expectObject[K, V]( + keyElement: (JsonConfig, JsonParser) => Either[ParseError, K], + valueElement: (JsonConfig, JsonParser) => Either[ParseError, V] + ): Either[ParseError, Map[K, V]] = + if jsChars(i) != '{' then Left(JsonParseError(showError(s"Beginning of object expected at position [$i]"))) + else + i += 1 + eatWhitespace + val acc = scala.collection.mutable.Map.empty[K, V] + var done: Option[Either[ParseError, Map[K, V]]] = None + while done.isEmpty do + (for { + keyLabel <- keyElement(cfg, this) + _ <- expectColon + mapVal <- valueElement(cfg, this) + _ = acc.put(keyLabel, mapVal) + _ <- expectComma + } yield (keyLabel, mapVal)) match + case Left(CommaExpected(_)) if jsChars(i) == '}' => + i += 1 + eatWhitespace + done = Some(Right(acc.toMap)) + case Left(e) => + done = Some(Left(e)) + case Right(_) => + done.get + + // Special case of JSON object where each entry is a field of a class + def expectClass( + fieldMap: Map[String, (JsonConfig, JsonParser) => Either[ParseError, ?]], + fieldValues: scala.collection.mutable.HashMap[String, Any] // pre-set values (Option:None, default values) + ): Either[ParseError, Map[String, ?]] = + if jsChars(i) != '{' then Left(JsonParseError(showError(s"Beginning of class object expected at position [$i]"))) + else + i += 1 + eatWhitespace + var done: Option[Either[ParseError, Map[String, ?]]] = None + while done.isEmpty do + (for { + fieldLabel <- expectLabel + _ <- expectColon + fieldValue <- fieldMap(fieldLabel)(cfg, this) + _ = fieldValues.put(fieldLabel, fieldValue) + _ <- expectComma + } yield fieldValue) match + case Left(CommaExpected(_)) if jsChars(i) == '}' => + i += 1 + eatWhitespace + done = Some(Right(fieldValues.toMap)) + case Left(e) => + done = Some(Left(e)) + case Right(_) => + done.get + + // Slower String parsing that handles special escaped chars + private def _expectString = + val builder = new java.lang.StringBuilder() + while i < max && jsChars(i) != '"' do + if jsChars(i) == '\\' then { + jsChars(i + 1) match { + case '"' => + builder.append('\"') + i += 2 + + case '\\' => + builder.append('\\') + i += 2 + + case 'b' => + builder.append('\b') + i += 2 + + case 'f' => + builder.append('\f') + i += 2 + + case 'n' => + builder.append('\n') + i += 2 + + case 'r' => + builder.append('\r') + i += 2 + + case 't' => + builder.append('\t') + i += 2 + + case 'u' => + val hexEncoded = js.substring(i + 2, i + 6) + val unicodeChar = Integer.parseInt(hexEncoded, 16).toChar + builder.append(unicodeChar.toString) + i += 6 + + case c => + builder.append(c) + i += 2 + } + } else { + builder.append(jsChars(i)) + i += 1 + } + builder.toString + + def showError(msg: String): String = { + val (clip, dashes) = i match { + case ep if ep <= 50 && max < 80 => (js, ep) + case ep if ep <= 50 => (js.substring(0, 77) + "...", ep) + case ep if ep > 50 && ep + 30 >= max => + ("..." + js.substring(i - 49), 52) + case ep => ("..." + js.substring(ep - 49, ep + 27) + "...", 52) + } + msg + s" at positio [$i]" + "\n" + clip.replaceAll("[\n\t]", "~") + "\n" + ("-" * dashes) + "^" + } diff --git a/src/main/scala/co.blocke.scalajack/json/JsonParser2.scala b/src/main/scala/co.blocke.scalajack/json/JsonParser2.scala deleted file mode 100644 index 2922ab5d..00000000 --- a/src/main/scala/co.blocke.scalajack/json/JsonParser2.scala +++ /dev/null @@ -1,219 +0,0 @@ -package co.blocke.scalajack -package json - -import parser.* - -import scala.util.* -import co.blocke.scala_reflection.TypedName -import scala.collection.mutable.{ListBuffer, HashMap} - -case class JsonParser2(js: String, cfg: JsonConfig) extends Parser: - - //-------------------------------------- - //-------------------------------------- - // JSON Housekeeping - //-------------------------------------- - //-------------------------------------- - - private val jsChars: Array[Char] = js.toCharArray - private var i = 0 - private val max: Int = jsChars.length - - inline def eatWhitespace = - while i < max && jsChars(i).isWhitespace do i += 1 - - inline def nullCheck: Boolean = - if i + 3 < max && jsChars(i) == 'n' && jsChars(i + 1) == 'u' && jsChars(i + 2) == 'l' && jsChars(i + 3) == 'l' then - i += 4 - true - else - false - - inline def expectComma: Either[ParseError, Unit] = // Note: this consumes whitespace before/after the ',' - eatWhitespace - if jsChars(i) == ',' then - i += 1 - eatWhitespace - Right(()) - else - Left(CommaExpected(showError(s"Comma expected at position [$i]"))) - - inline def expectColon: Either[ParseError, Unit] = // Note: this consumes whitespace before/after the ':' - eatWhitespace - if jsChars(i) == ':' then - i += 1 - eatWhitespace - Right(()) - else - Left(JsonParseError(showError(s"Expected colon at position [$i]"))) - - // JSON label (i.e. object key, which is always a simple string, ie no escaped/special chars) - inline def expectLabel: Either[ParseError, String] = - jsChars(i) match - case '"' => - i += 1 - val mark = i - while i < max && jsChars(i) != '"' do i += 1 - i += 1 - Right(js.substring(mark, i - 1)) - case x => Left(JsonParseError(showError(s"Unexpected character '$x' where beginning of label expected at position [$i]"))) - - //-------------------------------------- - //-------------------------------------- - // Parser Implementation - //-------------------------------------- - //-------------------------------------- - inline def parseBoolean(): Either[ParseError, Boolean] = - jsChars(i) match - case 't' if i + 3 < max && jsChars(i + 1) == 'r' && jsChars(i + 2) == 'u' && jsChars(i + 3) == 'e' => - i += 4 - Right(true) - case 'f' if i + 4 < max && jsChars(i + 1) == 'a' && jsChars(i + 2) == 'l' && jsChars(i + 3) == 's' && jsChars(i + 4) == 'e' => - i += 5 - Right(false) - case x => - // if (isMapKey || cfg.permissivePrimitives) && jsChars(i) == '"' then - // for { - // _ <- expectQuote - // result <- expectBoolean() - // _ <- expectQuote - // } yield result - // else - Left(JsonParseError(showError(s"Unexpected character '$x' where beginning of boolean value expected at position [$i]"))) - - - inline def parseString(): Either[ParseError, String] = - nullCheck match - case true if cfg.forbidNullsInInput => Left(JsonParseError(showError(s"Forbidden 'null' value received at position [$i]"))) - case true => Right(null.asInstanceOf[String]) - case false => - jsChars(i) match - case '"' => - i += 1 - val mark = i // save position in case we need complex string parse - var captured: Option[String] = None - while i < max && jsChars(i) != '"' do - jsChars(i) match - case '\\' => // oops! special char found--do slow string parse - i = mark - captured = Some(_expectString) - case _ => i += 1 - i += 1 - Right(captured.getOrElse(js.substring(mark, i - 1))) - case x => Left(JsonParseError(showError(s"Unexpected character '$x' where beginning of a string expected at position [$i]"))) - - inline def parseLong(): Either[ParseError, Long] = - val mark = i - var done = false - while !done do - jsChars(i) match - case c if (c >= '0' && c <= '9') || c == '-' => i += 1 - case _ => done = true - Try(js.substring(mark, i).toLong) match - case Success(g) => Right(g) - case Failure(f) => - // if (isMapKey || cfg.permissivePrimitives) && jsChars(i) == '"' then - // for { - // _ <- expectQuote - // result <- expectLong() - // _ <- expectQuote - // } yield result - // else - val msg = - if mark == i then s"""Int/Long expected but couldn't parse from "${jsChars(i)}" at position [$i]""" - else s"""Int/Long expected but couldn't parse from "${js.substring(mark, i)}" at position [$i]""" - i = mark - Left(JsonParseError(showError(msg))) - - def parseList(inst: Instruction): Either[ParseError, List[inst.Z]] = - nullCheck match - case true if cfg.forbidNullsInInput => Left(JsonParseError(showError(s"Forbidden 'null' value received at position [$i]"))) - case true => Right(null) - case false => - if jsChars(i) != '[' then Left(JsonParseError(showError(s"Beginning of list expected at position [$i]"))) - else - i += 1 - eatWhitespace - val acc = ListBuffer.empty[inst.Z] - var done: Option[Either[ParseError, List[inst.Z]]] = None - while done.isEmpty do - (for { - el <- parse(inst) - _ = acc.addOne(el) - _ <- expectComma - } yield el) match - case Left(CommaExpected(_)) if jsChars(i) == ']' => - i += 1 - eatWhitespace - done = Some(Right(acc.toList)) - case Left(e) => - done = Some(Left(e)) - case Right(_) => - done.get - - // Special case of JSON object where each entry is a field of a class - def parseClass(inst: Map[String,Instruction], fieldValues: HashMap[String,Any]): Either[ParseError, Map[String,Any]] = - if jsChars(i) != '{' then Left(JsonParseError(showError(s"Beginning of class object expected at position [$i]"))) - else - i += 1 - eatWhitespace - var done: Option[Either[ParseError, Map[String, ?]]] = None - while done.isEmpty do - (for { - fieldLabel <- expectLabel - _ <- expectColon - fieldValue <- inst.get(fieldLabel).map(parse(_)).getOrElse(null) - _ = fieldValues.put(fieldLabel, fieldValue) - _ <- expectComma - } yield fieldValue) match - case Left(CommaExpected(_)) if jsChars(i) == '}' => - i += 1 - eatWhitespace - done = Some(Right(fieldValues.toMap)) - case Left(e) => - done = Some(Left(e)) - case Right(_) => - done.get - - //-------------------------------------- - //-------------------------------------- - // Utility Functions - //-------------------------------------- - //-------------------------------------- - - // Slower String parsing that handles special escaped chars - private def _expectString = - val builder = new java.lang.StringBuilder() - while i < max && jsChars(i) != '"' do - if jsChars(i) == '\\' then { - jsChars(i + 1) match { - case c @ ('"' | '\\' | 'b' | 'f' | 'n' | 'r' | 't') => - builder.append(c) - i += 2 - - case 'u' => - val hexEncoded = js.substring(i + 2, i + 6) - val unicodeChar = Integer.parseInt(hexEncoded, 16).toChar - builder.append(unicodeChar.toString) - i += 6 - - case c => - builder.append(c) - i += 2 - } - } else { - builder.append(jsChars(i)) - i += 1 - } - builder.toString - - def showError(msg: String): String = { - val (clip, dashes) = i match { - case ep if ep <= 50 && max < 80 => (js, ep) - case ep if ep <= 50 => (js.substring(0, 77) + "...", ep) - case ep if ep > 50 && ep + 30 >= max => - ("..." + js.substring(i - 49), 52) - case ep => ("..." + js.substring(ep - 49, ep + 27) + "...", 52) - } - msg + s" at position [$i]" + "\n" + clip.replaceAll("[\n\t]", "~") + "\n" + ("-" * dashes) + "^" - } diff --git a/src/main/scala/co.blocke.scalajack/json/JsonParser3.scala b/src/main/scala/co.blocke.scalajack/json/JsonParser3.scala deleted file mode 100644 index 260f800b..00000000 --- a/src/main/scala/co.blocke.scalajack/json/JsonParser3.scala +++ /dev/null @@ -1,188 +0,0 @@ -package co.blocke.scalajack -package json - -import scala.util.{Try, Success} -import scala.annotation._ - -case class JsonParser3( js: String ): - - trait Terminal - sealed trait JSON - - case class StringJson( b: Int, e: Int ) extends JSON: - def get: String = js.substring(b,e) - case class BooleanJson( v: Boolean ) extends JSON - case class IntJson( v: Long ) extends JSON - case class FloatJson( v: Double ) extends JSON - case class ObjectJson( v: Map[String, JSON] ) extends JSON - case class ArrayJson( v: List[JSON] ) extends JSON - case class NullJson() extends JSON - - case class TerminateArrayJson() extends JSON with Terminal // marker for end of Array/Object - case class TerminateObjectJson() extends JSON with Terminal // marker for end of Array/Object - - private val jsChars: Array[Char] = js.toCharArray - private var i = 0 - private val max: Int = jsChars.length - - def reset() = i = 0 - - def parse(expectComma: Boolean = false): JSON = - var result: Option[JSON] = None - val commaFound: Option[JSON] = - if expectComma then - var found = if i == max && (jsChars(i-1)==']' || jsChars(i-1)=='}') then true else false - var done = false - while i < max && !done do - (jsChars(i): @switch) match - case ' ' => - i += 1 - case '\n' => - i += 1 - case '\t' => - i += 1 - case '\r' => - i += 1 - case ',' => - i += 1 - found = true - done = true - case ':' => // cheat! Treat ',' and ':' the same, so {"foo","bar","a","b"} == {"foo":"bar","a":"b"} - i += 1 - found = true - done = true - case ']' => - i += 1 - found = true - done = true - case '}' => - i += 1 - found = true - done = true - case c => - done = true - found match - case true if jsChars(i-1) == '}' => Some(TerminateObjectJson()) - case true if jsChars(i-1) == ']' => Some(TerminateArrayJson()) - case true => None // comma found... continue parsing - case _ => throw new JsonParseError("Unterminated array or object") - else - None // no comma involvement--continue parsing - - commaFound.getOrElse{ - while i < max && result.isEmpty do { - (jsChars(i): @switch) match - case '"' => - i += 1 - result = Some(parseString()) - case 't' | 'f' => - result = Some(parseBoolean()) - case 'n' => - result = Some(parseNull()) - case ' ' => - i += 1 - case '\n' => - i += 1 - case '\t' => - i += 1 - case '\r' => - i += 1 - case ']' => - i += 1 - result = Some(TerminateArrayJson()) - case '}' => - i += 1 - result = Some(TerminateObjectJson()) - case '[' => - val acc = scala.collection.mutable.ListBuffer.empty[JSON] - i += 1 - var last: JSON = parse() - while !last.isInstanceOf[TerminateArrayJson] do - acc.addOne(last) - last = parse(true) - result = Some(ArrayJson(acc.toList)) - case '{' => - val acc = scala.collection.mutable.Map.empty[String,JSON] - i += 1 - var done = false - var key: JSON = parse() - while !done do - key match - case k if k.isInstanceOf[StringJson] => - // while i < max && !key.isInstanceOf[TerminateObjectJson] do - val value: JSON = parse(true) - if value.isInstanceOf[Terminal] then - throw new JsonParseError("Malformed object") - acc.put(k.asInstanceOf[StringJson].get, value) - key = parse(true) - case k if k.isInstanceOf[TerminateObjectJson] => - result = Some(ObjectJson(acc.toMap)) - done = true - case _ => - throw new JsonParseError("Malformed object (invalid key value)") - if i == max && key.isInstanceOf[TerminateObjectJson] then result = Some(ObjectJson(acc.toMap)) - case c if (c >= '0' && c <= '9') || c == '-' || c == '.' || c == '+' => - result = Some(parseNumber()) - } - result.getOrElse(throw new JsonParseError("Unterminated array or object")) - } - - inline def parseNull() = - if i + 3 < max && jsChars(i) == 'n' && jsChars(i + 1) == 'u' && jsChars(i + 2) == 'l' && jsChars(i + 3) == 'l' then - i += 4 - NullJson() - else - throw new JsonParseError(s"Unexpected character '${jsChars(i)}'") - - inline def parseBoolean() = - jsChars(i) match - case 't' if i + 3 < max && jsChars(i + 1) == 'r' && jsChars(i + 2) == 'u' && jsChars(i + 3) == 'e' => - i += 4 - BooleanJson(true) - case 'f' if i + 4 < max && jsChars(i + 1) == 'a' && jsChars(i + 2) == 'l' && jsChars(i + 3) == 's' && jsChars(i + 4) == 'e' => - i += 5 - BooleanJson(false) - case _ => - throw new JsonParseError(s"Unexpected character '${jsChars(i)}'") - - inline def parseString() = - val mark = i - while i < max && jsChars(i) != '"' do - if jsChars(i) == '\\' then // skip escaped special characters - i += 1 - i += 1 - if i == max then - i = mark - throw new JsonParseError(s"Unterminated string starting") - i += 1 - StringJson(mark, i-1) - - inline def parseNumber() = - var isFloat = false - var done = false - val mark = i - while i < max && !done do - jsChars(i) match - case c if (c >= '0' && c <= '9') => - i += 1 - case '.' => - isFloat = true - i += 1 - case 'e' | 'E' | '-' | '+' => - i += 1 - case _ => - done = true - if isFloat then - Try(js.substring(mark,i).toDouble) match - case Success(v) => - FloatJson(v) - case _ => - i = mark - throw new JsonParseError(s"Cannot parse number starting") - else - Try(js.substring(mark,i).toLong) match - case Success(v) => - IntJson(v) - case _ => - i = mark - throw new JsonParseError(s"Cannot parse number starting") diff --git a/src/main/scala/co.blocke.scalajack/json/JsonReader.scala b/src/main/scala/co.blocke.scalajack/json/JsonReader.scala index cf569c9e..4bfb51a0 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonReader.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonReader.scala @@ -2,44 +2,76 @@ package co.blocke.scalajack package json import co.blocke.scala_reflection.{RTypeRef, TypedName} +import co.blocke.scala_reflection.rtypes.{OptionRType, ScalaFieldInfo} import co.blocke.scala_reflection.reflect.rtypeRefs.* -import parser.* import scala.quoted.* import scala.collection.mutable.HashMap import scala.util.{Failure, Success, Try} import scala.collection.Factory +/** We depart from ZIO-Json here. ZIO-Json uses implicits to marshal the right JsonDecoder. This works great for primitive types + * but I had issues trying to get it to work with macros. Since we have all the necessary elements to explicitly provide + * decoders for "constructed" types (collections, classes, ...) we just provided them explicitly. + */ object JsonReader: def refRead[T]( ref: RTypeRef[T] - )(using q: Quotes, tt: Type[T]): Expr[Instruction] = + )(using q: Quotes, tt: Type[T]): Expr[JsonDecoder[T]] = import quotes.reflect.* ref match - case r: PrimitiveRef[?] if r.family == PrimFamily.Boolish => '{ BooleanInstruction() } - case r: PrimitiveRef[?] if r.family == PrimFamily.Stringish => '{ StringInstruction() } - case r: PrimitiveRef[?] if r.family == PrimFamily.Longish => '{ IntInstruction() } + case r: PrimitiveRef[?] => + Expr.summon[JsonDecoder[T]].getOrElse(throw JsonTypeError("No JsonDecoder defined for type " + TypeRepr.of[T].typeSymbol.name)) case r: SeqRef[?] => - r.elementRef.refType match - case '[e] => - r.refType match - case '[t] => - val elementInstruction = refRead[e](r.elementRef.asInstanceOf[RTypeRef[e]]) - '{ SeqInstruction[e, t]($elementInstruction)(using ${ Expr.summon[Factory[e, t]].get }) } - - case r: ScalaClassRef[?] => r.refType match case '[t] => - val fieldInstructions = Expr.ofList( - r.fields - .map { f => - f.fieldRef.refType match - case '[e] => - (Expr(f.name), refRead[e](f.fieldRef.asInstanceOf[RTypeRef[e]])) + r.elementRef.refType match + case '[e] => + val elemDecoder = Expr.summon[JsonDecoder[e]].getOrElse(refRead[e](r.elementRef.asInstanceOf[RTypeRef[e]])) + '{ + JsonDecoder.seq[e]($elemDecoder) map (_.to(${ Expr.summon[Factory[e, T]].get })) + } + + case r: ScalaClassRef[?] => + val fieldNames = Expr(r.fields.map(_.name).toArray) + val fieldDecoders = Expr.ofList( + r.fields.map(f => + f.fieldRef.refType match + case '[e] => + Expr.summon[JsonDecoder[e]].getOrElse(refRead[e](f.fieldRef.asInstanceOf[RTypeRef[e]])) + ) + ) + val instantiator = JsonReaderUtil.classInstantiator[T](r.asInstanceOf[ClassRef[T]]) + val optionalFields = Expr(r.fields.zipWithIndex.collect { case (f, i) if f.fieldRef.isInstanceOf[OptionRef[_]] => i }.toArray) + val fieldsE = Expr.ofList(r.fields.asInstanceOf[List[ScalaFieldInfoRef]].map(_.expr)) + + // TODO: See if we can't get the default values at compile-time. Mix with null/None for a predefined value array: + // Use the Select.... business to fire the default accessors and get an Expr[Any] value back, that we can unscrable in '{} + + '{ + val preloadedFieldValues: Array[Any] = $fieldsE + .asInstanceOf[List[ScalaFieldInfo]] + .map(_ match + case optFld if optFld.fieldType.isInstanceOf[OptionRType[_]] => None + case defFld if defFld.defaultValueAccessorName.isDefined => + val (companion, accessor) = defFld.defaultValueAccessorName.get + // Have to use Java reflection here to get default value--Scala compiler won't have access to companion + // or accessor if we do a ${} block, and using compiler staging would murder performance. + { + val c = Class.forName(companion) + val cons = c.getDeclaredConstructor() + cons.setAccessible(true) + val m = c.getMethod(accessor) + m.setAccessible(true) + m.invoke(cons.newInstance()) } - .map(u => Expr.ofTuple(u)) + case _ => null ) - val instantiator = JsonReaderUtil.classInstantiator[t](r.asInstanceOf[ClassRef[t]]) - '{ ScalaClassInstruction[t]($fieldInstructions.toMap, $instantiator) } + .toArray + ClassDecoder[T]($fieldNames, $fieldDecoders.toArray, $instantiator, preloadedFieldValues) + } + + // We'd love to pass this in--preloaded with defaults/None values + // val fieldValues = new Array[Any](fields.length) diff --git a/src/main/scala/co.blocke.scalajack/json/JsonReader.scalax b/src/main/scala/co.blocke.scalajack/json/JsonReader.scalax deleted file mode 100644 index f9c395c8..00000000 --- a/src/main/scala/co.blocke.scalajack/json/JsonReader.scalax +++ /dev/null @@ -1,49 +0,0 @@ -package co.blocke.scalajack -package json - -import co.blocke.scala_reflection.{RTypeRef, TypedName} -import scala.quoted.* -import scala.collection.mutable.HashMap -import scala.util.{Failure, Success, Try} - -trait JsonReader[B, T]: - def apply(in: B): T - -/* -case class JsonReader() extends ReaderModule: - - val root: ReaderModule = this // Should never be accessed--we're the root! - - // Did the user supply an extension module? - val extension = Try(Class.forName("co.blocke.scalajack.json.ReaderExtension")) match - case Success(c) => Some(c.getDeclaredConstructor().newInstance.asInstanceOf[ReaderModule]) - case Failure(_) => None - - val next: Option[ReaderModule] = Some( - readers.PrimitiveReader( - Some(readers.CollectionReader(Some(TerminusReaderModule(extension, root)), root)), - // readers.ColletionReader( - // readers.ClassReader( - // readers.EnumReader( - // readers.MiscReader( - // TerminusReaderModule(extension, root), - // root - // ), - // root - // ), - // this - // ), - // this - // ), - this - ) - ) - - def refRead[T]( - ref: RTypeRef[T], - parserE: Expr[JsonParser], - cfgE: Expr[JsonConfig], - isMapKey: Boolean = false - )(using q: Quotes, tt: Type[T])(using cache: HashMap[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]]): Expr[Either[ParseError, T]] = - next.get.refRead(ref, parserE, cfgE, isMapKey) - */ diff --git a/src/main/scala/co.blocke.scalajack/json/JsonReaderUtil.scala b/src/main/scala/co.blocke.scalajack/json/JsonReaderUtil.scala index 1b52e355..f28ad84b 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonReaderUtil.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonReaderUtil.scala @@ -7,17 +7,35 @@ import co.blocke.scala_reflection.{Clazzes, RTypeRef, TypedName} object JsonReaderUtil: - def classInstantiator[T: Type](ref: ClassRef[T])(using Quotes): Expr[Map[String, ?] => T] = + // def classInstantiator[T: Type](ref: ClassRef[T])(using Quotes): Expr[Map[String, ?] => T] = + // import quotes.reflect.* + // val sym = TypeRepr.of[T].classSymbol.get + // '{ (fieldMap: Map[String, ?]) => + // ${ + // val tree = Apply( + // Select.unique(New(TypeIdent(sym)), ""), + // ref.fields.map { f => + // f.fieldRef.refType match + // case '[t] => + // '{ fieldMap(${ Expr(f.name) }).asInstanceOf[t] }.asTerm + // } + // ) + // tree.asExpr.asExprOf[T] + // } + // } + + def classInstantiator[T: Type](ref: ClassRef[T])(using Quotes): Expr[Array[?] => T] = import quotes.reflect.* val sym = TypeRepr.of[T].classSymbol.get - '{ (fieldMap: Map[String, ?]) => + '{ (fieldValues: Array[?]) => ${ val tree = Apply( Select.unique(New(TypeIdent(sym)), ""), - ref.fields.map { f => + ref.fields.zipWithIndex.map { case (f, i) => f.fieldRef.refType match case '[t] => - '{ fieldMap(${ Expr(f.name) }).asInstanceOf[t] }.asTerm + val idx = Expr(i) + '{ fieldValues($idx).asInstanceOf[t] }.asTerm } ) tree.asExpr.asExprOf[T] diff --git a/src/main/scala/co.blocke.scalajack/json/JsonSource.scala b/src/main/scala/co.blocke.scalajack/json/JsonSource.scala new file mode 100644 index 00000000..547bbf26 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/JsonSource.scala @@ -0,0 +1,69 @@ +package co.blocke.scalajack +package json + +import scala.annotation.* + +// ZIO-Json defines a series of different Readers. Not exactly sure why--maybe to support different +// modes (streaming, ...)? At least for now we only need one, so merged key bits of Readers into one. +case class JsonSource(js: CharSequence): + private var i = 0 + private[json] val max = js.length + + def pos = i + + inline def read(): Char = + if i < max then + i += 1 + history(i - 1) + else BUFFER_EXCEEDED + + inline def readSkipWhitespace(): Char = + var c: Char = 0 + while { c = read(); isWhitespace(c) } do () + c + + private inline def history(p: Int): Char = js.charAt(p) + + inline def retract() = i -= 1 + + private inline def isWhitespace(c: Char): Boolean = + (c: @switch) match { + case ' ' => true + case '\r' => true + case '\n' => true + case '\t' => true + case _ => false + } + + // Read, transforming escaped chars and stopping when we hit '"' + inline def readEscapedString(): Char = + read() match + case '\\' => + val c2 = read() + (c2: @switch) match + case '"' | '\\' | '/' => c2 + case 'b' => '\b' + case 'f' => '\f' + case 'n' => '\n' + case 'r' => '\r' + case 't' => '\t' + case 'u' => nextHex4() + case _ => throw JsonParseError(s"Invalid '\\${c2.toChar}' in string", this) + case '"' => END_OF_STRING + case BUFFER_EXCEEDED => throw new JsonParseError("Unexpected end of buffer", this) + case c => c + + inline def nextHex4(): Char = + var i: Int = 0 + var accum: Int = 0 + while i < 4 do + var c = read().toInt + if c == BUFFER_EXCEEDED then throw JsonParseError("Unexpected EOB in string", this) + c = + if '0' <= c && c <= '9' then c - '0' + else if 'A' <= c && c <= 'F' then c - 'A' + 10 + else if 'a' <= c && c <= 'f' then c - 'a' + 10 + else throw JsonParseError("Invalid hex character in string", this) + accum = accum * 16 + c + i += 1 + accum.toChar diff --git a/src/main/scala/co.blocke.scalajack/json/Numbers.scala b/src/main/scala/co.blocke.scalajack/json/Numbers.scala new file mode 100644 index 00000000..99b1845a --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/Numbers.scala @@ -0,0 +1,829 @@ +/* + * Copyright 2019-2022 John A. De Goes and the ZIO Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package co.blocke.scalajack +package json + +import java.io.* +import scala.util.control.NoStackTrace + +/** Total, fast, number parsing. + * + * The Java and Scala standard libraries throw exceptions when we attempt to + * parse an invalid number. Unfortunately, exceptions are very expensive, and + * untrusted data can be maliciously constructed to DOS a server. + * + * This suite of functions mitigates against such attacks by building up the + * numbers one character at a time, which has been shown through extensive + * benchmarking to be orders of magnitude faster than exception-throwing stdlib + * parsers, for valid and invalid inputs. This approach, proposed by alexknvl, + * was also benchmarked against regexp-based pre-validation. + * + * Note that although the behaviour is identical to the Java stdlib when given + * the canonical form of a primitive (i.e. the .toString) of a number there may + * be differences in behaviour for non-canonical forms. e.g. the Java stdlib + * may reject "1.0" when parsed as an `BigInteger` but we may parse it as a + * `1`, although "1.1" would be rejected. Parsing of `BigDecimal` preserves the + * trailing zeros on the right but not on the left, e.g. "000.00001000" will be + * "1.000e-5", which is useful in cases where the trailing zeros denote + * measurement accuracy. + * + * `BigInteger`, `BigDecimal`, `Float` and `Double` have a configurable bit + * limit on the size of the significand, to avoid OOM style attacks, which is + * 128 bits by default. + * + * Results are contained in a specialisation of Option that avoids boxing. + */ +// TODO hex radix +// TODO octal radix +object SafeNumbers { + import UnsafeNumbers.UnsafeNumber + + def byte(num: String): ByteOption = + try ByteSome(UnsafeNumbers.byte(num)) + catch { case UnsafeNumber => ByteNone } + + def short(num: String): ShortOption = + try ShortSome(UnsafeNumbers.short(num)) + catch { case UnsafeNumber => ShortNone } + + def int(num: String): IntOption = + try IntSome(UnsafeNumbers.int(num)) + catch { case UnsafeNumber => IntNone } + + def long(num: String): LongOption = + try LongSome(UnsafeNumbers.long(num)) + catch { case UnsafeNumber => LongNone } + + def bigInteger( + num: String, + max_bits: Int = 128 + ): Option[java.math.BigInteger] = + try Some(UnsafeNumbers.bigInteger(num, max_bits)) + catch { case UnsafeNumber => None } + + def float(num: String, max_bits: Int = 128): FloatOption = + try FloatSome(UnsafeNumbers.float(num, max_bits)) + catch { case UnsafeNumber => FloatNone } + + def double(num: String, max_bits: Int = 128): DoubleOption = + try DoubleSome(UnsafeNumbers.double(num, max_bits)) + catch { case UnsafeNumber => DoubleNone } + + def bigDecimal( + num: String, + max_bits: Int = 128 + ): Option[java.math.BigDecimal] = + try Some(UnsafeNumbers.bigDecimal(num, max_bits)) + catch { case UnsafeNumber => None } + + // Based on the amazing work of Raffaello Giulietti + // "The Schubfach way to render doubles": https://drive.google.com/file/d/1luHhyQF9zKlM8yJ1nebU0OgVYhfC6CBN/view + // Sources with the license are here: https://github.com/c4f7fcce9cb06515/Schubfach/blob/3c92d3c9b1fead540616c918cdfef432bca53dfa/todec/src/math/DoubleToDecimal.java + def toString(x: Double): String = { + val bits = java.lang.Double.doubleToLongBits(x) + val ieeeExponent = (bits >> 52).toInt & 0x7ff + val ieeeMantissa = bits & 0xfffffffffffffL + if ieeeExponent == 2047 then { + if x != x then """"NaN"""" + else if bits < 0 then """"-Infinity"""" + else """"Infinity"""" + } else + { + val s = new java.lang.StringBuilder(24) + if bits < 0 then s.append('-') + if x == 0.0f then s.append('0').append('.').append('0') + else { + var e = ieeeExponent - 1075 + var m = ieeeMantissa | 0x10000000000000L + var dv = 0L + var exp = 0 + if e == 0 then dv = m + else if e >= -52 && e < 0 && m << e == 0 then dv = m >> -e + else { + var expShift, expCorr = 0 + var cblShift = 2 + if ieeeExponent == 0 then { + e = -1074 + m = ieeeMantissa + if ieeeMantissa < 3 then { + m *= 10 + expShift = 1 + } + } else if ieeeMantissa == 0 && ieeeExponent > 1 then { + expCorr = 131007 + cblShift = 1 + } + exp = e * 315653 - expCorr >> 20 + val i = exp + 324 << 1 + val g1 = gs(i) + val g0 = gs(i + 1) + val h = (-exp * 108853 >> 15) + e + 2 + val cb = m << 2 + val outm1 = (m.toInt & 0x1) - 1 + val vb = rop(g1, g0, cb << h) + val vbls = rop(g1, g0, cb - cblShift << h) + outm1 + val vbrd = outm1 - rop(g1, g0, cb + 2 << h) + val s = vb >> 2 + if s < 100 || { + dv = s / 10 // FIXME: Use Math.multiplyHigh(s, 1844674407370955168L) instead after dropping JDK 8 support + val sp40 = dv * 40 + val upin = (vbls - sp40).toInt + (((sp40 + vbrd).toInt + 40) ^ upin) >= 0 || { + dv += ~upin >>> 31 + exp += 1 + false + } + } + then { + val s4 = s << 2 + val uin = (vbls - s4).toInt + dv = (~ { + if (((s4 + vbrd).toInt + 4) ^ uin) < 0 then uin + else (vb.toInt & 0x3) + (s.toInt & 0x1) - 3 + } >>> 31) + s + exp -= expShift + } + } + val len = digitCount(dv) + exp += len - 1 + if exp < -3 || exp >= 7 then { + val dotOff = s.length + 1 + s.append(dv) + var i = s.length - 1 + while i > dotOff && s.charAt(i) == '0' do i -= 1 + s.setLength(i + 1) + s.insert(dotOff, '.').append('E').append(exp) + } else if exp < 0 then { + s.append('0').append('.') + while { + exp += 1 + exp != 0 + } do s.append('0') + s.append(dv) + var i = s.length - 1 + while s.charAt(i) == '0' do i -= 1 + s.setLength(i + 1) + s + } else if exp + 1 < len then { + val dotOff = s.length + exp + 1 + s.append(dv) + var i = s.length - 1 + while s.charAt(i) == '0' do i -= 1 + s.setLength(i + 1) + s.insert(dotOff, '.') + } else s.append(dv).append('.').append('0') + } + }.toString + } + + def toString(x: Float): String = { + val bits = java.lang.Float.floatToIntBits(x) + val ieeeExponent = (bits >> 23) & 0xff + val ieeeMantissa = bits & 0x7fffff + if ieeeExponent == 255 then { + if x != x then """"NaN"""" + else if bits < 0 then """"-Infinity"""" + else """"Infinity"""" + } else + { + val s = new java.lang.StringBuilder(16) + if bits < 0 then s.append('-') + if x == 0.0f then s.append('0').append('.').append('0') + else { + var e = ieeeExponent - 150 + var m = ieeeMantissa | 0x800000 + var dv, exp = 0 + if e == 0 then dv = m + else if e >= -23 && e < 0 && m << e == 0 then dv = m >> -e + else { + var expShift, expCorr = 0 + var cblShift = 2 + if ieeeExponent == 0 then { + e = -149 + m = ieeeMantissa + if ieeeMantissa < 8 then { + m *= 10 + expShift = 1 + } + } else if ieeeMantissa == 0 && ieeeExponent > 1 then { + expCorr = 131007 + cblShift = 1 + } + exp = e * 315653 - expCorr >> 20 + val g1 = gs(exp + 324 << 1) + 1 + val h = (-exp * 108853 >> 15) + e + 1 + val cb = m << 2 + val outm1 = (m & 0x1) - 1 + val vb = rop(g1, cb << h) + val vbls = rop(g1, cb - cblShift << h) + outm1 + val vbrd = outm1 - rop(g1, cb + 2 << h) + val s = vb >> 2 + if s < 100 || { + dv = (s * 3435973837L >>> 35).toInt // divide a positive int by 10 + val sp40 = dv * 40 + val upin = vbls - sp40 + ((sp40 + vbrd + 40) ^ upin) >= 0 || { + dv += ~upin >>> 31 + exp += 1 + false + } + } + then { + val s4 = s << 2 + val uin = vbls - s4 + dv = (~ { + if ((s4 + vbrd + 4) ^ uin) < 0 then uin + else (vb & 0x3) + (s & 0x1) - 3 + } >>> 31) + s + exp -= expShift + } + } + val len = digitCount(dv.toLong) + exp += len - 1 + if exp < -3 || exp >= 7 then { + val dotOff = s.length + 1 + s.append(dv) + var i = s.length - 1 + while i > dotOff && s.charAt(i) == '0' do i -= 1 + s.setLength(i + 1) + s.insert(dotOff, '.').append('E').append(exp) + } else if exp < 0 then { + s.append('0').append('.') + while { + exp += 1 + exp != 0 + } do s.append('0') + s.append(dv) + var i = s.length - 1 + while s.charAt(i) == '0' do i -= 1 + s.setLength(i + 1) + s + } else if exp + 1 < len then { + val dotOff = s.length + exp + 1 + s.append(dv) + var i = s.length - 1 + while s.charAt(i) == '0' do i -= 1 + s.setLength(i + 1) + s.insert(dotOff, '.') + } else s.append(dv).append('.').append('0') + } + }.toString + } + + private[this] def rop(g1: Long, g0: Long, cp: Long): Long = { + val x1 = multiplyHigh(g0, cp) // FIXME: Use Math.multiplyHigh after dropping JDK 8 support + val z = (g1 * cp >>> 1) + x1 + val y1 = multiplyHigh(g1, cp) // FIXME: Use Math.multiplyHigh after dropping JDK 8 support + (z >>> 63) + y1 | -(z & 0x7fffffffffffffffL) >>> 63 + } + + private[this] def rop(g: Long, cp: Int): Int = { + val x1 = + ((g & 0xffffffffL) * cp >>> 32) + (g >>> 32) * cp // FIXME: Use Math.multiplyHigh after dropping JDK 8 support + (x1 >>> 31).toInt | -x1.toInt >>> 31 + } + + private[this] def multiplyHigh(x: Long, y: Long): Long = { + val x2 = x & 0xffffffffL + val y2 = y & 0xffffffffL + val b = x2 * y2 + val x1 = x >>> 32 + val y1 = y >>> 32 + val a = x1 * y1 + (((b >>> 32) + (x1 + x2) * (y1 + y2) - b - a) >>> 32) + a + } + + // Adoption of a nice trick form Daniel Lemire's blog that works for numbers up to 10^18: + // https://lemire.me/blog/2021/06/03/computing-the-number-of-digits-of-an-integer-even-faster/ + private[this] def digitCount(x: Long): Int = (offsets(java.lang.Long.numberOfLeadingZeros(x)) + x >> 58).toInt + + final private val offsets = Array( + 5088146770730811392L, 5088146770730811392L, 5088146770730811392L, 5088146770730811392L, 5088146770730811392L, 5088146770730811392L, 5088146770730811392L, 5088146770730811392L, 4889916394579099648L, 4889916394579099648L, 4889916394579099648L, + 4610686018427387904L, 4610686018427387904L, 4610686018427387904L, 4610686018427387904L, 4323355642275676160L, 4323355642275676160L, 4323355642275676160L, 4035215266123964416L, 4035215266123964416L, 4035215266123964416L, 3746993889972252672L, + 3746993889972252672L, 3746993889972252672L, 3746993889972252672L, 3458764413820540928L, 3458764413820540928L, 3458764413820540928L, 3170534127668829184L, 3170534127668829184L, 3170534127668829184L, 2882303760517117440L, 2882303760517117440L, + 2882303760517117440L, 2882303760517117440L, 2594073385265405696L, 2594073385265405696L, 2594073385265405696L, 2305843009203693952L, 2305843009203693952L, 2305843009203693952L, 2017612633060982208L, 2017612633060982208L, 2017612633060982208L, + 2017612633060982208L, 1729382256910170464L, 1729382256910170464L, 1729382256910170464L, 1441151880758548720L, 1441151880758548720L, 1441151880758548720L, 1152921504606845976L, 1152921504606845976L, 1152921504606845976L, 1152921504606845976L, + 864691128455135132L, 864691128455135132L, 864691128455135132L, 576460752303423478L, 576460752303423478L, 576460752303423478L, 576460752303423478L, 576460752303423478L, 576460752303423478L, 576460752303423478L + ) + + private[this] val gs: Array[Long] = Array( + 5696189077778435540L, 6557778377634271669L, 9113902524445496865L, 1269073367360058862L, 7291122019556397492L, 1015258693888047090L, 5832897615645117993L, 6346230177223303157L, 4666318092516094394L, 8766332956520552849L, 7466108948025751031L, + 8492109508320019073L, 5972887158420600825L, 4949013199285060097L, 4778309726736480660L, 3959210559428048077L, 7645295562778369056L, 6334736895084876923L, 6116236450222695245L, 3223115108696946377L, 4892989160178156196L, 2578492086957557102L, + 7828782656285049914L, 436238524390181040L, 6263026125028039931L, 2193665226883099993L, 5010420900022431944L, 9133629810990300641L, 8016673440035891111L, 9079784475471615541L, 6413338752028712889L, 5419153173006337271L, 5130671001622970311L, + 6179996945776024979L, 8209073602596752498L, 6198646298499729642L, 6567258882077401998L, 8648265853541694037L, 5253807105661921599L, 1384589460720489745L, 8406091369059074558L, 5904691951894693915L, 6724873095247259646L, 8413102376257665455L, + 5379898476197807717L, 4885807493635177203L, 8607837561916492348L, 438594360332462878L, 6886270049533193878L, 4040224303007880625L, 5509016039626555102L, 6921528257148214824L, 8814425663402488164L, 3695747581953323071L, 7051540530721990531L, + 4801272472933613619L, 5641232424577592425L, 1996343570975935733L, 9025971879324147880L, 3194149713561497173L, 7220777503459318304L, 2555319770849197738L, 5776622002767454643L, 3888930224050313352L, 4621297602213963714L, 6800492993982161005L, + 7394076163542341943L, 5346765568258592123L, 5915260930833873554L, 7966761269348784022L, 4732208744667098843L, 8218083422849982379L, 7571533991467358150L, 2080887032334240837L, 6057227193173886520L, 1664709625867392670L, 4845781754539109216L, + 1331767700693914136L, 7753250807262574745L, 7664851543223128102L, 6202600645810059796L, 6131881234578502482L, 4962080516648047837L, 3060830580291846824L, 7939328826636876539L, 6742003335837910079L, 6351463061309501231L, 7238277076041283225L, + 5081170449047600985L, 3945947253462071419L, 8129872718476161576L, 6313515605539314269L, 6503898174780929261L, 3206138077060496254L, 5203118539824743409L, 720236054277441842L, 8324989663719589454L, 4841726501585817270L, 6659991730975671563L, + 5718055608639608977L, 5327993384780537250L, 8263793301653597505L, 8524789415648859601L, 3998697245790980200L, 6819831532519087681L, 1354283389261828999L, 5455865226015270144L, 8462124340893283845L, 8729384361624432231L, 8005375723316388668L, + 6983507489299545785L, 4559626171282155773L, 5586805991439636628L, 3647700937025724618L, 8938889586303418605L, 3991647091870204227L, 7151111669042734884L, 3193317673496163382L, 5720889335234187907L, 4399328546167885867L, 9153422936374700651L, + 8883600081239572549L, 7322738349099760521L, 5262205657620702877L, 5858190679279808417L, 2365090118725607140L, 4686552543423846733L, 7426095317093351197L, 7498484069478154774L, 813706063123630946L, 5998787255582523819L, 2495639257869859918L, + 4799029804466019055L, 3841185813666843096L, 7678447687145630488L, 6145897301866948954L, 6142758149716504390L, 8606066656235469486L, 4914206519773203512L, 6884853324988375589L, 7862730431637125620L, 3637067690497580296L, 6290184345309700496L, + 2909654152398064237L, 5032147476247760397L, 483048914547496228L, 8051435961996416635L, 2617552670646949126L, 6441148769597133308L, 2094042136517559301L, 5152919015677706646L, 5364582523955957764L, 8244670425084330634L, 4893983223587622099L, + 6595736340067464507L, 5759860986241052841L, 5276589072053971606L, 918539974250931950L, 8442542515286354569L, 7003687180914356604L, 6754034012229083655L, 7447624152102440445L, 5403227209783266924L, 5958099321681952356L, 8645163535653227079L, + 3998935692578258285L, 6916130828522581663L, 5043822961433561789L, 5532904662818065330L, 7724407183888759755L, 8852647460508904529L, 3135679457367239799L, 7082117968407123623L, 4353217973264747001L, 5665694374725698898L, 7171923193353707924L, + 9065110999561118238L, 407030665140201709L, 7252088799648894590L, 4014973346854071690L, 5801671039719115672L, 3211978677483257352L, 4641336831775292537L, 8103606164099471367L, 7426138930840468060L, 5587072233075333540L, 5940911144672374448L, + 4469657786460266832L, 4752728915737899558L, 7265075043910123789L, 7604366265180639294L, 556073626030467093L, 6083493012144511435L, 2289533308195328836L, 4866794409715609148L, 1831626646556263069L, 7786871055544974637L, 1085928227119065748L, + 6229496844435979709L, 6402765803808118083L, 4983597475548783767L, 6966887050417449628L, 7973755960878054028L, 3768321651184098759L, 6379004768702443222L, 6704006135689189330L, 5103203814961954578L, 1673856093809441141L, 8165126103939127325L, + 833495342724150664L, 6532100883151301860L, 666796274179320531L, 5225680706521041488L, 533437019343456425L, 8361089130433666380L, 8232196860433350926L, 6688871304346933104L, 6585757488346680741L, 5351097043477546483L, 7113280398048299755L, + 8561755269564074374L, 313202192651548637L, 6849404215651259499L, 2095236161492194072L, 5479523372521007599L, 3520863336564710419L, 8767237396033612159L, 99358116390671185L, 7013789916826889727L, 1924160900483492110L, 5611031933461511781L, + 7073351942499659173L, 8977651093538418850L, 7628014293257544353L, 7182120874830735080L, 6102411434606035483L, 5745696699864588064L, 4881929147684828386L, 9193114719783340903L, 2277063414182859933L, 7354491775826672722L, 5510999546088198270L, + 5883593420661338178L, 719450822128648293L, 4706874736529070542L, 4264909472444828957L, 7530999578446512867L, 8668529563282681493L, 6024799662757210294L, 3245474835884234871L, 4819839730205768235L, 4441054276078343059L, 7711743568329229176L, + 7105686841725348894L, 6169394854663383341L, 3839875066009323953L, 4935515883730706673L, 1227225645436504001L, 7896825413969130677L, 118886625327451240L, 6317460331175304541L, 5629132522374826477L, 5053968264940243633L, 2658631610528906020L, + 8086349223904389813L, 2409136169475294470L, 6469079379123511850L, 5616657750322145900L, 5175263503298809480L, 4493326200257716720L, 8280421605278095168L, 7189321920412346751L, 6624337284222476135L, 217434314217011916L, 5299469827377980908L, + 173947451373609533L, 8479151723804769452L, 7657013551681595899L, 6783321379043815562L, 2436262026603366396L, 5426657103235052449L, 7483032843395558602L, 8682651365176083919L, 6438829327320028278L, 6946121092140867135L, 6995737869226977784L, + 5556896873712693708L, 5596590295381582227L, 8891034997940309933L, 7109870065239576402L, 7112827998352247947L, 153872830078795637L, 5690262398681798357L, 5657121486175901994L, 9104419837890877372L, 1672696748397622544L, 7283535870312701897L, + 6872180620830963520L, 5826828696250161518L, 1808395681922860493L, 4661462957000129214L, 5136065360280198718L, 7458340731200206743L, 2683681354335452463L, 5966672584960165394L, 5836293898210272294L, 4773338067968132315L, 6513709525939172997L, + 7637340908749011705L, 1198563204647900987L, 6109872726999209364L, 958850563718320789L, 4887898181599367491L, 2611754858345611793L, 7820637090558987986L, 489458958611068546L, 6256509672447190388L, 7770264796372675483L, 5005207737957752311L, + 682188614985274902L, 8008332380732403697L, 6625525006089305327L, 6406665904585922958L, 1611071190129533939L, 5125332723668738366L, 4978205766845537474L, 8200532357869981386L, 4275780412210949635L, 6560425886295985109L, 1575949922397804547L, + 5248340709036788087L, 3105434345289198799L, 8397345134458860939L, 6813369359833673240L, 6717876107567088751L, 7295369895237893754L, 5374300886053671001L, 3991621508819359841L, 8598881417685873602L, 2697245599369065423L, 6879105134148698881L, + 7691819701608117823L, 5503284107318959105L, 4308781353915539097L, 8805254571710334568L, 6894050166264862555L, 7044203657368267654L, 9204588947753800367L, 5635362925894614123L, 9208345565573995455L, 9016580681431382598L, 3665306460692661759L, + 7213264545145106078L, 6621593983296039730L, 5770611636116084862L, 8986624001378742108L, 4616489308892867890L, 3499950386361083363L, 7386382894228588624L, 5599920618177733380L, 5909106315382870899L, 6324610901913141866L, 4727285052306296719L, + 6904363128901468655L, 7563656083690074751L, 5512957784129484362L, 6050924866952059801L, 2565691819932632328L, 4840739893561647841L, 207879048575150701L, 7745183829698636545L, 5866629699833106606L, 6196147063758909236L, 4693303759866485285L, + 4956917651007127389L, 1909968600522233067L, 7931068241611403822L, 6745298575577483229L, 6344854593289123058L, 1706890045720076260L, 5075883674631298446L, 5054860851317971332L, 8121413879410077514L, 4398428547366843807L, 6497131103528062011L, + 5363417245264430207L, 5197704882822449609L, 2446059388840589004L, 8316327812515919374L, 7603043836886852730L, 6653062250012735499L, 7927109476880437346L, 5322449800010188399L, 8186361988875305038L, 8515919680016301439L, 7564155960087622576L, + 6812735744013041151L, 7895999175441053223L, 5450188595210432921L, 4472124932981887417L, 8720301752336692674L, 3466051078029109543L, 6976241401869354139L, 4617515269794242796L, 5580993121495483311L, 5538686623206349399L, 8929588994392773298L, + 5172549782388248714L, 7143671195514218638L, 7827388640652509295L, 5714936956411374911L, 727887690409141951L, 9143899130258199857L, 6698643526767492606L, 7315119304206559886L, 1669566006672083762L, 5852095443365247908L, 8714350434821487656L, + 4681676354692198327L, 1437457125744324640L, 7490682167507517323L, 4144605808561874585L, 5992545734006013858L, 7005033461591409992L, 4794036587204811087L, 70003547160262509L, 7670458539527697739L, 1956680082827375175L, 6136366831622158191L, + 3410018473632855302L, 4909093465297726553L, 883340371535329080L, 7854549544476362484L, 8792042223940347174L, 6283639635581089987L, 8878308186523232901L, 5026911708464871990L, 3413297734476675998L, 8043058733543795184L, 5461276375162681596L, + 6434446986835036147L, 6213695507501100438L, 5147557589468028918L, 1281607591258970028L, 8236092143148846269L, 205897738643396882L, 6588873714519077015L, 2009392598285672668L, 5271098971615261612L, 1607514078628538134L, 8433758354584418579L, + 4416696933176616176L, 6747006683667534863L, 5378031953912248102L, 5397605346934027890L, 7991774377871708805L, 8636168555094444625L, 3563466967739958280L, 6908934844075555700L, 2850773574191966624L, 5527147875260444560L, 2280618859353573299L, + 8843436600416711296L, 3648990174965717279L, 7074749280333369037L, 1074517732601618662L, 5659799424266695229L, 6393637408194160414L, 9055679078826712367L, 4695796630997791177L, 7244543263061369894L, 67288490056322619L, 5795634610449095915L, + 1898505199416013257L, 4636507688359276732L, 1518804159532810606L, 7418412301374842771L, 4274761062623452130L, 5934729841099874217L, 1575134442727806543L, 4747783872879899373L, 6794130776295110719L, 7596454196607838997L, 9025934834701221989L, + 6077163357286271198L, 3531399053019067268L, 4861730685829016958L, 6514468057157164137L, 7778769097326427133L, 8578474484080507458L, 6223015277861141707L, 1328756365151540482L, 4978412222288913365L, 6597028314234097870L, 7965459555662261385L, + 1331873265919780784L, 6372367644529809108L, 1065498612735824627L, 5097894115623847286L, 4541747704930570025L, 8156630584998155658L, 3577447513147001717L, 6525304467998524526L, 6551306825259511697L, 5220243574398819621L, 3396371052836654196L, + 8352389719038111394L, 1744844869796736390L, 6681911775230489115L, 3240550303208344274L, 5345529420184391292L, 2592440242566675419L, 8552847072295026067L, 5992578795477635832L, 6842277657836020854L, 1104714221640198342L, 5473822126268816683L, + 2728445784683113836L, 8758115402030106693L, 2520838848122026975L, 7006492321624085354L, 5706019893239531903L, 5605193857299268283L, 6409490321962580684L, 8968310171678829253L, 8410510107769173933L, 7174648137343063403L, 1194384864102473662L, + 5739718509874450722L, 4644856706023889253L, 9183549615799121156L, 53073100154402158L, 7346839692639296924L, 7421156109607342373L, 5877471754111437539L, 7781599295056829060L, 4701977403289150031L, 8069953843416418410L, 7523163845262640050L, + 9222577334724359132L, 6018531076210112040L, 7378061867779487306L, 4814824860968089632L, 5902449494223589845L, 7703719777548943412L, 2065221561273923105L, 6162975822039154729L, 7186200471132003969L, 4930380657631323783L, 7593634784276558337L, + 7888609052210118054L, 1081769210616762369L, 6310887241768094443L, 2710089775864365057L, 5048709793414475554L, 5857420635433402369L, 8077935669463160887L, 3837849794580578305L, 6462348535570528709L, 8604303057777328129L, 5169878828456422967L, + 8728116853592817665L, 8271806125530276748L, 6586289336264687617L, 6617444900424221398L, 8958380283753660417L, 5293955920339377119L, 1632681004890062849L, 8470329472543003390L, 6301638422566010881L, 6776263578034402712L, 5041310738052808705L, + 5421010862427522170L, 343699775700336641L, 8673617379884035472L, 549919641120538625L, 6938893903907228377L, 5973958935009296385L, 5551115123125782702L, 1089818333265526785L, 8881784197001252323L, 3588383740595798017L, 7105427357601001858L, + 6560055807218548737L, 5684341886080801486L, 8937393460516749313L, 9094947017729282379L, 1387108685230112769L, 7275957614183425903L, 2954361355555045377L, 5820766091346740722L, 6052837899185946625L, 4656612873077392578L, 1152921504606846977L, + 7450580596923828125L, 1L, 5960464477539062500L, 1L, 4768371582031250000L, 1L, 7629394531250000000L, 1L, 6103515625000000000L, 1L, 4882812500000000000L, 1L, 7812500000000000000L, 1L, 6250000000000000000L, 1L, 5000000000000000000L, 1L, + 8000000000000000000L, 1L, 6400000000000000000L, 1L, 5120000000000000000L, 1L, 8192000000000000000L, 1L, 6553600000000000000L, 1L, 5242880000000000000L, 1L, 8388608000000000000L, 1L, 6710886400000000000L, 1L, 5368709120000000000L, 1L, + 8589934592000000000L, 1L, 6871947673600000000L, 1L, 5497558138880000000L, 1L, 8796093022208000000L, 1L, 7036874417766400000L, 1L, 5629499534213120000L, 1L, 9007199254740992000L, 1L, 7205759403792793600L, 1L, 5764607523034234880L, 1L, + 4611686018427387904L, 1L, 7378697629483820646L, 3689348814741910324L, 5902958103587056517L, 1106804644422573097L, 4722366482869645213L, 6419466937650923963L, 7555786372591432341L, 8426472692870523179L, 6044629098073145873L, 4896503746925463381L, + 4835703278458516698L, 7606551812282281028L, 7737125245533626718L, 1102436455425918676L, 6189700196426901374L, 4571297979082645264L, 4951760157141521099L, 5501712790637071373L, 7922816251426433759L, 3268717242906448711L, 6338253001141147007L, + 4459648201696114131L, 5070602400912917605L, 9101741783469756789L, 8112963841460668169L, 5339414816696835055L, 6490371073168534535L, 6116206260728423206L, 5192296858534827628L, 4892965008582738565L, 8307674973655724205L, 5984069606361426541L, + 6646139978924579364L, 4787255685089141233L, 5316911983139663491L, 5674478955442268148L, 8507059173023461586L, 5389817513965718714L, 6805647338418769269L, 2467179603801619810L, 5444517870735015415L, 3818418090412251009L, 8711228593176024664L, + 6109468944659601615L, 6968982874540819731L, 6732249563098636453L, 5575186299632655785L, 3541125243107954001L, 8920298079412249256L, 5665800388972726402L, 7136238463529799405L, 2687965903807225960L, 5708990770823839524L, 2150372723045780768L, + 9134385233318143238L, 7129945171615159552L, 7307508186654514591L, 169932915179262157L, 5846006549323611672L, 7514643961627230372L, 4676805239458889338L, 2322366354559873974L, 7482888383134222941L, 1871111759924843197L, 5986310706507378352L, + 8875587037423695204L, 4789048565205902682L, 3411120815197045840L, 7662477704329444291L, 7302467711686228506L, 6129982163463555433L, 3997299761978027643L, 4903985730770844346L, 6887188624324332438L, 7846377169233350954L, 7330152984177021577L, + 6277101735386680763L, 7708796794712572423L, 5021681388309344611L, 633014213657192454L, 8034690221294951377L, 6546845963964373411L, 6427752177035961102L, 1548127956429588405L, 5142201741628768881L, 6772525587256536209L, 8227522786606030210L, + 7146692124868547611L, 6582018229284824168L, 5717353699894838089L, 5265614583427859334L, 8263231774657780795L, 8424983333484574935L, 7687147617339583786L, 6739986666787659948L, 6149718093871667029L, 5391989333430127958L, 8609123289839243947L, + 8627182933488204734L, 2706550819517059345L, 6901746346790563787L, 4009915062984602637L, 5521397077432451029L, 8741955272500547595L, 8834235323891921647L, 8453105213888010667L, 7067388259113537318L, 3073135356368498210L, 5653910607290829854L, + 6147857099836708891L, 9046256971665327767L, 4302548137625868741L, 7237005577332262213L, 8976061732213560478L, 5789604461865809771L, 1646826163657982898L, 4631683569492647816L, 8696158560410206965L, 7410693711188236507L, 1001132845059645012L, + 5928554968950589205L, 6334929498160581494L, 4742843975160471364L, 5067943598528465196L, 7588550360256754183L, 2574686535532678828L, 6070840288205403346L, 5749098043168053386L, 4856672230564322677L, 2754604027163487547L, 7770675568902916283L, + 6252040850832535236L, 6216540455122333026L, 8690981495407938512L, 4973232364097866421L, 5108110788955395648L, 7957171782556586274L, 4483628447586722714L, 6365737426045269019L, 5431577165440333333L, 5092589940836215215L, 6189936139723221828L, + 8148143905337944345L, 680525786702379117L, 6518515124270355476L, 544420629361903293L, 5214812099416284380L, 7814234132973343281L, 8343699359066055009L, 3279402575902573442L, 6674959487252844007L, 4468196468093013915L, 5339967589802275205L, + 9108580396587276617L, 8543948143683640329L, 5350356597684866779L, 6835158514946912263L, 6124959685518848585L, 5468126811957529810L, 8589316563156989191L, 8749002899132047697L, 4519534464196406897L, 6999202319305638157L, 9149650793469991003L, + 5599361855444510526L, 3630371820034082479L, 8958978968711216842L, 2119246097312621643L, 7167183174968973473L, 7229420099962962799L, 5733746539975178779L, 249512857857504755L, 9173994463960286046L, 4088569387313917931L, 7339195571168228837L, + 1426181102480179183L, 5871356456934583069L, 6674968104097008831L, 4697085165547666455L, 7184648890648562227L, 7515336264876266329L, 2272066188182923754L, 6012269011901013063L, 3662327357917294165L, 4809815209520810450L, 6619210701075745655L, + 7695704335233296721L, 1367365084866417240L, 6156563468186637376L, 8472589697376954439L, 4925250774549309901L, 4933397350530608390L, 7880401239278895842L, 4204086946107063100L, 6304320991423116673L, 8897292778998515965L, 5043456793138493339L, + 1583811001085947287L, 8069530869021589342L, 6223446416479425982L, 6455624695217271474L, 1289408318441630463L, 5164499756173817179L, 2876201062124259532L, 8263199609878107486L, 8291270514140725574L, 6610559687902485989L, 4788342003941625298L, + 5288447750321988791L, 5675348010524255400L, 8461516400515182066L, 5391208002096898316L, 6769213120412145653L, 2468291994306563491L, 5415370496329716522L, 5663982410187161116L, 8664592794127546436L, 1683674226815637140L, 6931674235302037148L, + 8725637010936330358L, 5545339388241629719L, 1446486386636198802L, 8872543021186607550L, 6003727033359828406L, 7098034416949286040L, 4802981626687862725L, 5678427533559428832L, 3842385301350290180L, 9085484053695086131L, 7992490889531419449L, + 7268387242956068905L, 4549318304254180398L, 5814709794364855124L, 3639454643403344318L, 4651767835491884099L, 4756238122093630616L, 7442828536787014559L, 2075957773236943501L, 5954262829429611647L, 3505440625960509963L, 4763410263543689317L, + 8338375722881273455L, 7621456421669902908L, 5962703527126216881L, 6097165137335922326L, 8459511636442883828L, 4877732109868737861L, 4922934901783351901L, 7804371375789980578L, 4187347028111452718L, 6243497100631984462L, 7039226437231072498L, + 4994797680505587570L, 1942032335042947675L, 7991676288808940112L, 3107251736068716280L, 6393341031047152089L, 8019824610967838509L, 5114672824837721671L, 8260534096145225969L, 8183476519740354675L, 304133702235675419L, 6546781215792283740L, + 243306961788540335L, 5237424972633826992L, 194645569430832268L, 8379879956214123187L, 2156107318460286790L, 6703903964971298549L, 7258909076881094917L, 5363123171977038839L, 7651801668875831096L, 8580997075163262143L, 6708859448088464268L, + 6864797660130609714L, 9056436373212681737L, 5491838128104487771L, 9089823505941100552L, 8786941004967180435L, 1630996757909074751L, 7029552803973744348L, 1304797406327259801L, 5623642243178995478L, 4733186739803718164L, 8997827589086392765L, + 5728424376314993901L, 7198262071269114212L, 4582739501051995121L, 5758609657015291369L, 9200214822954461581L, 9213775451224466191L, 9186320494614273045L, 7371020360979572953L, 5504381988320463275L, 5896816288783658362L, 8092854405398280943L, + 4717453031026926690L, 2784934709576714431L, 7547924849643082704L, 4455895535322743090L, 6038339879714466163L, 5409390835629149634L, 4830671903771572930L, 8016861483245230030L, 7729075046034516689L, 3603606336337592240L, 6183260036827613351L, + 4727559476441028954L, 4946608029462090681L, 1937373173781868001L, 7914572847139345089L, 8633820300163854287L, 6331658277711476071L, 8751730647502038591L, 5065326622169180857L, 5156710110630675711L, 8104522595470689372L, 872038547525260492L, + 6483618076376551497L, 6231654060133073878L, 5186894461101241198L, 1295974433364548779L, 8299031137761985917L, 228884686012322885L, 6639224910209588733L, 5717130970922723793L, 5311379928167670986L, 8263053591480089358L, 8498207885068273579L, + 308164894771456841L, 6798566308054618863L, 2091206323188120634L, 5438853046443695090L, 5362313873292406831L, 8702164874309912144L, 8579702197267850929L, 6961731899447929715L, 8708436165185235905L, 5569385519558343772L, 6966748932148188724L, + 8911016831293350036L, 3768100661953281312L, 7128813465034680029L, 1169806122191669888L, 5703050772027744023L, 2780519305124291072L, 9124881235244390437L, 2604156480827910553L, 7299904988195512349L, 7617348406775193928L, 5839923990556409879L, + 7938553132791110304L, 4671939192445127903L, 8195516913603843405L, 7475102707912204646L, 2044780617540418478L, 5980082166329763716L, 9014522123516155429L, 4784065733063810973L, 5366943291441969181L, 7654505172902097557L, 6742434858936195528L, + 6123604138321678046L, 1704599072407046100L, 4898883310657342436L, 8742376887409457526L, 7838213297051747899L, 1075082168258445910L, 6270570637641398319L, 2704740141977711890L, 5016456510113118655L, 4008466520953124674L, 8026330416180989848L, + 6413546433524999478L, 6421064332944791878L, 8820185961561909905L, 5136851466355833503L, 1522125547136662440L, 8218962346169333605L, 590726468047704741L, 6575169876935466884L, 472581174438163793L, 5260135901548373507L, 2222739346921486196L, + 8416217442477397611L, 5401057362445333075L, 6732973953981918089L, 2476171482585311299L, 5386379163185534471L, 3825611593439204201L, 8618206661096855154L, 2431629734760816398L, 6894565328877484123L, 3789978195179608280L, 5515652263101987298L, + 6721331370885596947L, 8825043620963179677L, 8909455786045999954L, 7060034896770543742L, 3438215814094889640L, 5648027917416434993L, 8284595873388777197L, 9036844667866295990L, 2187306953196312545L, 7229475734293036792L, 1749845562557050036L, + 5783580587434429433L, 6933899672158505514L, 4626864469947543547L, 13096515613938926L, 7402983151916069675L, 1865628832353257443L, 5922386521532855740L, 1492503065882605955L, 4737909217226284592L, 1194002452706084764L, 7580654747562055347L, + 3755078331700690783L, 6064523798049644277L, 8538085887473418112L, 4851619038439715422L, 3141119895236824166L, 7762590461503544675L, 6870466239749873827L, 6210072369202835740L, 5496372991799899062L, 4968057895362268592L, 4397098393439919250L, + 7948892632579629747L, 8880031836874825961L, 6359114106063703798L, 3414676654757950445L, 5087291284850963038L, 6421090138548270680L, 8139666055761540861L, 8429069814306277926L, 6511732844609232689L, 4898581444074067179L, 5209386275687386151L, + 5763539562630208905L, 8335018041099817842L, 5532314485466423924L, 6668014432879854274L, 736502773631228816L, 5334411546303883419L, 2433876626275938215L, 8535058474086213470L, 7583551416783411467L, 6828046779268970776L, 6066841133426729173L, + 5462437423415176621L, 3008798499370428177L, 8739899877464282594L, 1124728784250774760L, 6991919901971426075L, 2744457434771574970L, 5593535921577140860L, 2195565947817259976L, 8949657474523425376L, 3512905516507615961L, 7159725979618740301L, + 965650005835137607L, 5727780783694992240L, 8151217634151930732L, 9164449253911987585L, 3818576177788313364L, 7331559403129590068L, 3054860942230650691L, 5865247522503672054L, 6133237568526430876L, 4692198018002937643L, 6751264462192099863L, + 7507516828804700229L, 8957348732136404618L, 6006013463043760183L, 9010553393080078856L, 4804810770435008147L, 1674419492351197600L, 7687697232696013035L, 4523745595132871322L, 6150157786156810428L, 3618996476106297057L, 4920126228925448342L, + 6584545995626947969L, 7872201966280717348L, 3156575963519296104L, 6297761573024573878L, 6214609585557347207L, 5038209258419659102L, 8661036483187788089L, 8061134813471454564L, 6478960743616640295L, 6448907850777163651L, 7027843002264267398L, + 5159126280621730921L, 3777599994440458757L, 8254602048994769474L, 2354811176362823687L, 6603681639195815579L, 3728523348461214111L, 5282945311356652463L, 4827493086139926451L, 8452712498170643941L, 5879314530452927160L, 6762169998536515153L, + 2858777216991386566L, 5409735998829212122L, 5976370588335019576L, 8655577598126739396L, 2183495311852210675L, 6924462078501391516L, 9125493878965589187L, 5539569662801113213L, 5455720695801516188L, 8863311460481781141L, 6884478705911470739L, + 7090649168385424913L, 3662908557358221429L, 5672519334708339930L, 6619675660628487467L, 9076030935533343889L, 1368109020150804139L, 7260824748426675111L, 2939161623491598473L, 5808659798741340089L, 506654891422323617L, 4646927838993072071L, + 2249998320508814055L, 7435084542388915313L, 9134020534926967972L, 5948067633911132251L, 1773193205828708893L, 4758454107128905800L, 8797252194146787761L, 7613526571406249281L, 4852231473780084609L, 6090821257124999425L, 2037110771653112526L, + 4872657005699999540L, 1629688617322490021L, 7796251209119999264L, 2607501787715984033L, 6237000967295999411L, 3930675837543742388L, 4989600773836799529L, 1299866262664038749L, 7983361238138879246L, 5769134835004372321L, 6386688990511103397L, + 2770633460632542696L, 5109351192408882717L, 7750529990618899641L, 8174961907854212348L, 5022150355506418780L, 6539969526283369878L, 7707069099147045347L, 5231975621026695903L, 631632057204770793L, 8371160993642713444L, 8389308921011453915L, + 6696928794914170755L, 8556121544180118293L, 5357543035931336604L, 6844897235344094635L, 8572068857490138567L, 5417812354437685931L, 6857655085992110854L, 644901068808238421L, 5486124068793688683L, 2360595262417545899L, 8777798510069901893L, + 1932278012497118276L, 7022238808055921514L, 5235171224739604944L, 5617791046444737211L, 6032811387162639117L, 8988465674311579538L, 5963149404718312264L, 7190772539449263630L, 8459868338516560134L, 5752618031559410904L, 6767894670813248108L, + 9204188850495057447L, 5294608251188331487L + ) +} + +// specialised Options to avoid boxing. Prefer .isEmpty guarded access to .value +// for higher performance: pattern matching is slightly slower. + +sealed abstract class ByteOption { + def isEmpty: Boolean + def value: Byte +} +case object ByteNone extends ByteOption { + def isEmpty = true + def value: Byte = throw new java.util.NoSuchElementException +} +case class ByteSome(value: Byte) extends ByteOption { + def isEmpty = false +} + +sealed abstract class ShortOption { + def isEmpty: Boolean + def value: Short +} +case object ShortNone extends ShortOption { + def isEmpty = true + def value: Short = throw new java.util.NoSuchElementException +} +case class ShortSome(value: Short) extends ShortOption { + def isEmpty = false +} + +sealed abstract class IntOption { + def isEmpty: Boolean + def value: Int +} +case object IntNone extends IntOption { + def isEmpty = true + def value: Int = throw new java.util.NoSuchElementException +} +case class IntSome(value: Int) extends IntOption { + def isEmpty = false +} + +sealed abstract class LongOption { + def isEmpty: Boolean + def value: Long +} +case object LongNone extends LongOption { + def isEmpty = true + def value: Long = throw new java.util.NoSuchElementException +} +case class LongSome(value: Long) extends LongOption { + def isEmpty = false +} + +sealed abstract class FloatOption { + def isEmpty: Boolean + def value: Float +} +case object FloatNone extends FloatOption { + def isEmpty = true + def value: Float = throw new java.util.NoSuchElementException +} +case class FloatSome(value: Float) extends FloatOption { + def isEmpty = false +} + +sealed abstract class DoubleOption { + def isEmpty: Boolean + def value: Double +} +case object DoubleNone extends DoubleOption { + def isEmpty = true + def value: Double = throw new java.util.NoSuchElementException +} +case class DoubleSome(value: Double) extends DoubleOption { + def isEmpty = false +} + +// The underlying implementation uses an exception that has no stack trace for +// the failure case, which is 20x faster than retaining stack traces. Therefore, +// we require no boxing of the results on the happy path. This slows down the +// unhappy path a little bit, but it's still on the same order of magnitude as +// the happy path. +// +// This API should only be used by people who know what they are doing. Note +// that JsonSource implementations consume one character beyond the number that is +// parsed, because there is no terminator character. +object UnsafeNumbers { + + // should never escape into user code + case object UnsafeNumber + extends Exception( + "if you see this a dev made a mistake using UnsafeNumbers" + ) + with NoStackTrace + + def byte(num: String): Byte = + byte_(new JsonSource(num), true) + def byte_(in: JsonSource, consume: Boolean): Byte = + long__(in, Byte.MinValue, Byte.MaxValue, consume).toByte + + def short(num: String): Short = + short_(new JsonSource(num), true) + def short_(in: JsonSource, consume: Boolean): Short = + long__(in, Short.MinValue, Short.MaxValue, consume).toShort + + def int(num: String): Int = + int_(new JsonSource(num), true) + def int_(in: JsonSource, consume: Boolean): Int = + long__(in, Int.MinValue, Int.MaxValue, consume).toInt + + def long(num: String): Long = + long_(new JsonSource(num), true) + def long_(in: JsonSource, consume: Boolean): Long = + long__(in, Long.MinValue, Long.MaxValue, consume) + + def bigInteger(num: String, max_bits: Int): java.math.BigInteger = + bigInteger_(new JsonSource(num), true, max_bits) + def bigInteger_( + in: JsonSource, + consume: Boolean, + max_bits: Int + ): java.math.BigInteger = { + var current: Int = in.read() + var negative = false + + if current == '-' then { + negative = true + current = in.read() + } else if current == '+' then current = in.read() + if current == -1 then throw UnsafeNumber + + bigDecimal__(in, consume, negative, current, true, max_bits).unscaledValue + } + + // measured faster than Character.isDigit + @inline private[this] def isDigit(i: Int): Boolean = + '0' <= i && i <= '9' + + // is it worth keeping this custom long__ instead of using bigInteger since it + // is approximately double the performance. + def long__(in: JsonSource, lower: Long, upper: Long, consume: Boolean): Long = { + var current: Int = 0 + + current = in.read() + if current == -1 then throw UnsafeNumber + var negative = false + if current == '-' then { + negative = true + current = in.read() + if current == -1 then throw UnsafeNumber + } else if current == '+' then { + current = in.read() + if current == -1 then throw UnsafeNumber + } + + if !isDigit(current) then throw UnsafeNumber + + var accum: Long = 0L + while { + { + val c = current - '0' + if accum <= longunderflow then + if accum < longunderflow then throw UnsafeNumber + else if accum == longunderflow && c == 9 then throw UnsafeNumber + // count down, not up, because it is larger + accum = accum * 10 - c // should never underflow + current = in.read() + }; current != -1 && isDigit(current) + } do () + + if consume && current != -1 then throw UnsafeNumber + + if negative then + if accum < lower || upper < accum then throw UnsafeNumber + else accum + else if accum == Long.MinValue then throw UnsafeNumber + else { + accum = -accum + if accum < lower || upper < accum then throw UnsafeNumber + else accum + } + } + + def float(num: String, max_bits: Int): Float = + float_(new JsonSource(num), true, max_bits) + + def float_(in: JsonSource, consume: Boolean, max_bits: Int): Float = { + var current: Int = in.read() + var negative = false + + def readAll(s: String): Unit = { + var i = 0 + val len = s.length + + while i < len do { + current = in.read() + if current != s(i) then throw UnsafeNumber + i += 1 + } + + current = in.read() // to be consistent read the terminator + + if consume && current != -1 then throw UnsafeNumber + } + + if current == 'N' then { + readAll("aN") + return Float.NaN + } + + if current == '-' then { + negative = true + current = in.read() + } else if current == '+' then { + current = in.read() + } + + if current == 'I' then { + readAll("nfinity") + + if negative then return Float.NegativeInfinity + else return Float.PositiveInfinity + } + + if current == -1 then throw UnsafeNumber + + val res = bigDecimal__(in, consume, negative = negative, initial = current, int_only = false, max_bits = max_bits) + + if negative && res.unscaledValue == java.math.BigInteger.ZERO then -0.0f + else res.floatValue + } + + def double(num: String, max_bits: Int): Double = + double_(new JsonSource(num), true, max_bits) + + def double_(in: JsonSource, consume: Boolean, max_bits: Int): Double = { + var current: Int = in.read() + var negative = false + + def readall(s: String): Unit = { + var i = 0 + val len = s.length + while i < len do { + current = in.read() + if current != s(i) then throw UnsafeNumber + i += 1 + } + current = in.read() // to be consistent read the terminator + if consume && current != -1 then throw UnsafeNumber + } + + if current == 'N' then { + readall("aN") + return Double.NaN + } + + if current == '-' then { + negative = true + current = in.read() + } else if current == '+' then current = in.read() + + if current == 'I' then { + readall("nfinity") + if negative then return Double.NegativeInfinity + else return Double.PositiveInfinity + } + + if current == -1 then throw UnsafeNumber + + // we could avoid going via BigDecimal if we wanted to do something like + // https://github.com/plokhotnyuk/jsoniter-scala/blob/56ff2a60e28aa27bd4788caf3b1557a558c00fa1/jsoniter-scala-core/jvm/src/main/scala/com/github/plokhotnyuk/jsoniter_scala/core/JsonJsonReader.scala#L1395-L1425 + // based on + // https://www.reddit.com/r/rust/comments/a6j5j1/making_rust_float_parsing_fast_and_correct + // + // the fallback of .doubleValue tends to call out to parseDouble which + // ultimately uses strtod from the system libraries and they may loop until + // the answer converges + // https://github.com/rust-lang/rust/pull/27307/files#diff-fe6c36003393c49bf7e5c413458d6d9cR43-R84 + val res = bigDecimal__(in, consume, negative, current, false, max_bits) + // BigDecimal doesn't have a negative zero, so we need to apply manually + if negative && res.unscaledValue == java.math.BigInteger.ZERO then -0.0 + // TODO implement Algorithm M or Bigcomp and avoid going via BigDecimal + else res.doubleValue + } + + def bigDecimal(num: String, max_bits: Int): java.math.BigDecimal = + bigDecimal_(new JsonSource(num), true, max_bits) + def bigDecimal_( + in: JsonSource, + consume: Boolean, + max_bits: Int + ): java.math.BigDecimal = { + var current: Int = in.read() + var negative = false + + if current == '-' then { + negative = true + current = in.read() + } else if current == '+' then current = in.read() + if current == -1 then throw UnsafeNumber + + bigDecimal__(in, consume, negative, current, false, max_bits) + } + + def bigDecimal__( + in: JsonSource, + consume: Boolean, + negative: Boolean, + initial: Int, + int_only: Boolean, + max_bits: Int + ): java.math.BigDecimal = { + var current: Int = initial + // record the significand as Long until it overflows, then swap to BigInteger + var sig: Long = -1 // -1 means it hasn't been seen yet + var sig_ : java.math.BigInteger = null // non-null wins over sig + var dot: Int = 0 // counts from the right + var exp: Int = 0 // implied + + def advance(): Boolean = { + current = in.read() + current != -1 + } + + // skip trailing zero on the left + while current == '0' do { + sig = 0 + if !advance() then return java.math.BigDecimal.ZERO + } + + def push_sig(): Unit = { + val c = current - '0' + // would be nice if there was a fused instruction... + if sig_ != null then { + sig_ = sig_ + .multiply(java.math.BigInteger.TEN) + .add(bigIntegers(c)) + // arbitrary limit on BigInteger size to avoid OOM attacks + if sig_.bitLength >= max_bits then throw UnsafeNumber + } else if sig >= longoverflow then + sig_ = java.math.BigInteger + .valueOf(sig) + .multiply(java.math.BigInteger.TEN) + .add(bigIntegers(c)) + else if sig < 0 then sig = c.toLong + else sig = sig * 10 + c + } + + def significand() = + if sig <= 0 then java.math.BigDecimal.ZERO + else { + val res = + if sig_ != null then new java.math.BigDecimal(sig_) + else new java.math.BigDecimal(sig) + if negative then res.negate else res + } + + while isDigit(current) do { + push_sig() + if !advance() then return significand() + } + + if int_only then { + if consume && current != -1 then throw UnsafeNumber + return significand() + } + + if current == '.' then { + if sig < 0 then sig = 0 // e.g. ".1" is shorthand for "0.1" + if !advance() then return significand() + while isDigit(current) do { + dot += 1 + if sig > 0 || current != '0' then push_sig() + // overflowed... + if dot < 0 then throw UnsafeNumber + advance() + } + } + + if sig < 0 then throw UnsafeNumber // no significand + + if current == 'E' || current == 'e' then exp = int_(in, consume) + else if consume && current != -1 then throw UnsafeNumber + + val scale = if dot < 1 then exp else exp - dot + val res = significand() + if scale != 0 then res.scaleByPowerOfTen(scale) + else res + } + // note that bigDecimal does not have a negative zero + private[this] val bigIntegers: Array[java.math.BigInteger] = + (0L to 9L).map(java.math.BigInteger.valueOf).toArray + private[this] val longunderflow: Long = Long.MinValue / 10L + private[this] val longoverflow: Long = Long.MaxValue / 10L +} diff --git a/src/main/scala/co.blocke.scalajack/json/ParseInstructions.scalax b/src/main/scala/co.blocke.scalajack/json/ParseInstructions.scalax deleted file mode 100644 index 3051b812..00000000 --- a/src/main/scala/co.blocke.scalajack/json/ParseInstructions.scalax +++ /dev/null @@ -1,11 +0,0 @@ -package co.blocke.scalajack -package json - - -sealed trait ParseInstruction - -// A JsonReader converts the JSON type to final Scala/Java type -case class ParseLong[T]( reader: JsonReader[Long,T] ) extends ParseInstruction -case class ParseString[T]( reader: JsonReader[String,T] ) extends ParseInstruction -case class ParseList[T]( reader: JsonReader[List[T],Seq[T]] ) extends ParseInstruction -case class ParseObject[T]( fields: Map[String, JsonReader[Map[String,?],T]] ) extends ParseInstruction \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json/ReaderModule.scalax b/src/main/scala/co.blocke.scalajack/json/ReaderModule.scalax deleted file mode 100644 index 7372b34c..00000000 --- a/src/main/scala/co.blocke.scalajack/json/ReaderModule.scalax +++ /dev/null @@ -1,24 +0,0 @@ -package co.blocke.scalajack -package json - -import co.blocke.scala_reflection.{RTypeRef, TypedName} -import scala.quoted.* -import scala.collection.mutable.HashMap -import scala.util.{Failure, Success, Try} - -trait ReaderModule: - val root: ReaderModule - def readerFn[T](ref: RTypeRef[T], isMapKey: Boolean = false)(using q: Quotes, tt: Type[T])(using cache: HashMap[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]]): Expr[(JsonConfig, JsonParser) => Either[ParseError, T]] - -case class TerminusReaderModule(extension: Option[ReaderModule], root: ReaderModule) extends ReaderModule: - def readerFn[T](ref: RTypeRef[T], isMapKey: Boolean = false)(using q: Quotes, tt: Type[T])(using cache: HashMap[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]]): Expr[(JsonConfig, JsonParser) => Either[ParseError, T]] = - ref match - case t => - val extResult = extension match - case None => Failure(JsonParseError("???")) // Should Never Happen(tm) - case Some(ext) => Try(ext.readerFn[T](t)) - extResult match - case Success(v) => v - case Failure(_) => - val className = Expr(t.name) - '{ (j: JsonConfig, p: JsonParser) => Left(JsonParseError("Unknown (or unsupported) RTypeRef class " + $className)) } diff --git a/src/main/scala/co.blocke.scalajack/json/ReaderModule2.scalax b/src/main/scala/co.blocke.scalajack/json/ReaderModule2.scalax deleted file mode 100644 index 172f6e9e..00000000 --- a/src/main/scala/co.blocke.scalajack/json/ReaderModule2.scalax +++ /dev/null @@ -1,35 +0,0 @@ -package co.blocke.scalajack -package json - -import co.blocke.scala_reflection.{RTypeRef, TypedName} -import scala.quoted.* -import scala.collection.mutable.HashMap -import scala.util.{Failure, Success, Try} - -trait ReaderModule: - val next: Option[ReaderModule] - val root: ReaderModule - - def refRead[T]( - ref: RTypeRef[T], - parserE: Expr[JsonParser], - cfgE: Expr[JsonConfig], - isMapKey: Boolean = false - )(using q: Quotes, tt: Type[T])(using cache: HashMap[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]]): Expr[Either[ParseError, T]] - -case class TerminusReaderModule(next: Option[ReaderModule], root: ReaderModule) extends ReaderModule: - - def refRead[T]( - ref: RTypeRef[T], - parserE: Expr[JsonParser], - cfgE: Expr[JsonConfig], - isMapKey: Boolean = false - )(using q: Quotes, tt: Type[T])(using cache: HashMap[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]]): Expr[Either[ParseError, T]] = - - val name = Expr(ref.name) - next match - case None => '{ Left(JsonParseError("Unknown (or unsupported) type " + $name)) } - case Some(ext) => - Try(ext.refRead[T](ref, parserE, cfgE, isMapKey)) match - case Success(v) => v - case Failure(_) => '{ Left(JsonParseError("Unknown (or unsupported) type " + $name)) } diff --git a/src/main/scala/co.blocke.scalajack/json/StringMatrix.scala b/src/main/scala/co.blocke.scalajack/json/StringMatrix.scala new file mode 100644 index 00000000..ffbb799f --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/StringMatrix.scala @@ -0,0 +1,84 @@ +package co.blocke.scalajack +package json + +// A data structure encoding a simple algorithm for Trie pruning: Given a list +// of strings, and a sequence of incoming characters, find the strings that +// match, by manually maintaining a bitset. Empty strings are not allowed. +// +// ScalaJack: Removal of ZIO's original aliases feature for speed--we don't need this. +// +final class StringMatrix(val xs: Array[String]) { + require(xs.forall(_.nonEmpty)) + require(xs.nonEmpty) + require(xs.length < 64) + + val width = xs.length + val height: Int = xs.map(_.length).max + val lengths: Array[Int] = xs.map(_.length) + val initial: Long = (0 until width).foldLeft(0L)((bs, r) => bs | (1L << r)) + + private val matrix: Array[Int] = { + val m = Array.fill[Int](width * height)(-1) + var string: Int = 0 + while string < width do { + val s = xs(string) + val len = s.length + var char: Int = 0 + while char < len do { + m(width * char + string) = s.codePointAt(char) + char += 1 + } + string += 1 + } + m + } + + private val resolve: Array[Int] = Array.tabulate[Int](xs.length)(identity) + + // must be called with increasing `char` (starting with bitset obtained from a + // call to 'initial', char = 0) + def update(bitset: Long, char: Int, c: Int): Long = + if char >= height then 0L // too long + else if bitset == 0L then 0L // everybody lost + else { + var latest: Long = bitset + val base: Int = width * char + + if bitset == initial then { // special case when it is dense since it is simple + var string: Int = 0 + while string < width do { + if matrix(base + string) != c then latest = latest ^ (1L << string) + string += 1 + } + } else { + var remaining: Long = bitset + while remaining != 0L do { + val string: Int = java.lang.Long.numberOfTrailingZeros(remaining) + val bit: Long = 1L << string + if matrix(base + string) != c then latest = latest ^ bit + remaining = remaining ^ bit + } + } + + latest + } + + // excludes entries that are not the given exact length + def exact(bitset: Long, length: Int): Long = + if length > height then 0L // too long + else { + var latest: Long = bitset + var remaining: Long = bitset + while remaining != 0L do { + val string: Int = java.lang.Long.numberOfTrailingZeros(remaining) + val bit: Long = 1L << string + if lengths(string) != length then latest = latest ^ bit + remaining = remaining ^ bit + } + latest + } + + def first(bitset: Long): Int = + if bitset == 0L then -1 + else resolve(java.lang.Long.numberOfTrailingZeros(bitset)) // never returns 64 +} diff --git a/src/main/scala/co.blocke.scalajack/json2/package.scala b/src/main/scala/co.blocke.scalajack/json/package.scala similarity index 77% rename from src/main/scala/co.blocke.scalajack/json2/package.scala rename to src/main/scala/co.blocke.scalajack/json/package.scala index 3bb815c2..5fd5d8e1 100644 --- a/src/main/scala/co.blocke.scalajack/json2/package.scala +++ b/src/main/scala/co.blocke.scalajack/json/package.scala @@ -1,5 +1,5 @@ package co.blocke.scalajack -package json2 +package json val BUFFER_EXCEEDED: Char = 7 // Old "BELL" ASCII value, used as a marker when we've run off the end of the known world -val END_OF_STRING: Char = 3 \ No newline at end of file +val END_OF_STRING: Char = 3 diff --git a/src/main/scala/co.blocke.scalajack/json2/ClassDecoder.scala b/src/main/scala/co.blocke.scalajack/json2/ClassDecoder.scala deleted file mode 100644 index 8b8d1618..00000000 --- a/src/main/scala/co.blocke.scalajack/json2/ClassDecoder.scala +++ /dev/null @@ -1,31 +0,0 @@ -package co.blocke.scalajack -package json2 - -import json.JsonParseError -import scala.annotation._ - -trait ClassDecoder[A] extends JsonDecoder[A]: - self => - def unsafeDecodeField(in: JsonReader): A - -object ClassDecoder: - def apply[A: scala.reflect.ClassTag](fields: Array[String], fieldDecoders: Array[JsonDecoder[_]]) = new JsonDecoder[A] { - val fieldMatrix = new StringMatrix(fields) - // not ideal--use Scala macros, but for now... - val constructor = summon[scala.reflect.ClassTag[A]].runtimeClass.getConstructors().head - - def unsafeDecode(in: JsonReader): A = - val fieldValues = new Array[Any](fields.length) - JsonParser.charWithWS(in, '{') - if JsonParser.firstField(in) then - var done = false - while !done do - val fieldIdx = JsonParser.parseField(in, fieldMatrix) - if fieldIdx < 0 then JsonParser.skipValue(in) - else - val dec = fieldDecoders(fieldIdx) - fieldValues(fieldIdx) = dec.unsafeDecode(in) - if !JsonParser.nextField(in) then done = true - else throw new JsonParseError("Expected fields!") - constructor.newInstance(fieldValues*).asInstanceOf[A] - } \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json2/JsonParser.scala b/src/main/scala/co.blocke.scalajack/json2/JsonParser.scala deleted file mode 100644 index 671b6e4b..00000000 --- a/src/main/scala/co.blocke.scalajack/json2/JsonParser.scala +++ /dev/null @@ -1,164 +0,0 @@ -package co.blocke.scalajack -package json2 - -import json.JsonParseError -import scala.annotation._ - -object JsonParser: - - private val ull: Array[Char] = "ull".toCharArray - private val alse: Array[Char] = "alse".toCharArray - private val rue: Array[Char] = "rue".toCharArray - - def parseBoolean(gen: JsonReader): Boolean = - (gen.readSkipWhitespace(): @switch) match { - case 't' => - readChars(gen, rue, "true") - true - case 'f' => - readChars(gen, alse, "false") - false - case c => - throw JsonParseError(s"Expected true or false value") - } - - def parseInt(gen: JsonReader): Int = { - checkNumber(gen) - try { - val i = UnsafeNumbers.int_(gen, false) - gen.retract() - i - } catch { - case UnsafeNumbers.UnsafeNumber => throw JsonParseError("Expected an Int") - } - } - - def parseString(gen: JsonReader): CharSequence = - charWithWS(gen, '"') - val sb = new FastStringBuilder(64) - while true do - val c = gen.readEscapedString() - if (c == END_OF_STRING) - return sb.buffer // mutable thing escapes, but cannot be changed - sb.append(c.toChar) - throw JsonParseError("Invalid string value detected") - - // Returns index of field name read in, or -1 if not found - def parseField(gen: JsonReader, fieldMatrix: StringMatrix): Int = - val f = enumeration(gen, fieldMatrix) - charWithWS(gen, ':') - f - - // True if we got anything besides a ], False for ] - def firstArrayElement(in: JsonReader): Boolean = - (in.readSkipWhitespace(): @switch) match - case ']' => false - case _ => - in.retract() - true - - def nextArrayElement(in: JsonReader): Boolean = - (in.readSkipWhitespace(): @switch) match - case ',' => true - case ']' => false - case c => throw JsonParseError(s"expected ',' or ']' got '$c'") - - // True if we got a string (implies a retraction), False for } - def firstField(in: JsonReader): Boolean = - (in.readSkipWhitespace(): @switch) match { - case '"' => true - case '}' => false - case c => - throw JsonParseError(s"expected string or '}' got '$c'") - } - - // True if we got a comma, and False for } - def nextField(in: JsonReader): Boolean = - (in.readSkipWhitespace(): @switch) match { - case ',' => - charWithWS(in, '"') - true - case '}' => false - case c => - throw JsonParseError(s"expected ',' or '}' got '$c'") - } - - def skipValue(in: JsonReader): Unit = - (in.readSkipWhitespace(): @switch) match { - case 'n' => readChars(in, ull, "null") - case 'f' => readChars(in, alse, "false") - case 't' => readChars(in, rue, "true") - case '{' => - if (firstField(in)) { - while ({ - { - char(in, '"') - skipString(in) - char(in, ':') - skipValue(in) - }; nextField(in) - }) () - } - case '[' => - if (firstArrayElement(in)) { - while ({ skipValue(in); nextArrayElement(in) }) () - } - case '"' => - skipString(in) - case '-' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '.' => - skipNumber(in) - case c => throw JsonParseError(s"Unexpected '$c'") - } - - def skipNumber(in: JsonReader): Unit = { - while (isNumber(in.read())) {} - in.retract() - } - - def skipString(in: JsonReader): Unit = - var i: Int = 0 - while ({ i = in.readEscapedString(); i != -1 }) () - - private def checkNumber(gen: JsonReader): Unit = - (gen.readSkipWhitespace(): @switch) match - case '-' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '.' => () - case c => throw JsonParseError(s"Expected a number, got $c") - gen.retract() - - @inline private[this] def isNumber(c: Char): Boolean = - (c: @switch) match - case '+' | '-' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '.' | 'e' | 'E' => true - case _ => false - - private inline def readChars( - gen: JsonReader, - expect: Array[Char], - errMsg: String - ): Unit = - var i: Int = 0 - while i < expect.length do - if gen.read() != expect(i) then throw JsonParseError(s"Expected ${errMsg}") - i += 1 - - @inline def charWithWS(gen: JsonReader, c: Char): Unit = - val got = gen.readSkipWhitespace() - if got != c then throw JsonParseError(s"Expected '$c' got '$got'") - - @inline def char(gen: JsonReader, c: Char): Unit = - val got = gen.read() - if got != c then throw JsonParseError(s"Expected '$c' got '$got'") - - def enumeration( - gen: JsonReader, - matrix: StringMatrix - ): Int = { - var i: Int = 0 - var bs: Long = matrix.initial - var c: Int = -1 - while ({ c = gen.readEscapedString(); c != END_OF_STRING }) { - bs = matrix.update(bs, i, c) - i += 1 - } - bs = matrix.exact(bs, i) - matrix.first(bs) - } \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json2/JsonReader.scala b/src/main/scala/co.blocke.scalajack/json2/JsonReader.scala deleted file mode 100644 index c0f57bde..00000000 --- a/src/main/scala/co.blocke.scalajack/json2/JsonReader.scala +++ /dev/null @@ -1,86 +0,0 @@ -package co.blocke.scalajack -package json2 - -import scala.annotation._ -import json.JsonParseError - -case class JsonReader( js: CharSequence ): - private var i = 0 - private val max = js.length - - inline def read(): Char = - if i < max then - i += 1 - history(i - 1) - else BUFFER_EXCEEDED - - inline def readSkipWhitespace(): Char = - var c: Char = 0 - while { c = read(); isWhitespace(c) } do () - c - - inline private def history(p: Int): Char = js.charAt(p) - - inline def retract() = i -= 1 - - inline private def isWhitespace(c: Char): Boolean = - (c: @switch) match { - case ' ' => true - case '\r' => true - case '\n' => true - case '\t' => true - case _ => false - } - - // Read, transforming escaped chars and stopping when we hit '"' - inline def readEscapedString(): Char = - read() match - case '\\' => - val c2 = read() - (c2: @switch) match - case '"' | '\\' | '/' => c2 - case 'b' => '\b' - case 'f' => '\f' - case 'n' => '\n' - case 'r' => '\r' - case 't' => '\t' - case 'u' => nextHex4() - case _ => throw JsonParseError(s"Invalid '\\${c2.toChar}' in string") - case '"' => END_OF_STRING - case BUFFER_EXCEEDED => throw new JsonParseError("Unexpected end of buffer") - case c => c - - inline def nextHex4(): Char = - var i: Int = 0 - var accum: Int = 0 - while i < 4 do - var c = read().toInt - if (c == BUFFER_EXCEEDED) - throw JsonParseError("unexpected EOB in string") - c = - if ('0' <= c && c <= '9') c - '0' - else if ('A' <= c && c <= 'F') c - 'A' + 10 - else if ('a' <= c && c <= 'f') c - 'a' + 10 - else - throw JsonParseError("Invalid hex character in string") - accum = accum * 16 + c - i += 1 - accum.toChar - - /* - Learnings: - - 1) Group implicit decoders into prioritized groups, favoring common/simple things like primitive types. Eg. esoteric things like date/time would be lowest. - 2) Spell out specific collection types BEFORE a general "built" colleciton type--saves cycles doing the conversion - 3) Use @switch macro to tell Scala to use a lookup table, not if-then-else in match-case statements - - Unknown--as of yet unknown how classes are handled.... - - TODO: - [ ] Learn how this StringMatrix works - [ ] Classes are *derived* decoders--magic! Clearly some mix of reading fields (set in StringMatrix), using FieldKeyDecoder and map decoder, then - constructed into a class.... - 1. In JsonDecoder, implement the [K,V] decoder. - 2. Manually construct a class decoder using StringMatrix and supply field-specific decoders for each field - 3. Try it! - */ \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json2/Numbers.scala b/src/main/scala/co.blocke.scalajack/json2/Numbers.scala deleted file mode 100644 index 40cc69f6..00000000 --- a/src/main/scala/co.blocke.scalajack/json2/Numbers.scala +++ /dev/null @@ -1,990 +0,0 @@ -/* - * Copyright 2019-2022 John A. De Goes and the ZIO Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package co.blocke.scalajack -package json2 - -import java.io._ -import scala.util.control.NoStackTrace - -/** - * Total, fast, number parsing. - * - * The Java and Scala standard libraries throw exceptions when we attempt to - * parse an invalid number. Unfortunately, exceptions are very expensive, and - * untrusted data can be maliciously constructed to DOS a server. - * - * This suite of functions mitigates against such attacks by building up the - * numbers one character at a time, which has been shown through extensive - * benchmarking to be orders of magnitude faster than exception-throwing stdlib - * parsers, for valid and invalid inputs. This approach, proposed by alexknvl, - * was also benchmarked against regexp-based pre-validation. - * - * Note that although the behaviour is identical to the Java stdlib when given - * the canonical form of a primitive (i.e. the .toString) of a number there may - * be differences in behaviour for non-canonical forms. e.g. the Java stdlib - * may reject "1.0" when parsed as an `BigInteger` but we may parse it as a - * `1`, although "1.1" would be rejected. Parsing of `BigDecimal` preserves the - * trailing zeros on the right but not on the left, e.g. "000.00001000" will be - * "1.000e-5", which is useful in cases where the trailing zeros denote - * measurement accuracy. - * - * `BigInteger`, `BigDecimal`, `Float` and `Double` have a configurable bit - * limit on the size of the significand, to avoid OOM style attacks, which is - * 128 bits by default. - * - * Results are contained in a specialisation of Option that avoids boxing. - */ -// TODO hex radix -// TODO octal radix -object SafeNumbers { - import UnsafeNumbers.UnsafeNumber - - def byte(num: String): ByteOption = - try ByteSome(UnsafeNumbers.byte(num)) - catch { case UnsafeNumber => ByteNone } - - def short(num: String): ShortOption = - try ShortSome(UnsafeNumbers.short(num)) - catch { case UnsafeNumber => ShortNone } - - def int(num: String): IntOption = - try IntSome(UnsafeNumbers.int(num)) - catch { case UnsafeNumber => IntNone } - - def long(num: String): LongOption = - try LongSome(UnsafeNumbers.long(num)) - catch { case UnsafeNumber => LongNone } - - def bigInteger( - num: String, - max_bits: Int = 128 - ): Option[java.math.BigInteger] = - try Some(UnsafeNumbers.bigInteger(num, max_bits)) - catch { case UnsafeNumber => None } - - def float(num: String, max_bits: Int = 128): FloatOption = - try FloatSome(UnsafeNumbers.float(num, max_bits)) - catch { case UnsafeNumber => FloatNone } - - def double(num: String, max_bits: Int = 128): DoubleOption = - try DoubleSome(UnsafeNumbers.double(num, max_bits)) - catch { case UnsafeNumber => DoubleNone } - - def bigDecimal( - num: String, - max_bits: Int = 128 - ): Option[java.math.BigDecimal] = - try Some(UnsafeNumbers.bigDecimal(num, max_bits)) - catch { case UnsafeNumber => None } - - // Based on the amazing work of Raffaello Giulietti - // "The Schubfach way to render doubles": https://drive.google.com/file/d/1luHhyQF9zKlM8yJ1nebU0OgVYhfC6CBN/view - // Sources with the license are here: https://github.com/c4f7fcce9cb06515/Schubfach/blob/3c92d3c9b1fead540616c918cdfef432bca53dfa/todec/src/math/DoubleToDecimal.java - def toString(x: Double): String = { - val bits = java.lang.Double.doubleToLongBits(x) - val ieeeExponent = (bits >> 52).toInt & 0x7ff - val ieeeMantissa = bits & 0xfffffffffffffL - if (ieeeExponent == 2047) { - if (x != x) """"NaN"""" - else if (bits < 0) """"-Infinity"""" - else """"Infinity"""" - } else { - val s = new java.lang.StringBuilder(24) - if (bits < 0) s.append('-') - if (x == 0.0f) s.append('0').append('.').append('0') - else { - var e = ieeeExponent - 1075 - var m = ieeeMantissa | 0x10000000000000L - var dv = 0L - var exp = 0 - if (e == 0) dv = m - else if (e >= -52 && e < 0 && m << e == 0) dv = m >> -e - else { - var expShift, expCorr = 0 - var cblShift = 2 - if (ieeeExponent == 0) { - e = -1074 - m = ieeeMantissa - if (ieeeMantissa < 3) { - m *= 10 - expShift = 1 - } - } else if (ieeeMantissa == 0 && ieeeExponent > 1) { - expCorr = 131007 - cblShift = 1 - } - exp = e * 315653 - expCorr >> 20 - val i = exp + 324 << 1 - val g1 = gs(i) - val g0 = gs(i + 1) - val h = (-exp * 108853 >> 15) + e + 2 - val cb = m << 2 - val outm1 = (m.toInt & 0x1) - 1 - val vb = rop(g1, g0, cb << h) - val vbls = rop(g1, g0, cb - cblShift << h) + outm1 - val vbrd = outm1 - rop(g1, g0, cb + 2 << h) - val s = vb >> 2 - if ( - s < 100 || { - dv = s / 10 // FIXME: Use Math.multiplyHigh(s, 1844674407370955168L) instead after dropping JDK 8 support - val sp40 = dv * 40 - val upin = (vbls - sp40).toInt - (((sp40 + vbrd).toInt + 40) ^ upin) >= 0 || { - dv += ~upin >>> 31 - exp += 1 - false - } - } - ) { - val s4 = s << 2 - val uin = (vbls - s4).toInt - dv = (~ { - if ((((s4 + vbrd).toInt + 4) ^ uin) < 0) uin - else (vb.toInt & 0x3) + (s.toInt & 0x1) - 3 - } >>> 31) + s - exp -= expShift - } - } - val len = digitCount(dv) - exp += len - 1 - if (exp < -3 || exp >= 7) { - val dotOff = s.length + 1 - s.append(dv) - var i = s.length - 1 - while (i > dotOff && s.charAt(i) == '0') i -= 1 - s.setLength(i + 1) - s.insert(dotOff, '.').append('E').append(exp) - } else if (exp < 0) { - s.append('0').append('.') - while ({ - exp += 1 - exp != 0 - }) s.append('0') - s.append(dv) - var i = s.length - 1 - while (s.charAt(i) == '0') i -= 1 - s.setLength(i + 1) - s - } else if (exp + 1 < len) { - val dotOff = s.length + exp + 1 - s.append(dv) - var i = s.length - 1 - while (s.charAt(i) == '0') i -= 1 - s.setLength(i + 1) - s.insert(dotOff, '.') - } else s.append(dv).append('.').append('0') - } - }.toString - } - - def toString(x: Float): String = { - val bits = java.lang.Float.floatToIntBits(x) - val ieeeExponent = (bits >> 23) & 0xff - val ieeeMantissa = bits & 0x7fffff - if (ieeeExponent == 255) { - if (x != x) """"NaN"""" - else if (bits < 0) """"-Infinity"""" - else """"Infinity"""" - } else { - val s = new java.lang.StringBuilder(16) - if (bits < 0) s.append('-') - if (x == 0.0f) s.append('0').append('.').append('0') - else { - var e = ieeeExponent - 150 - var m = ieeeMantissa | 0x800000 - var dv, exp = 0 - if (e == 0) dv = m - else if (e >= -23 && e < 0 && m << e == 0) dv = m >> -e - else { - var expShift, expCorr = 0 - var cblShift = 2 - if (ieeeExponent == 0) { - e = -149 - m = ieeeMantissa - if (ieeeMantissa < 8) { - m *= 10 - expShift = 1 - } - } else if (ieeeMantissa == 0 && ieeeExponent > 1) { - expCorr = 131007 - cblShift = 1 - } - exp = e * 315653 - expCorr >> 20 - val g1 = gs(exp + 324 << 1) + 1 - val h = (-exp * 108853 >> 15) + e + 1 - val cb = m << 2 - val outm1 = (m & 0x1) - 1 - val vb = rop(g1, cb << h) - val vbls = rop(g1, cb - cblShift << h) + outm1 - val vbrd = outm1 - rop(g1, cb + 2 << h) - val s = vb >> 2 - if ( - s < 100 || { - dv = (s * 3435973837L >>> 35).toInt // divide a positive int by 10 - val sp40 = dv * 40 - val upin = vbls - sp40 - ((sp40 + vbrd + 40) ^ upin) >= 0 || { - dv += ~upin >>> 31 - exp += 1 - false - } - } - ) { - val s4 = s << 2 - val uin = vbls - s4 - dv = (~ { - if (((s4 + vbrd + 4) ^ uin) < 0) uin - else (vb & 0x3) + (s & 0x1) - 3 - } >>> 31) + s - exp -= expShift - } - } - val len = digitCount(dv.toLong) - exp += len - 1 - if (exp < -3 || exp >= 7) { - val dotOff = s.length + 1 - s.append(dv) - var i = s.length - 1 - while (i > dotOff && s.charAt(i) == '0') i -= 1 - s.setLength(i + 1) - s.insert(dotOff, '.').append('E').append(exp) - } else if (exp < 0) { - s.append('0').append('.') - while ({ - exp += 1 - exp != 0 - }) s.append('0') - s.append(dv) - var i = s.length - 1 - while (s.charAt(i) == '0') i -= 1 - s.setLength(i + 1) - s - } else if (exp + 1 < len) { - val dotOff = s.length + exp + 1 - s.append(dv) - var i = s.length - 1 - while (s.charAt(i) == '0') i -= 1 - s.setLength(i + 1) - s.insert(dotOff, '.') - } else s.append(dv).append('.').append('0') - } - }.toString - } - - private[this] def rop(g1: Long, g0: Long, cp: Long): Long = { - val x1 = multiplyHigh(g0, cp) // FIXME: Use Math.multiplyHigh after dropping JDK 8 support - val z = (g1 * cp >>> 1) + x1 - val y1 = multiplyHigh(g1, cp) // FIXME: Use Math.multiplyHigh after dropping JDK 8 support - (z >>> 63) + y1 | -(z & 0x7fffffffffffffffL) >>> 63 - } - - private[this] def rop(g: Long, cp: Int): Int = { - val x1 = - ((g & 0xffffffffL) * cp >>> 32) + (g >>> 32) * cp // FIXME: Use Math.multiplyHigh after dropping JDK 8 support - (x1 >>> 31).toInt | -x1.toInt >>> 31 - } - - private[this] def multiplyHigh(x: Long, y: Long): Long = { - val x2 = x & 0xffffffffL - val y2 = y & 0xffffffffL - val b = x2 * y2 - val x1 = x >>> 32 - val y1 = y >>> 32 - val a = x1 * y1 - (((b >>> 32) + (x1 + x2) * (y1 + y2) - b - a) >>> 32) + a - } - - // Adoption of a nice trick form Daniel Lemire's blog that works for numbers up to 10^18: - // https://lemire.me/blog/2021/06/03/computing-the-number-of-digits-of-an-integer-even-faster/ - private[this] def digitCount(x: Long): Int = (offsets(java.lang.Long.numberOfLeadingZeros(x)) + x >> 58).toInt - - private final val offsets = Array( - 5088146770730811392L, 5088146770730811392L, 5088146770730811392L, 5088146770730811392L, 5088146770730811392L, - 5088146770730811392L, 5088146770730811392L, 5088146770730811392L, 4889916394579099648L, 4889916394579099648L, - 4889916394579099648L, 4610686018427387904L, 4610686018427387904L, 4610686018427387904L, 4610686018427387904L, - 4323355642275676160L, 4323355642275676160L, 4323355642275676160L, 4035215266123964416L, 4035215266123964416L, - 4035215266123964416L, 3746993889972252672L, 3746993889972252672L, 3746993889972252672L, 3746993889972252672L, - 3458764413820540928L, 3458764413820540928L, 3458764413820540928L, 3170534127668829184L, 3170534127668829184L, - 3170534127668829184L, 2882303760517117440L, 2882303760517117440L, 2882303760517117440L, 2882303760517117440L, - 2594073385265405696L, 2594073385265405696L, 2594073385265405696L, 2305843009203693952L, 2305843009203693952L, - 2305843009203693952L, 2017612633060982208L, 2017612633060982208L, 2017612633060982208L, 2017612633060982208L, - 1729382256910170464L, 1729382256910170464L, 1729382256910170464L, 1441151880758548720L, 1441151880758548720L, - 1441151880758548720L, 1152921504606845976L, 1152921504606845976L, 1152921504606845976L, 1152921504606845976L, - 864691128455135132L, 864691128455135132L, 864691128455135132L, 576460752303423478L, 576460752303423478L, - 576460752303423478L, 576460752303423478L, 576460752303423478L, 576460752303423478L, 576460752303423478L - ) - - private[this] val gs: Array[Long] = Array( - 5696189077778435540L, 6557778377634271669L, 9113902524445496865L, 1269073367360058862L, 7291122019556397492L, - 1015258693888047090L, 5832897615645117993L, 6346230177223303157L, 4666318092516094394L, 8766332956520552849L, - 7466108948025751031L, 8492109508320019073L, 5972887158420600825L, 4949013199285060097L, 4778309726736480660L, - 3959210559428048077L, 7645295562778369056L, 6334736895084876923L, 6116236450222695245L, 3223115108696946377L, - 4892989160178156196L, 2578492086957557102L, 7828782656285049914L, 436238524390181040L, 6263026125028039931L, - 2193665226883099993L, 5010420900022431944L, 9133629810990300641L, 8016673440035891111L, 9079784475471615541L, - 6413338752028712889L, 5419153173006337271L, 5130671001622970311L, 6179996945776024979L, 8209073602596752498L, - 6198646298499729642L, 6567258882077401998L, 8648265853541694037L, 5253807105661921599L, 1384589460720489745L, - 8406091369059074558L, 5904691951894693915L, 6724873095247259646L, 8413102376257665455L, 5379898476197807717L, - 4885807493635177203L, 8607837561916492348L, 438594360332462878L, 6886270049533193878L, 4040224303007880625L, - 5509016039626555102L, 6921528257148214824L, 8814425663402488164L, 3695747581953323071L, 7051540530721990531L, - 4801272472933613619L, 5641232424577592425L, 1996343570975935733L, 9025971879324147880L, 3194149713561497173L, - 7220777503459318304L, 2555319770849197738L, 5776622002767454643L, 3888930224050313352L, 4621297602213963714L, - 6800492993982161005L, 7394076163542341943L, 5346765568258592123L, 5915260930833873554L, 7966761269348784022L, - 4732208744667098843L, 8218083422849982379L, 7571533991467358150L, 2080887032334240837L, 6057227193173886520L, - 1664709625867392670L, 4845781754539109216L, 1331767700693914136L, 7753250807262574745L, 7664851543223128102L, - 6202600645810059796L, 6131881234578502482L, 4962080516648047837L, 3060830580291846824L, 7939328826636876539L, - 6742003335837910079L, 6351463061309501231L, 7238277076041283225L, 5081170449047600985L, 3945947253462071419L, - 8129872718476161576L, 6313515605539314269L, 6503898174780929261L, 3206138077060496254L, 5203118539824743409L, - 720236054277441842L, 8324989663719589454L, 4841726501585817270L, 6659991730975671563L, 5718055608639608977L, - 5327993384780537250L, 8263793301653597505L, 8524789415648859601L, 3998697245790980200L, 6819831532519087681L, - 1354283389261828999L, 5455865226015270144L, 8462124340893283845L, 8729384361624432231L, 8005375723316388668L, - 6983507489299545785L, 4559626171282155773L, 5586805991439636628L, 3647700937025724618L, 8938889586303418605L, - 3991647091870204227L, 7151111669042734884L, 3193317673496163382L, 5720889335234187907L, 4399328546167885867L, - 9153422936374700651L, 8883600081239572549L, 7322738349099760521L, 5262205657620702877L, 5858190679279808417L, - 2365090118725607140L, 4686552543423846733L, 7426095317093351197L, 7498484069478154774L, 813706063123630946L, - 5998787255582523819L, 2495639257869859918L, 4799029804466019055L, 3841185813666843096L, 7678447687145630488L, - 6145897301866948954L, 6142758149716504390L, 8606066656235469486L, 4914206519773203512L, 6884853324988375589L, - 7862730431637125620L, 3637067690497580296L, 6290184345309700496L, 2909654152398064237L, 5032147476247760397L, - 483048914547496228L, 8051435961996416635L, 2617552670646949126L, 6441148769597133308L, 2094042136517559301L, - 5152919015677706646L, 5364582523955957764L, 8244670425084330634L, 4893983223587622099L, 6595736340067464507L, - 5759860986241052841L, 5276589072053971606L, 918539974250931950L, 8442542515286354569L, 7003687180914356604L, - 6754034012229083655L, 7447624152102440445L, 5403227209783266924L, 5958099321681952356L, 8645163535653227079L, - 3998935692578258285L, 6916130828522581663L, 5043822961433561789L, 5532904662818065330L, 7724407183888759755L, - 8852647460508904529L, 3135679457367239799L, 7082117968407123623L, 4353217973264747001L, 5665694374725698898L, - 7171923193353707924L, 9065110999561118238L, 407030665140201709L, 7252088799648894590L, 4014973346854071690L, - 5801671039719115672L, 3211978677483257352L, 4641336831775292537L, 8103606164099471367L, 7426138930840468060L, - 5587072233075333540L, 5940911144672374448L, 4469657786460266832L, 4752728915737899558L, 7265075043910123789L, - 7604366265180639294L, 556073626030467093L, 6083493012144511435L, 2289533308195328836L, 4866794409715609148L, - 1831626646556263069L, 7786871055544974637L, 1085928227119065748L, 6229496844435979709L, 6402765803808118083L, - 4983597475548783767L, 6966887050417449628L, 7973755960878054028L, 3768321651184098759L, 6379004768702443222L, - 6704006135689189330L, 5103203814961954578L, 1673856093809441141L, 8165126103939127325L, 833495342724150664L, - 6532100883151301860L, 666796274179320531L, 5225680706521041488L, 533437019343456425L, 8361089130433666380L, - 8232196860433350926L, 6688871304346933104L, 6585757488346680741L, 5351097043477546483L, 7113280398048299755L, - 8561755269564074374L, 313202192651548637L, 6849404215651259499L, 2095236161492194072L, 5479523372521007599L, - 3520863336564710419L, 8767237396033612159L, 99358116390671185L, 7013789916826889727L, 1924160900483492110L, - 5611031933461511781L, 7073351942499659173L, 8977651093538418850L, 7628014293257544353L, 7182120874830735080L, - 6102411434606035483L, 5745696699864588064L, 4881929147684828386L, 9193114719783340903L, 2277063414182859933L, - 7354491775826672722L, 5510999546088198270L, 5883593420661338178L, 719450822128648293L, 4706874736529070542L, - 4264909472444828957L, 7530999578446512867L, 8668529563282681493L, 6024799662757210294L, 3245474835884234871L, - 4819839730205768235L, 4441054276078343059L, 7711743568329229176L, 7105686841725348894L, 6169394854663383341L, - 3839875066009323953L, 4935515883730706673L, 1227225645436504001L, 7896825413969130677L, 118886625327451240L, - 6317460331175304541L, 5629132522374826477L, 5053968264940243633L, 2658631610528906020L, 8086349223904389813L, - 2409136169475294470L, 6469079379123511850L, 5616657750322145900L, 5175263503298809480L, 4493326200257716720L, - 8280421605278095168L, 7189321920412346751L, 6624337284222476135L, 217434314217011916L, 5299469827377980908L, - 173947451373609533L, 8479151723804769452L, 7657013551681595899L, 6783321379043815562L, 2436262026603366396L, - 5426657103235052449L, 7483032843395558602L, 8682651365176083919L, 6438829327320028278L, 6946121092140867135L, - 6995737869226977784L, 5556896873712693708L, 5596590295381582227L, 8891034997940309933L, 7109870065239576402L, - 7112827998352247947L, 153872830078795637L, 5690262398681798357L, 5657121486175901994L, 9104419837890877372L, - 1672696748397622544L, 7283535870312701897L, 6872180620830963520L, 5826828696250161518L, 1808395681922860493L, - 4661462957000129214L, 5136065360280198718L, 7458340731200206743L, 2683681354335452463L, 5966672584960165394L, - 5836293898210272294L, 4773338067968132315L, 6513709525939172997L, 7637340908749011705L, 1198563204647900987L, - 6109872726999209364L, 958850563718320789L, 4887898181599367491L, 2611754858345611793L, 7820637090558987986L, - 489458958611068546L, 6256509672447190388L, 7770264796372675483L, 5005207737957752311L, 682188614985274902L, - 8008332380732403697L, 6625525006089305327L, 6406665904585922958L, 1611071190129533939L, 5125332723668738366L, - 4978205766845537474L, 8200532357869981386L, 4275780412210949635L, 6560425886295985109L, 1575949922397804547L, - 5248340709036788087L, 3105434345289198799L, 8397345134458860939L, 6813369359833673240L, 6717876107567088751L, - 7295369895237893754L, 5374300886053671001L, 3991621508819359841L, 8598881417685873602L, 2697245599369065423L, - 6879105134148698881L, 7691819701608117823L, 5503284107318959105L, 4308781353915539097L, 8805254571710334568L, - 6894050166264862555L, 7044203657368267654L, 9204588947753800367L, 5635362925894614123L, 9208345565573995455L, - 9016580681431382598L, 3665306460692661759L, 7213264545145106078L, 6621593983296039730L, 5770611636116084862L, - 8986624001378742108L, 4616489308892867890L, 3499950386361083363L, 7386382894228588624L, 5599920618177733380L, - 5909106315382870899L, 6324610901913141866L, 4727285052306296719L, 6904363128901468655L, 7563656083690074751L, - 5512957784129484362L, 6050924866952059801L, 2565691819932632328L, 4840739893561647841L, 207879048575150701L, - 7745183829698636545L, 5866629699833106606L, 6196147063758909236L, 4693303759866485285L, 4956917651007127389L, - 1909968600522233067L, 7931068241611403822L, 6745298575577483229L, 6344854593289123058L, 1706890045720076260L, - 5075883674631298446L, 5054860851317971332L, 8121413879410077514L, 4398428547366843807L, 6497131103528062011L, - 5363417245264430207L, 5197704882822449609L, 2446059388840589004L, 8316327812515919374L, 7603043836886852730L, - 6653062250012735499L, 7927109476880437346L, 5322449800010188399L, 8186361988875305038L, 8515919680016301439L, - 7564155960087622576L, 6812735744013041151L, 7895999175441053223L, 5450188595210432921L, 4472124932981887417L, - 8720301752336692674L, 3466051078029109543L, 6976241401869354139L, 4617515269794242796L, 5580993121495483311L, - 5538686623206349399L, 8929588994392773298L, 5172549782388248714L, 7143671195514218638L, 7827388640652509295L, - 5714936956411374911L, 727887690409141951L, 9143899130258199857L, 6698643526767492606L, 7315119304206559886L, - 1669566006672083762L, 5852095443365247908L, 8714350434821487656L, 4681676354692198327L, 1437457125744324640L, - 7490682167507517323L, 4144605808561874585L, 5992545734006013858L, 7005033461591409992L, 4794036587204811087L, - 70003547160262509L, 7670458539527697739L, 1956680082827375175L, 6136366831622158191L, 3410018473632855302L, - 4909093465297726553L, 883340371535329080L, 7854549544476362484L, 8792042223940347174L, 6283639635581089987L, - 8878308186523232901L, 5026911708464871990L, 3413297734476675998L, 8043058733543795184L, 5461276375162681596L, - 6434446986835036147L, 6213695507501100438L, 5147557589468028918L, 1281607591258970028L, 8236092143148846269L, - 205897738643396882L, 6588873714519077015L, 2009392598285672668L, 5271098971615261612L, 1607514078628538134L, - 8433758354584418579L, 4416696933176616176L, 6747006683667534863L, 5378031953912248102L, 5397605346934027890L, - 7991774377871708805L, 8636168555094444625L, 3563466967739958280L, 6908934844075555700L, 2850773574191966624L, - 5527147875260444560L, 2280618859353573299L, 8843436600416711296L, 3648990174965717279L, 7074749280333369037L, - 1074517732601618662L, 5659799424266695229L, 6393637408194160414L, 9055679078826712367L, 4695796630997791177L, - 7244543263061369894L, 67288490056322619L, 5795634610449095915L, 1898505199416013257L, 4636507688359276732L, - 1518804159532810606L, 7418412301374842771L, 4274761062623452130L, 5934729841099874217L, 1575134442727806543L, - 4747783872879899373L, 6794130776295110719L, 7596454196607838997L, 9025934834701221989L, 6077163357286271198L, - 3531399053019067268L, 4861730685829016958L, 6514468057157164137L, 7778769097326427133L, 8578474484080507458L, - 6223015277861141707L, 1328756365151540482L, 4978412222288913365L, 6597028314234097870L, 7965459555662261385L, - 1331873265919780784L, 6372367644529809108L, 1065498612735824627L, 5097894115623847286L, 4541747704930570025L, - 8156630584998155658L, 3577447513147001717L, 6525304467998524526L, 6551306825259511697L, 5220243574398819621L, - 3396371052836654196L, 8352389719038111394L, 1744844869796736390L, 6681911775230489115L, 3240550303208344274L, - 5345529420184391292L, 2592440242566675419L, 8552847072295026067L, 5992578795477635832L, 6842277657836020854L, - 1104714221640198342L, 5473822126268816683L, 2728445784683113836L, 8758115402030106693L, 2520838848122026975L, - 7006492321624085354L, 5706019893239531903L, 5605193857299268283L, 6409490321962580684L, 8968310171678829253L, - 8410510107769173933L, 7174648137343063403L, 1194384864102473662L, 5739718509874450722L, 4644856706023889253L, - 9183549615799121156L, 53073100154402158L, 7346839692639296924L, 7421156109607342373L, 5877471754111437539L, - 7781599295056829060L, 4701977403289150031L, 8069953843416418410L, 7523163845262640050L, 9222577334724359132L, - 6018531076210112040L, 7378061867779487306L, 4814824860968089632L, 5902449494223589845L, 7703719777548943412L, - 2065221561273923105L, 6162975822039154729L, 7186200471132003969L, 4930380657631323783L, 7593634784276558337L, - 7888609052210118054L, 1081769210616762369L, 6310887241768094443L, 2710089775864365057L, 5048709793414475554L, - 5857420635433402369L, 8077935669463160887L, 3837849794580578305L, 6462348535570528709L, 8604303057777328129L, - 5169878828456422967L, 8728116853592817665L, 8271806125530276748L, 6586289336264687617L, 6617444900424221398L, - 8958380283753660417L, 5293955920339377119L, 1632681004890062849L, 8470329472543003390L, 6301638422566010881L, - 6776263578034402712L, 5041310738052808705L, 5421010862427522170L, 343699775700336641L, 8673617379884035472L, - 549919641120538625L, 6938893903907228377L, 5973958935009296385L, 5551115123125782702L, 1089818333265526785L, - 8881784197001252323L, 3588383740595798017L, 7105427357601001858L, 6560055807218548737L, 5684341886080801486L, - 8937393460516749313L, 9094947017729282379L, 1387108685230112769L, 7275957614183425903L, 2954361355555045377L, - 5820766091346740722L, 6052837899185946625L, 4656612873077392578L, 1152921504606846977L, 7450580596923828125L, 1L, - 5960464477539062500L, 1L, 4768371582031250000L, 1L, 7629394531250000000L, 1L, 6103515625000000000L, 1L, - 4882812500000000000L, 1L, 7812500000000000000L, 1L, 6250000000000000000L, 1L, 5000000000000000000L, 1L, - 8000000000000000000L, 1L, 6400000000000000000L, 1L, 5120000000000000000L, 1L, 8192000000000000000L, 1L, - 6553600000000000000L, 1L, 5242880000000000000L, 1L, 8388608000000000000L, 1L, 6710886400000000000L, 1L, - 5368709120000000000L, 1L, 8589934592000000000L, 1L, 6871947673600000000L, 1L, 5497558138880000000L, 1L, - 8796093022208000000L, 1L, 7036874417766400000L, 1L, 5629499534213120000L, 1L, 9007199254740992000L, 1L, - 7205759403792793600L, 1L, 5764607523034234880L, 1L, 4611686018427387904L, 1L, 7378697629483820646L, - 3689348814741910324L, 5902958103587056517L, 1106804644422573097L, 4722366482869645213L, 6419466937650923963L, - 7555786372591432341L, 8426472692870523179L, 6044629098073145873L, 4896503746925463381L, 4835703278458516698L, - 7606551812282281028L, 7737125245533626718L, 1102436455425918676L, 6189700196426901374L, 4571297979082645264L, - 4951760157141521099L, 5501712790637071373L, 7922816251426433759L, 3268717242906448711L, 6338253001141147007L, - 4459648201696114131L, 5070602400912917605L, 9101741783469756789L, 8112963841460668169L, 5339414816696835055L, - 6490371073168534535L, 6116206260728423206L, 5192296858534827628L, 4892965008582738565L, 8307674973655724205L, - 5984069606361426541L, 6646139978924579364L, 4787255685089141233L, 5316911983139663491L, 5674478955442268148L, - 8507059173023461586L, 5389817513965718714L, 6805647338418769269L, 2467179603801619810L, 5444517870735015415L, - 3818418090412251009L, 8711228593176024664L, 6109468944659601615L, 6968982874540819731L, 6732249563098636453L, - 5575186299632655785L, 3541125243107954001L, 8920298079412249256L, 5665800388972726402L, 7136238463529799405L, - 2687965903807225960L, 5708990770823839524L, 2150372723045780768L, 9134385233318143238L, 7129945171615159552L, - 7307508186654514591L, 169932915179262157L, 5846006549323611672L, 7514643961627230372L, 4676805239458889338L, - 2322366354559873974L, 7482888383134222941L, 1871111759924843197L, 5986310706507378352L, 8875587037423695204L, - 4789048565205902682L, 3411120815197045840L, 7662477704329444291L, 7302467711686228506L, 6129982163463555433L, - 3997299761978027643L, 4903985730770844346L, 6887188624324332438L, 7846377169233350954L, 7330152984177021577L, - 6277101735386680763L, 7708796794712572423L, 5021681388309344611L, 633014213657192454L, 8034690221294951377L, - 6546845963964373411L, 6427752177035961102L, 1548127956429588405L, 5142201741628768881L, 6772525587256536209L, - 8227522786606030210L, 7146692124868547611L, 6582018229284824168L, 5717353699894838089L, 5265614583427859334L, - 8263231774657780795L, 8424983333484574935L, 7687147617339583786L, 6739986666787659948L, 6149718093871667029L, - 5391989333430127958L, 8609123289839243947L, 8627182933488204734L, 2706550819517059345L, 6901746346790563787L, - 4009915062984602637L, 5521397077432451029L, 8741955272500547595L, 8834235323891921647L, 8453105213888010667L, - 7067388259113537318L, 3073135356368498210L, 5653910607290829854L, 6147857099836708891L, 9046256971665327767L, - 4302548137625868741L, 7237005577332262213L, 8976061732213560478L, 5789604461865809771L, 1646826163657982898L, - 4631683569492647816L, 8696158560410206965L, 7410693711188236507L, 1001132845059645012L, 5928554968950589205L, - 6334929498160581494L, 4742843975160471364L, 5067943598528465196L, 7588550360256754183L, 2574686535532678828L, - 6070840288205403346L, 5749098043168053386L, 4856672230564322677L, 2754604027163487547L, 7770675568902916283L, - 6252040850832535236L, 6216540455122333026L, 8690981495407938512L, 4973232364097866421L, 5108110788955395648L, - 7957171782556586274L, 4483628447586722714L, 6365737426045269019L, 5431577165440333333L, 5092589940836215215L, - 6189936139723221828L, 8148143905337944345L, 680525786702379117L, 6518515124270355476L, 544420629361903293L, - 5214812099416284380L, 7814234132973343281L, 8343699359066055009L, 3279402575902573442L, 6674959487252844007L, - 4468196468093013915L, 5339967589802275205L, 9108580396587276617L, 8543948143683640329L, 5350356597684866779L, - 6835158514946912263L, 6124959685518848585L, 5468126811957529810L, 8589316563156989191L, 8749002899132047697L, - 4519534464196406897L, 6999202319305638157L, 9149650793469991003L, 5599361855444510526L, 3630371820034082479L, - 8958978968711216842L, 2119246097312621643L, 7167183174968973473L, 7229420099962962799L, 5733746539975178779L, - 249512857857504755L, 9173994463960286046L, 4088569387313917931L, 7339195571168228837L, 1426181102480179183L, - 5871356456934583069L, 6674968104097008831L, 4697085165547666455L, 7184648890648562227L, 7515336264876266329L, - 2272066188182923754L, 6012269011901013063L, 3662327357917294165L, 4809815209520810450L, 6619210701075745655L, - 7695704335233296721L, 1367365084866417240L, 6156563468186637376L, 8472589697376954439L, 4925250774549309901L, - 4933397350530608390L, 7880401239278895842L, 4204086946107063100L, 6304320991423116673L, 8897292778998515965L, - 5043456793138493339L, 1583811001085947287L, 8069530869021589342L, 6223446416479425982L, 6455624695217271474L, - 1289408318441630463L, 5164499756173817179L, 2876201062124259532L, 8263199609878107486L, 8291270514140725574L, - 6610559687902485989L, 4788342003941625298L, 5288447750321988791L, 5675348010524255400L, 8461516400515182066L, - 5391208002096898316L, 6769213120412145653L, 2468291994306563491L, 5415370496329716522L, 5663982410187161116L, - 8664592794127546436L, 1683674226815637140L, 6931674235302037148L, 8725637010936330358L, 5545339388241629719L, - 1446486386636198802L, 8872543021186607550L, 6003727033359828406L, 7098034416949286040L, 4802981626687862725L, - 5678427533559428832L, 3842385301350290180L, 9085484053695086131L, 7992490889531419449L, 7268387242956068905L, - 4549318304254180398L, 5814709794364855124L, 3639454643403344318L, 4651767835491884099L, 4756238122093630616L, - 7442828536787014559L, 2075957773236943501L, 5954262829429611647L, 3505440625960509963L, 4763410263543689317L, - 8338375722881273455L, 7621456421669902908L, 5962703527126216881L, 6097165137335922326L, 8459511636442883828L, - 4877732109868737861L, 4922934901783351901L, 7804371375789980578L, 4187347028111452718L, 6243497100631984462L, - 7039226437231072498L, 4994797680505587570L, 1942032335042947675L, 7991676288808940112L, 3107251736068716280L, - 6393341031047152089L, 8019824610967838509L, 5114672824837721671L, 8260534096145225969L, 8183476519740354675L, - 304133702235675419L, 6546781215792283740L, 243306961788540335L, 5237424972633826992L, 194645569430832268L, - 8379879956214123187L, 2156107318460286790L, 6703903964971298549L, 7258909076881094917L, 5363123171977038839L, - 7651801668875831096L, 8580997075163262143L, 6708859448088464268L, 6864797660130609714L, 9056436373212681737L, - 5491838128104487771L, 9089823505941100552L, 8786941004967180435L, 1630996757909074751L, 7029552803973744348L, - 1304797406327259801L, 5623642243178995478L, 4733186739803718164L, 8997827589086392765L, 5728424376314993901L, - 7198262071269114212L, 4582739501051995121L, 5758609657015291369L, 9200214822954461581L, 9213775451224466191L, - 9186320494614273045L, 7371020360979572953L, 5504381988320463275L, 5896816288783658362L, 8092854405398280943L, - 4717453031026926690L, 2784934709576714431L, 7547924849643082704L, 4455895535322743090L, 6038339879714466163L, - 5409390835629149634L, 4830671903771572930L, 8016861483245230030L, 7729075046034516689L, 3603606336337592240L, - 6183260036827613351L, 4727559476441028954L, 4946608029462090681L, 1937373173781868001L, 7914572847139345089L, - 8633820300163854287L, 6331658277711476071L, 8751730647502038591L, 5065326622169180857L, 5156710110630675711L, - 8104522595470689372L, 872038547525260492L, 6483618076376551497L, 6231654060133073878L, 5186894461101241198L, - 1295974433364548779L, 8299031137761985917L, 228884686012322885L, 6639224910209588733L, 5717130970922723793L, - 5311379928167670986L, 8263053591480089358L, 8498207885068273579L, 308164894771456841L, 6798566308054618863L, - 2091206323188120634L, 5438853046443695090L, 5362313873292406831L, 8702164874309912144L, 8579702197267850929L, - 6961731899447929715L, 8708436165185235905L, 5569385519558343772L, 6966748932148188724L, 8911016831293350036L, - 3768100661953281312L, 7128813465034680029L, 1169806122191669888L, 5703050772027744023L, 2780519305124291072L, - 9124881235244390437L, 2604156480827910553L, 7299904988195512349L, 7617348406775193928L, 5839923990556409879L, - 7938553132791110304L, 4671939192445127903L, 8195516913603843405L, 7475102707912204646L, 2044780617540418478L, - 5980082166329763716L, 9014522123516155429L, 4784065733063810973L, 5366943291441969181L, 7654505172902097557L, - 6742434858936195528L, 6123604138321678046L, 1704599072407046100L, 4898883310657342436L, 8742376887409457526L, - 7838213297051747899L, 1075082168258445910L, 6270570637641398319L, 2704740141977711890L, 5016456510113118655L, - 4008466520953124674L, 8026330416180989848L, 6413546433524999478L, 6421064332944791878L, 8820185961561909905L, - 5136851466355833503L, 1522125547136662440L, 8218962346169333605L, 590726468047704741L, 6575169876935466884L, - 472581174438163793L, 5260135901548373507L, 2222739346921486196L, 8416217442477397611L, 5401057362445333075L, - 6732973953981918089L, 2476171482585311299L, 5386379163185534471L, 3825611593439204201L, 8618206661096855154L, - 2431629734760816398L, 6894565328877484123L, 3789978195179608280L, 5515652263101987298L, 6721331370885596947L, - 8825043620963179677L, 8909455786045999954L, 7060034896770543742L, 3438215814094889640L, 5648027917416434993L, - 8284595873388777197L, 9036844667866295990L, 2187306953196312545L, 7229475734293036792L, 1749845562557050036L, - 5783580587434429433L, 6933899672158505514L, 4626864469947543547L, 13096515613938926L, 7402983151916069675L, - 1865628832353257443L, 5922386521532855740L, 1492503065882605955L, 4737909217226284592L, 1194002452706084764L, - 7580654747562055347L, 3755078331700690783L, 6064523798049644277L, 8538085887473418112L, 4851619038439715422L, - 3141119895236824166L, 7762590461503544675L, 6870466239749873827L, 6210072369202835740L, 5496372991799899062L, - 4968057895362268592L, 4397098393439919250L, 7948892632579629747L, 8880031836874825961L, 6359114106063703798L, - 3414676654757950445L, 5087291284850963038L, 6421090138548270680L, 8139666055761540861L, 8429069814306277926L, - 6511732844609232689L, 4898581444074067179L, 5209386275687386151L, 5763539562630208905L, 8335018041099817842L, - 5532314485466423924L, 6668014432879854274L, 736502773631228816L, 5334411546303883419L, 2433876626275938215L, - 8535058474086213470L, 7583551416783411467L, 6828046779268970776L, 6066841133426729173L, 5462437423415176621L, - 3008798499370428177L, 8739899877464282594L, 1124728784250774760L, 6991919901971426075L, 2744457434771574970L, - 5593535921577140860L, 2195565947817259976L, 8949657474523425376L, 3512905516507615961L, 7159725979618740301L, - 965650005835137607L, 5727780783694992240L, 8151217634151930732L, 9164449253911987585L, 3818576177788313364L, - 7331559403129590068L, 3054860942230650691L, 5865247522503672054L, 6133237568526430876L, 4692198018002937643L, - 6751264462192099863L, 7507516828804700229L, 8957348732136404618L, 6006013463043760183L, 9010553393080078856L, - 4804810770435008147L, 1674419492351197600L, 7687697232696013035L, 4523745595132871322L, 6150157786156810428L, - 3618996476106297057L, 4920126228925448342L, 6584545995626947969L, 7872201966280717348L, 3156575963519296104L, - 6297761573024573878L, 6214609585557347207L, 5038209258419659102L, 8661036483187788089L, 8061134813471454564L, - 6478960743616640295L, 6448907850777163651L, 7027843002264267398L, 5159126280621730921L, 3777599994440458757L, - 8254602048994769474L, 2354811176362823687L, 6603681639195815579L, 3728523348461214111L, 5282945311356652463L, - 4827493086139926451L, 8452712498170643941L, 5879314530452927160L, 6762169998536515153L, 2858777216991386566L, - 5409735998829212122L, 5976370588335019576L, 8655577598126739396L, 2183495311852210675L, 6924462078501391516L, - 9125493878965589187L, 5539569662801113213L, 5455720695801516188L, 8863311460481781141L, 6884478705911470739L, - 7090649168385424913L, 3662908557358221429L, 5672519334708339930L, 6619675660628487467L, 9076030935533343889L, - 1368109020150804139L, 7260824748426675111L, 2939161623491598473L, 5808659798741340089L, 506654891422323617L, - 4646927838993072071L, 2249998320508814055L, 7435084542388915313L, 9134020534926967972L, 5948067633911132251L, - 1773193205828708893L, 4758454107128905800L, 8797252194146787761L, 7613526571406249281L, 4852231473780084609L, - 6090821257124999425L, 2037110771653112526L, 4872657005699999540L, 1629688617322490021L, 7796251209119999264L, - 2607501787715984033L, 6237000967295999411L, 3930675837543742388L, 4989600773836799529L, 1299866262664038749L, - 7983361238138879246L, 5769134835004372321L, 6386688990511103397L, 2770633460632542696L, 5109351192408882717L, - 7750529990618899641L, 8174961907854212348L, 5022150355506418780L, 6539969526283369878L, 7707069099147045347L, - 5231975621026695903L, 631632057204770793L, 8371160993642713444L, 8389308921011453915L, 6696928794914170755L, - 8556121544180118293L, 5357543035931336604L, 6844897235344094635L, 8572068857490138567L, 5417812354437685931L, - 6857655085992110854L, 644901068808238421L, 5486124068793688683L, 2360595262417545899L, 8777798510069901893L, - 1932278012497118276L, 7022238808055921514L, 5235171224739604944L, 5617791046444737211L, 6032811387162639117L, - 8988465674311579538L, 5963149404718312264L, 7190772539449263630L, 8459868338516560134L, 5752618031559410904L, - 6767894670813248108L, 9204188850495057447L, 5294608251188331487L - ) -} - -// specialised Options to avoid boxing. Prefer .isEmpty guarded access to .value -// for higher performance: pattern matching is slightly slower. - -sealed abstract class ByteOption { - def isEmpty: Boolean - def value: Byte -} -case object ByteNone extends ByteOption { - def isEmpty = true - def value: Byte = throw new java.util.NoSuchElementException -} -case class ByteSome(value: Byte) extends ByteOption { - def isEmpty = false -} - -sealed abstract class ShortOption { - def isEmpty: Boolean - def value: Short -} -case object ShortNone extends ShortOption { - def isEmpty = true - def value: Short = throw new java.util.NoSuchElementException -} -case class ShortSome(value: Short) extends ShortOption { - def isEmpty = false -} - -sealed abstract class IntOption { - def isEmpty: Boolean - def value: Int -} -case object IntNone extends IntOption { - def isEmpty = true - def value: Int = throw new java.util.NoSuchElementException -} -case class IntSome(value: Int) extends IntOption { - def isEmpty = false -} - -sealed abstract class LongOption { - def isEmpty: Boolean - def value: Long -} -case object LongNone extends LongOption { - def isEmpty = true - def value: Long = throw new java.util.NoSuchElementException -} -case class LongSome(value: Long) extends LongOption { - def isEmpty = false -} - -sealed abstract class FloatOption { - def isEmpty: Boolean - def value: Float -} -case object FloatNone extends FloatOption { - def isEmpty = true - def value: Float = throw new java.util.NoSuchElementException -} -case class FloatSome(value: Float) extends FloatOption { - def isEmpty = false -} - -sealed abstract class DoubleOption { - def isEmpty: Boolean - def value: Double -} -case object DoubleNone extends DoubleOption { - def isEmpty = true - def value: Double = throw new java.util.NoSuchElementException -} -case class DoubleSome(value: Double) extends DoubleOption { - def isEmpty = false -} - -// The underlying implementation uses an exception that has no stack trace for -// the failure case, which is 20x faster than retaining stack traces. Therefore, -// we require no boxing of the results on the happy path. This slows down the -// unhappy path a little bit, but it's still on the same order of magnitude as -// the happy path. -// -// This API should only be used by people who know what they are doing. Note -// that JsonReader implementations consume one character beyond the number that is -// parsed, because there is no terminator character. -object UnsafeNumbers { - - // should never escape into user code - case object UnsafeNumber - extends Exception( - "if you see this a dev made a mistake using UnsafeNumbers" - ) - with NoStackTrace - - def byte(num: String): Byte = - byte_(new JsonReader(num), true) - def byte_(in: JsonReader, consume: Boolean): Byte = - long__(in, Byte.MinValue, Byte.MaxValue, consume).toByte - - def short(num: String): Short = - short_(new JsonReader(num), true) - def short_(in: JsonReader, consume: Boolean): Short = - long__(in, Short.MinValue, Short.MaxValue, consume).toShort - - def int(num: String): Int = - int_(new JsonReader(num), true) - def int_(in: JsonReader, consume: Boolean): Int = - long__(in, Int.MinValue, Int.MaxValue, consume).toInt - - def long(num: String): Long = - long_(new JsonReader(num), true) - def long_(in: JsonReader, consume: Boolean): Long = - long__(in, Long.MinValue, Long.MaxValue, consume) - - def bigInteger(num: String, max_bits: Int): java.math.BigInteger = - bigInteger_(new JsonReader(num), true, max_bits) - def bigInteger_( - in: JsonReader, - consume: Boolean, - max_bits: Int - ): java.math.BigInteger = { - var current: Int = in.read() - var negative = false - - if (current == '-') { - negative = true - current = in.read() - } else if (current == '+') - current = in.read() - if (current == -1) throw UnsafeNumber - - bigDecimal__(in, consume, negative, current, true, max_bits).unscaledValue - } - - // measured faster than Character.isDigit - @inline private[this] def isDigit(i: Int): Boolean = - '0' <= i && i <= '9' - - // is it worth keeping this custom long__ instead of using bigInteger since it - // is approximately double the performance. - def long__(in: JsonReader, lower: Long, upper: Long, consume: Boolean): Long = { - var current: Int = 0 - - current = in.read() - if (current == -1) throw UnsafeNumber - var negative = false - if (current == '-') { - negative = true - current = in.read() - if (current == -1) throw UnsafeNumber - } else if (current == '+') { - current = in.read() - if (current == -1) throw UnsafeNumber - } - - if (!isDigit(current)) - throw UnsafeNumber - - var accum: Long = 0L - while ({ - { - val c = current - '0' - if (accum <= longunderflow) - if (accum < longunderflow) - throw UnsafeNumber - else if (accum == longunderflow && c == 9) - throw UnsafeNumber - // count down, not up, because it is larger - accum = accum * 10 - c // should never underflow - current = in.read() - }; current != -1 && isDigit(current) - }) () - - if (consume && current != -1) throw UnsafeNumber - - if (negative) - if (accum < lower || upper < accum) throw UnsafeNumber - else accum - else if (accum == Long.MinValue) - throw UnsafeNumber - else { - accum = -accum - if (accum < lower || upper < accum) throw UnsafeNumber - else accum - } - } - - def float(num: String, max_bits: Int): Float = - float_(new JsonReader(num), true, max_bits) - - def float_(in: JsonReader, consume: Boolean, max_bits: Int): Float = { - var current: Int = in.read() - var negative = false - - def readAll(s: String): Unit = { - var i = 0 - val len = s.length - - while (i < len) { - current = in.read() - if (current != s(i)) throw UnsafeNumber - i += 1 - } - - current = in.read() // to be consistent read the terminator - - if (consume && current != -1) - throw UnsafeNumber - } - - if (current == 'N') { - readAll("aN") - return Float.NaN - } - - if (current == '-') { - negative = true - current = in.read() - } else if (current == '+') { - current = in.read() - } - - if (current == 'I') { - readAll("nfinity") - - if (negative) return Float.NegativeInfinity - else return Float.PositiveInfinity - } - - if (current == -1) - throw UnsafeNumber - - val res = bigDecimal__(in, consume, negative = negative, initial = current, int_only = false, max_bits = max_bits) - - if (negative && res.unscaledValue == java.math.BigInteger.ZERO) -0.0f - else res.floatValue - } - - def double(num: String, max_bits: Int): Double = - double_(new JsonReader(num), true, max_bits) - - def double_(in: JsonReader, consume: Boolean, max_bits: Int): Double = { - var current: Int = in.read() - var negative = false - - def readall(s: String): Unit = { - var i = 0 - val len = s.length - while (i < len) { - current = in.read() - if (current != s(i)) throw UnsafeNumber - i += 1 - } - current = in.read() // to be consistent read the terminator - if (consume && current != -1) throw UnsafeNumber - } - - if (current == 'N') { - readall("aN") - return Double.NaN - } - - if (current == '-') { - negative = true - current = in.read() - } else if (current == '+') - current = in.read() - - if (current == 'I') { - readall("nfinity") - if (negative) return Double.NegativeInfinity - else return Double.PositiveInfinity - } - - if (current == -1) throw UnsafeNumber - - // we could avoid going via BigDecimal if we wanted to do something like - // https://github.com/plokhotnyuk/jsoniter-scala/blob/56ff2a60e28aa27bd4788caf3b1557a558c00fa1/jsoniter-scala-core/jvm/src/main/scala/com/github/plokhotnyuk/jsoniter_scala/core/JsonJsonReader.scala#L1395-L1425 - // based on - // https://www.reddit.com/r/rust/comments/a6j5j1/making_rust_float_parsing_fast_and_correct - // - // the fallback of .doubleValue tends to call out to parseDouble which - // ultimately uses strtod from the system libraries and they may loop until - // the answer converges - // https://github.com/rust-lang/rust/pull/27307/files#diff-fe6c36003393c49bf7e5c413458d6d9cR43-R84 - val res = bigDecimal__(in, consume, negative, current, false, max_bits) - // BigDecimal doesn't have a negative zero, so we need to apply manually - if (negative && res.unscaledValue == java.math.BigInteger.ZERO) -0.0 - // TODO implement Algorithm M or Bigcomp and avoid going via BigDecimal - else res.doubleValue - } - - def bigDecimal(num: String, max_bits: Int): java.math.BigDecimal = - bigDecimal_(new JsonReader(num), true, max_bits) - def bigDecimal_( - in: JsonReader, - consume: Boolean, - max_bits: Int - ): java.math.BigDecimal = { - var current: Int = in.read() - var negative = false - - if (current == '-') { - negative = true - current = in.read() - } else if (current == '+') - current = in.read() - if (current == -1) throw UnsafeNumber - - bigDecimal__(in, consume, negative, current, false, max_bits) - } - - def bigDecimal__( - in: JsonReader, - consume: Boolean, - negative: Boolean, - initial: Int, - int_only: Boolean, - max_bits: Int - ): java.math.BigDecimal = { - var current: Int = initial - // record the significand as Long until it overflows, then swap to BigInteger - var sig: Long = -1 // -1 means it hasn't been seen yet - var sig_ : java.math.BigInteger = null // non-null wins over sig - var dot: Int = 0 // counts from the right - var exp: Int = 0 // implied - - def advance(): Boolean = { - current = in.read() - current != -1 - } - - // skip trailing zero on the left - while (current == '0') { - sig = 0 - if (!advance()) - return java.math.BigDecimal.ZERO - } - - def push_sig(): Unit = { - val c = current - '0' - // would be nice if there was a fused instruction... - if (sig_ != null) { - sig_ = sig_ - .multiply(java.math.BigInteger.TEN) - .add(bigIntegers(c)) - // arbitrary limit on BigInteger size to avoid OOM attacks - if (sig_.bitLength >= max_bits) - throw UnsafeNumber - } else if (sig >= longoverflow) - sig_ = java.math.BigInteger - .valueOf(sig) - .multiply(java.math.BigInteger.TEN) - .add(bigIntegers(c)) - else if (sig < 0) sig = c.toLong - else sig = sig * 10 + c - } - - def significand() = - if (sig <= 0) java.math.BigDecimal.ZERO - else { - val res = - if (sig_ != null) - new java.math.BigDecimal(sig_) - else - new java.math.BigDecimal(sig) - if (negative) res.negate else res - } - - while (isDigit(current)) { - push_sig() - if (!advance()) - return significand() - } - - if (int_only) { - if (consume && current != -1) - throw UnsafeNumber - return significand() - } - - if (current == '.') { - if (sig < 0) sig = 0 // e.g. ".1" is shorthand for "0.1" - if (!advance()) - return significand() - while (isDigit(current)) { - dot += 1 - if (sig > 0 || current != '0') - push_sig() - // overflowed... - if (dot < 0) throw UnsafeNumber - advance() - } - } - - if (sig < 0) throw UnsafeNumber // no significand - - if (current == 'E' || current == 'e') - exp = int_(in, consume) - else if (consume && current != -1) - throw UnsafeNumber - - val scale = if (dot < 1) exp else exp - dot - val res = significand() - if (scale != 0) - res.scaleByPowerOfTen(scale) - else - res - } - // note that bigDecimal does not have a negative zero - private[this] val bigIntegers: Array[java.math.BigInteger] = - (0L to 9L).map(java.math.BigInteger.valueOf).toArray - private[this] val longunderflow: Long = Long.MinValue / 10L - private[this] val longoverflow: Long = Long.MaxValue / 10L -} \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json2/StringMatrix.scala b/src/main/scala/co.blocke.scalajack/json2/StringMatrix.scala deleted file mode 100644 index 320d826a..00000000 --- a/src/main/scala/co.blocke.scalajack/json2/StringMatrix.scala +++ /dev/null @@ -1,92 +0,0 @@ -package co.blocke.scalajack -package json2 - -// A data structure encoding a simple algorithm for Trie pruning: Given a list -// of strings, and a sequence of incoming characters, find the strings that -// match, by manually maintaining a bitset. Empty strings are not allowed. -// -// -final class StringMatrix(val xs: Array[String], aliases: Array[(String, Int)] = Array.empty) { - require(xs.forall(_.nonEmpty)) - require(xs.nonEmpty) - require(xs.length + aliases.length < 64) - require(aliases.forall(_._1.nonEmpty)) - require(aliases.forall(p => p._2 >= 0 && p._2 < xs.length)) - - val width = xs.length + aliases.length - val height: Int = xs.map(_.length).max max (if (aliases.isEmpty) 0 else aliases.map(_._1.length).max) - val lengths: Array[Int] = xs.map(_.length) ++ aliases.map(_._1.length) - val initial: Long = (0 until width).foldLeft(0L)((bs, r) => bs | (1L << r)) - - private val matrix: Array[Int] = { - val m = Array.fill[Int](width * height)(-1) - var string: Int = 0 - while (string < width) { - val s = if (string < xs.length) xs(string) else aliases(string - xs.length)._1 - val len = s.length - var char: Int = 0 - while (char < len) { - m(width * char + string) = s.codePointAt(char) - char += 1 - } - string += 1 - } - m - } - - private val resolve: Array[Int] = { - val r = Array.tabulate[Int](xs.length + aliases.length)(identity) - aliases.zipWithIndex.foreach { case ((_, pi), i) => r(xs.length + i) = pi } - r - } - - // must be called with increasing `char` (starting with bitset obtained from a - // call to 'initial', char = 0) - def update(bitset: Long, char: Int, c: Int): Long = - if (char >= height) 0L // too long - else if (bitset == 0L) 0L // everybody lost - else { - var latest: Long = bitset - val base: Int = width * char - - if (bitset == initial) { // special case when it is dense since it is simple - var string: Int = 0 - while (string < width) { - if (matrix(base + string) != c) - latest = latest ^ (1L << string) - string += 1 - } - } else { - var remaining: Long = bitset - while (remaining != 0L) { - val string: Int = java.lang.Long.numberOfTrailingZeros(remaining) - val bit: Long = 1L << string - if (matrix(base + string) != c) - latest = latest ^ bit - remaining = remaining ^ bit - } - } - - latest - } - - // excludes entries that are not the given exact length - def exact(bitset: Long, length: Int): Long = - if (length > height) 0L // too long - else { - var latest: Long = bitset - var remaining: Long = bitset - while (remaining != 0L) { - val string: Int = java.lang.Long.numberOfTrailingZeros(remaining) - val bit: Long = 1L << string - if (lengths(string) != length) - latest = latest ^ bit - remaining = remaining ^ bit - } - latest - } - - def first(bitset: Long): Int = - if (bitset == 0L) -1 - else resolve(java.lang.Long.numberOfTrailingZeros(bitset)) // never returns 64 -} \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/parser/Instruction.scala b/src/main/scala/co.blocke.scalajack/parser/Instruction.scala deleted file mode 100644 index 479eab24..00000000 --- a/src/main/scala/co.blocke.scalajack/parser/Instruction.scala +++ /dev/null @@ -1,60 +0,0 @@ -package co.blocke.scalajack -package parser - -import scala.collection.Factory - -enum Expects{ - case ExpectBoolean, ExpectString, ExpectLong, ExpectBigLong, ExpectBigDouble, ExpectDouble, ExpectList, ExpectClass, ExpectObject, ExpectTuple -} - -// Don't need Expects for: -// expectList: ListInstruction is the only acceptable instruction with arity-1 (1 paramter) -// expectObject: ObjectInstruction is the only taker here: 2 params [K,V] -// expectClass: ClassInstrunction is the only applicable value - - -sealed trait Instruction: - val expect: Expects - type Z - -// Expects function requires no parameters -abstract class SimpleInstruction[I,O]() extends Instruction: - type Z = O - val expect: Expects - def transform(in: Either[ParseError, I]): Either[ParseError, O] - -// Expects function requires a single parameter -abstract class ListInstruction[E,T](val elementInstruction: Instruction) extends Instruction: - type Z = T - def transform(in: Either[ParseError, List[elementInstruction.Z]]): Either[ParseError, T] - -abstract class ClassInstruction[T](val fieldInstructions: Map[String,Instruction]) extends Instruction: - type Z = T - def transform(in: Either[ParseError, Map[String,Any]]): Either[ParseError, T] - -//------------------------------------------------------------- - -case class BooleanInstruction() extends SimpleInstruction[Boolean,Boolean]: - val expect: Expects = Expects.ExpectBoolean - def transform(in: Either[ParseError, Boolean]): Either[ParseError, Boolean] = in - -case class CharInstruction() extends SimpleInstruction[String,Char]: - val expect: Expects = Expects.ExpectString - def transform(in: Either[ParseError, String]): Either[ParseError, Char] = in.map(_.head) - -case class IntInstruction() extends SimpleInstruction[Long,Int]: - val expect: Expects = Expects.ExpectLong - def transform(in: Either[ParseError, Long]): Either[ParseError, Int] = in.map(_.intValue) - -case class StringInstruction() extends SimpleInstruction[String,String]: - val expect: Expects = Expects.ExpectString - def transform(in: Either[ParseError, String]): Either[ParseError, String] = in - -case class SeqInstruction[E,T](override val elementInstruction: Instruction)(using Factory[E,T]) extends ListInstruction[E,T](elementInstruction): - val expect: Expects = Expects.ExpectList - inline def collectionConvert[X,Y](in:List[X])(using Factory[X,Y]) = in.to( summon[Factory[X,Y]] ) - def transform(in: Either[ParseError, List[elementInstruction.Z]]): Either[ParseError, T] = in.map(e => collectionConvert[E,T](e.asInstanceOf[List[E]])) - -case class ScalaClassInstruction[T](override val fieldInstructions: Map[String,Instruction], instantiator: Map[String,?]=>T) extends ClassInstruction[T](fieldInstructions): - val expect: Expects = Expects.ExpectClass - def transform(in: Either[ParseError, Map[String,Any]]): Either[ParseError,T] = in.map(instantiator(_)) diff --git a/src/main/scala/co.blocke.scalajack/parser/Parser.scala b/src/main/scala/co.blocke.scalajack/parser/Parser.scala deleted file mode 100644 index 97e9c8fd..00000000 --- a/src/main/scala/co.blocke.scalajack/parser/Parser.scala +++ /dev/null @@ -1,40 +0,0 @@ -package co.blocke.scalajack -package parser - -import scala.collection.mutable.HashMap - -abstract class ParseError(message: String) extends Throwable(message) - - -trait Parser: - - inline def parse(inst: Instruction): Either[ParseError, inst.Z] = - (inst.expect match - case Expects.ExpectString => inst.asInstanceOf[SimpleInstruction[String,?]].transform(parseString()) - case Expects.ExpectLong => inst.asInstanceOf[SimpleInstruction[Long,?]].transform(parseLong()) - case Expects.ExpectBoolean => parseBoolean() - case Expects.ExpectList => - val z = inst.asInstanceOf[ListInstruction[?,inst.Z]] - z.transform(parseList(z.elementInstruction)) - case Expects.ExpectClass => - val z = inst.asInstanceOf[ScalaClassInstruction[inst.Z]] - z.transform(parseClass(z.fieldInstructions, HashMap.empty[String,Any])) // TODO: HashMap of default values, option:None, etc - case Expects.ExpectObject => Left(new json.JsonParseError("Unupported")) - case Expects.ExpectTuple => Left(new json.JsonParseError("Unupported")) - case Expects.ExpectDouble => Left(new json.JsonParseError("Unupported")) - case Expects.ExpectBigLong => Left(new json.JsonParseError("Unupported")) - case Expects.ExpectBigDouble => Left(new json.JsonParseError("Unupported")) - ).asInstanceOf[Either[ParseError,inst.Z]] - - - //----------------------------------------------------- - - // Parse the raw primitives - inline def parseBoolean(): Either[ParseError, Boolean] - inline def parseString(): Either[ParseError, String] - inline def parseLong(): Either[ParseError, Long] - def parseList(inst: Instruction): Either[ParseError, List[inst.Z]] - def parseClass(inst: Map[String,Instruction], fieldValues: HashMap[String,Any]): Either[ParseError, Map[String,Any]] - - //----------------------------------------------------- - diff --git a/src/main/scala/co.blocke.scalajack/parser/Reader.scalax b/src/main/scala/co.blocke.scalajack/parser/Reader.scalax deleted file mode 100644 index ef89c4bf..00000000 --- a/src/main/scala/co.blocke.scalajack/parser/Reader.scalax +++ /dev/null @@ -1,33 +0,0 @@ -package co.blocke.scalajack -package parser - -import scala.collection.Factory - -trait Parser - -/** - * A Reader's job is simply this: to accept as input raw data from a Parser in one of the basic parser data types - * and produce a well-typed result fit for consumption. Raw Parser types are: - * - String - * - Long - * - BigLong - * - Double - * - BigDouble - * - Object (differs from map in that the values, representing fields, will likely have different types) - * - List[T] - * - Map[K,V] - */ -trait Reader - -class CharReader() extends Reader: - inline def read(p: Parser, data: String): Char = data.head - -class StringReader() extends Reader: - inline def read(p: Parser, data: String): String = data - -case class IntReader() extends Reader: - inline def read(p: Parser, data: Long): Int = data.intValue - -case class ListReader() extends Reader: - inline def collectionConvert[T,U](in:List[T])(using Factory[T, U]) = in.to( summon[Factory[T,U]] ) - inline def read[E,T](p: Parser, data: List[E])(using Factory[E,T]): T = collectionConvert[E,T](data) diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index 04d069b7..5a424718 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -14,13 +14,20 @@ object RunMe extends App: try - import json2.* - import json.JsonParseError + import json.* + + val js = """[{ + "street": "123 Main Street", + "city": "Anytown", + "state": "CA", + "postal_code": "12345" + }]""" + println(ScalaJack.read[Record](jsData)) - /* StringMatrix experiments.... - */ + /* StringMatrix experiments.... + */ - /* + /* val matrix = new StringMatrix(List("foo", "bar", "baz").toArray, Array(("boom", 0))) val r = JsonReader("boom\" asdf ") var i: Int = 0 @@ -32,12 +39,13 @@ object RunMe extends App: } bs = matrix.exact(bs, i) println("HERE: " + matrix.first(bs)) - */ + */ - // val numList = """[["1","2,3"],["4","5"]]""" - // val dec = JsonDecoder[List[List[String]]] - // println(dec.decodeJson(numList)) + // val numList = """[["1","2,3"],["4","5"]]""" + // val dec = JsonDecoder[List[List[String]]] + // println(dec.decodeJson(numList)) + /* implicit val addrDecoder: JsonDecoder[Address] = ClassDecoder( Array("street", "city", "state", "postal_code"), Array(JsonDecoder[String], JsonDecoder[String], JsonDecoder[String], JsonDecoder[String]) @@ -62,6 +70,7 @@ object RunMe extends App: val addr = """{"street":"319 Hampton Ct","city":"Coppell","state":"TX","postal_code":"75019"}""" val dec2 = JsonDecoder[Address] println(JsonDecoder[Record].decodeJson(jsData)) + */ /* import parser.* diff --git a/src/main/scala/co.blocke.scalajack/run/Record.scala b/src/main/scala/co.blocke.scalajack/run/Record.scala index 76b3f79f..aa726dd1 100644 --- a/src/main/scala/co.blocke.scalajack/run/Record.scala +++ b/src/main/scala/co.blocke.scalajack/run/Record.scala @@ -2,43 +2,43 @@ package co.blocke.scalajack package run case class Person( - name: String, - age: Int, - address: Address, - email: String, - phone_numbers: List[String], - is_employed: Boolean + name: String, + age: Int, + address: Address, + email: String, + phone_numbers: List[String], + is_employed: Boolean ) case class Address( - street: String, - city: String, - state: String, - postal_code: String + street: String, + city: String, + state: String, + postal_code: String ) case class Friend( - name: String, - age: Int, - email: String + name: String, + age: Int, + email: String ) case class Pet( - name: String, - species: String, - age: Int + name: String, + species: String, + age: Int ) case class Record( - person: Person, - hobbies: List[String], - friends: List[Friend], - pets: List[Pet] + person: Person, + hobbies: List[String], + friends: List[Friend], + pets: List[Pet] ) -case class Foo(name: String, age:Int) +case class Foo(name: String, age: Int) -val jsData = +val jsData = """{ "person": { "name": "John Doe", @@ -85,4 +85,4 @@ val jsData = "age": 3 } ] - }""" \ No newline at end of file + }""" From 4a7b7161d26cbabe7c9482bf8ce87cbbe1d2bbdf Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Thu, 16 Nov 2023 11:17:21 -0600 Subject: [PATCH 25/65] checkpoint --- benchmark/README.md | 7 +- benchmark/build.sbt | 3 +- .../src/main/scala/co.blocke/Benchmark.scala | 26 +- .../src/main/scala/co.blocke/ScalaJack.scala | 34 +- rnd/build.sbt | 53 + rnd/project/build.properties | 1 + rnd/project/metals.sbt | 6 + rnd/project/plugins.sbt | 1 + .../co/blocke/scalajack/ByteArrayAccess.java | 46 + .../co.blocke.scalajack/JsonWriter.scala | 3000 +++++++++++++++++ .../JsonWriterException.scala | 4 + .../main/scala/co.blocke.scalajack/Run.scala | 27 + .../co.blocke.scalajack/WriterConfig.scala | 53 + .../scalajack/json/ByteArrayAccess.java | 46 + .../scala/co.blocke.scalajack/ScalaJack.scala | 60 +- .../json/ClassDecoder.scala | 12 +- .../json/JsonDecoder.scalax | 81 - .../co.blocke.scalajack/json/JsonReader.scala | 61 +- .../co.blocke.scalajack/json/package.scala | 14 + .../json/{JsonDecoder.scala => sj.scala} | 26 +- .../scala/co.blocke.scalajack/run/Play.scala | 80 +- .../co.blocke.scalajack/run/Record.scala | 2 +- 22 files changed, 3380 insertions(+), 263 deletions(-) create mode 100644 rnd/build.sbt create mode 100644 rnd/project/build.properties create mode 100644 rnd/project/metals.sbt create mode 100644 rnd/project/plugins.sbt create mode 100644 rnd/src/main/java/co/blocke/scalajack/ByteArrayAccess.java create mode 100644 rnd/src/main/scala/co.blocke.scalajack/JsonWriter.scala create mode 100644 rnd/src/main/scala/co.blocke.scalajack/JsonWriterException.scala create mode 100644 rnd/src/main/scala/co.blocke.scalajack/Run.scala create mode 100644 rnd/src/main/scala/co.blocke.scalajack/WriterConfig.scala create mode 100644 src/main/java/co/blocke/scalajack/json/ByteArrayAccess.java delete mode 100644 src/main/scala/co.blocke.scalajack/json/JsonDecoder.scalax rename src/main/scala/co.blocke.scalajack/json/{JsonDecoder.scala => sj.scala} (74%) diff --git a/benchmark/README.md b/benchmark/README.md index bb407c4b..f7d9ffac 100644 --- a/benchmark/README.md +++ b/benchmark/README.md @@ -18,8 +18,9 @@ sbt "jmh:run -i 10 -wi 10 -f 2 -t 1 co.blocke.*" | Benchmark | Mode | Count | Score | Error | Units | |------------------|-------|-------:|----------------:|-------------:|-------| | Jsoniter | thrpt | 20 | 987991.329 | ± 6645.992 | ops/s | -| **ScalaJack 8** | thrpt | 20 | **633764.943**| ± 10394.860 | ops/s | +| **ScalaJack 8 (fast mode)** | thrpt | 20 | **642235.553**| ± 10394.860 | ops/s | | ZIOJson | thrpt | 20 | 586716.228 | ± 2542.783 | ops/s | +| **ScalaJack 8 (easy mode)** | thrpt | 20 | **426718.318**| ± 692.828 | ops/s | | Circe | thrpt | 20 | 266568.198 | ± 5695.754 | ops/s | | Play | thrpt | 20 | 207737.560 | ± 842.108 | ops/s | | Argonaut | thrpt | 20 | 197876.777 | ± 11181.751 | ops/s | @@ -59,8 +60,8 @@ I observed the older serializers processed JSON to/from an AST and used conventi parsing techniques; basically fortified editions of a simple JSON parser. ZIO-Json's impressive read performance wasn't achieved by any one thing, but rather a collection of well- applied techniques, including *not* using an intermediate AST. So naturally I incorporated -some of ZIO-Json's approach (and a bit of their code), stripped, refitted, and adapted to -ScalaJack, and read performance jumped to 633K. Nice! +some of ZIO-Json's approach (and a bit of their code) for JSON reading, stripped, refitted, +and adapted to ScalaJack, and read performance jumped to 633K. Nice! Jsoniter, it turns out, achieves its neck-breaking speed by going deep--very deep. They use a lot of low level byte arrays and bitwise operators, much as you'd expect in a C program, diff --git a/benchmark/build.sbt b/benchmark/build.sbt index e563f5a0..41de8e2e 100644 --- a/benchmark/build.sbt +++ b/benchmark/build.sbt @@ -36,8 +36,7 @@ lazy val benchmark = project libraryDependencies ++= Seq( "org.playframework" %% "play-json" % "3.0.1", "io.argonaut" %% "argonaut" % "6.3.9", - // "co.blocke" %% "scalajack" % "826a30_unknown", // Old-New - "co.blocke" %% "scalajack" % "e48b35_unknown", // New-New + "co.blocke" %% "scalajack" % "caaa5e_unknown", // New-New "co.blocke" %% "scala-reflection" % "sj_fixes_edbef8", "dev.zio" %% "zio-json" % "0.6.1", "org.typelevel" %% "fabric-core" % "1.12.6", diff --git a/benchmark/src/main/scala/co.blocke/Benchmark.scala b/benchmark/src/main/scala/co.blocke/Benchmark.scala index 40c61085..670d7c0e 100644 --- a/benchmark/src/main/scala/co.blocke/Benchmark.scala +++ b/benchmark/src/main/scala/co.blocke/Benchmark.scala @@ -43,24 +43,24 @@ trait HandTooledWritingBenchmark { @BenchmarkMode(Array(Mode.Throughput)) @OutputTimeUnit(TimeUnit.SECONDS) class ReadingBenchmark - extends CirceZ.CirceReadingBenchmark - with ScalaJackZ.ScalaJackReadingBenchmark - with JsoniterZ.JsoniterReadingBenchmark - with ZIOZ.ZIOJsonReadingBenchmark - with PlayZ.PlayReadingBenchmark - with ArgonautZ.ArgonautReadingBenchmark + // extends CirceZ.CirceReadingBenchmark + extends ScalaJackZ.ScalaJackReadingBenchmark + // with JsoniterZ.JsoniterReadingBenchmark + // with ZIOZ.ZIOJsonReadingBenchmark + // with PlayZ.PlayReadingBenchmark + // with ArgonautZ.ArgonautReadingBenchmark @State(Scope.Thread) @BenchmarkMode(Array(Mode.Throughput)) @OutputTimeUnit(TimeUnit.SECONDS) class WritingBenchmark - extends HandTooledWritingBenchmark - with CirceZ.CirceWritingBenchmark - with ScalaJackZ.ScalaJackWritingBenchmark - with JsoniterZ.JsoniterWritingBenchmark - with ZIOZ.ZIOJsonWritingBenchmark - with PlayZ.PlayWritingBenchmark - with ArgonautZ.ArgonautWritingBenchmark + // extends HandTooledWritingBenchmark + // with CirceZ.CirceWritingBenchmark + extends ScalaJackZ.ScalaJackWritingBenchmark + // with JsoniterZ.JsoniterWritingBenchmark + // with ZIOZ.ZIOJsonWritingBenchmark + // with PlayZ.PlayWritingBenchmark + // with ArgonautZ.ArgonautWritingBenchmark // "Old-New" ScalaJack // [info] Benchmark Mode Cnt Score Error Units diff --git a/benchmark/src/main/scala/co.blocke/ScalaJack.scala b/benchmark/src/main/scala/co.blocke/ScalaJack.scala index 0701cbe6..c019649d 100644 --- a/benchmark/src/main/scala/co.blocke/ScalaJack.scala +++ b/benchmark/src/main/scala/co.blocke/ScalaJack.scala @@ -5,39 +5,17 @@ import org.openjdk.jmh.annotations._ object ScalaJackZ: import co.blocke.scalajack.* - import json2.* - - implicit val addrDecoder: JsonDecoder[Address] = ClassDecoder( - Array("street", "city", "state", "postal_code"), - Array(JsonDecoder[String], JsonDecoder[String], JsonDecoder[String], JsonDecoder[String]) - ) - implicit val friendDecoder: JsonDecoder[Friend] = ClassDecoder( - Array("name", "age", "email"), - Array(JsonDecoder[String], JsonDecoder[Int], JsonDecoder[String]) - ) - implicit val petDecoder: JsonDecoder[Pet] = ClassDecoder( - Array("name", "species", "age"), - Array(JsonDecoder[String], JsonDecoder[String], JsonDecoder[Int]) - ) - implicit val PersonDecoder: JsonDecoder[Person] = ClassDecoder( - Array("name", "age", "address", "email", "phone_numbers", "is_employed"), - Array(JsonDecoder[String], JsonDecoder[Int], JsonDecoder[Address], JsonDecoder[String], JsonDecoder[List[String]], JsonDecoder[Boolean]) - ) - implicit val RecordDecoder: JsonDecoder[Record] = ClassDecoder( - Array("person", "hobbies", "friends", "pets"), - Array(JsonDecoder[Person], JsonDecoder[List[String]], JsonDecoder[List[Friend]], JsonDecoder[List[Pet]]) - ) - - // val jp = co.blocke.scalajack.json.JsonParser3(jsData) + import json.* + + implicit val blah: sj[Record] = ScalaJack.inspect[Record] trait ScalaJackReadingBenchmark{ @Benchmark - // def readRecordScalaJack = { jp.reset(); jp.parse() } // def readRecordScalaJack = ScalaJack.read[Record](jsData) - def readRecordScalaJack = JsonDecoder[Record].decodeJson(jsData) - } + def readRecordScalaJack = sj[Record].decodeJson(jsData) + } trait ScalaJackWritingBenchmark { @Benchmark def writeRecordScalaJack = ScalaJack.write(record) - } \ No newline at end of file + } diff --git a/rnd/build.sbt b/rnd/build.sbt new file mode 100644 index 00000000..705aed1f --- /dev/null +++ b/rnd/build.sbt @@ -0,0 +1,53 @@ +inThisBuild(List( + organization := "co.blocke", + homepage := Some(url("https://github.com/gzoller/ScalaJack")), + licenses := List("MIT" -> url("https://opensource.org/licenses/MIT")), + developers := List( + Developer( + "gzoller", + "Greg Zoller", + "gzoller@blocke.co", + url("http://www.blocke.co") + ) + ) +)) + +name := "rnd" +ThisBuild / organization := "co.blocke" +ThisBuild / scalaVersion := "3.3.0" + +lazy val root = project + .in(file(".")) + .settings(settings) + .settings( + name := "serializer", + Compile / packageBin / mappings += { + (baseDirectory.value / "plugin.properties") -> "plugin.properties" + }, + doc := null, // disable dottydoc for now + Compile / doc / sources := Seq(), + //sources in (Compile, doc) := Seq(), + Test / parallelExecution := false, + libraryDependencies ++= Seq( + "org.scalatest" %% "scalatest" % "3.2.17" % Test + ) + ) + +//========================== +// Settings +//========================== +lazy val settings = Seq( + javacOptions ++= Seq("-source", "1.8", "-target", "1.8"), + scalacOptions ++= compilerOptions +) + +lazy val compilerOptions = Seq( + "-unchecked", + "-feature", + "-language:implicitConversions", + "-deprecation", + // "-explain", + "-encoding", + "utf8" +) + diff --git a/rnd/project/build.properties b/rnd/project/build.properties new file mode 100644 index 00000000..27430827 --- /dev/null +++ b/rnd/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.9.6 diff --git a/rnd/project/metals.sbt b/rnd/project/metals.sbt new file mode 100644 index 00000000..cbb25c6a --- /dev/null +++ b/rnd/project/metals.sbt @@ -0,0 +1,6 @@ +// DO NOT EDIT! This file is auto-generated. + +// This file enables sbt-bloop to create bloop config files. + +addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.5.11") + diff --git a/rnd/project/plugins.sbt b/rnd/project/plugins.sbt new file mode 100644 index 00000000..7d517ef3 --- /dev/null +++ b/rnd/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.2") diff --git a/rnd/src/main/java/co/blocke/scalajack/ByteArrayAccess.java b/rnd/src/main/java/co/blocke/scalajack/ByteArrayAccess.java new file mode 100644 index 00000000..58e4313f --- /dev/null +++ b/rnd/src/main/java/co/blocke/scalajack/ByteArrayAccess.java @@ -0,0 +1,46 @@ +package co.blocke.scalajack; + +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; +import java.nio.ByteOrder; + +class ByteArrayAccess { // FIXME: Use Java wrapper as w/a for missing support of @PolymorphicSignature methods in Scala 3, see: https://github.com/lampepfl/dotty/issues/11332 + private static final VarHandle VH_LONG = + MethodHandles.byteArrayViewVarHandle(long[].class, ByteOrder.LITTLE_ENDIAN); + private static final VarHandle VH_INT = + MethodHandles.byteArrayViewVarHandle(int[].class, ByteOrder.LITTLE_ENDIAN); + private static final VarHandle VH_SHORT = + MethodHandles.byteArrayViewVarHandle(short[].class, ByteOrder.LITTLE_ENDIAN); + private static final VarHandle VH_LONG_REVERSED = + MethodHandles.byteArrayViewVarHandle(long[].class, ByteOrder.BIG_ENDIAN); + private static final VarHandle VH_INT_REVERSED = + MethodHandles.byteArrayViewVarHandle(int[].class, ByteOrder.BIG_ENDIAN); + + static void setLong(byte[] buf, int pos, long value) { + VH_LONG.set(buf, pos, value); + } + + static long getLong(byte[] buf, int pos) { + return (long) VH_LONG.get(buf, pos); + } + + static void setInt(byte[] buf, int pos, int value) { + VH_INT.set(buf, pos, value); + } + + static int getInt(byte[] buf, int pos) { + return (int) VH_INT.get(buf, pos); + } + + static void setShort(byte[] buf, int pos, short value) { + VH_SHORT.set(buf, pos, value); + } + + static void setLongReversed(byte[] buf, int pos, long value) { + VH_LONG_REVERSED.set(buf, pos, value); + } + + static int getIntReversed(byte[] buf, int pos) { + return (int) VH_INT_REVERSED.get(buf, pos); + } +} \ No newline at end of file diff --git a/rnd/src/main/scala/co.blocke.scalajack/JsonWriter.scala b/rnd/src/main/scala/co.blocke.scalajack/JsonWriter.scala new file mode 100644 index 00000000..61b621a0 --- /dev/null +++ b/rnd/src/main/scala/co.blocke.scalajack/JsonWriter.scala @@ -0,0 +1,3000 @@ +package co.blocke.scalajack + +import java.io.OutputStream +import java.math.BigInteger +import java.nio.charset.StandardCharsets +import java.nio.{BufferOverflowException, ByteBuffer} +import java.time._ +import java.util.UUID +import JsonWriter._ +import scala.annotation.tailrec +import scala.{specialized => sp} + +/** + * A writer for iterative serialization of JSON keys and values. + * + * @param buf an internal buffer for writing JSON data + * @param count the current position in the internal buffer + * @param limit the last position in the internal buffer + * @param indention the current indention level + * @param comma a flag indicating if the next element should be preceded by comma + * @param disableBufGrowing a flag indicating if growing of the internal buffer is disabled + * @param bbuf a byte buffer for writing JSON data + * @param out the output stream for writing JSON data + * @param config a writer configuration + */ +final class JsonWriter ( + private[this] var buf: Array[Byte] = new Array[Byte](32768), + private[this] var count: Int = 0, + private[this] var limit: Int = 32768, + private[this] var indention: Int = 0, + private[this] var comma: Boolean = false, + private[this] var disableBufGrowing: Boolean = false, + private[this] var bbuf: ByteBuffer = null, + private[this] var out: OutputStream = null, + private[this] var config: WriterConfig = null) { + + // Within any given thread, lets re-use this writer/buffer! Why re-allocate all this machinery many times? + def result = new String(buf, 0, count, StandardCharsets.UTF_8) + def reset = + count = 0 + comma = false + indention = 0 + + /** + * Writes a `Boolean` value as a JSON key. + * + * @param x the `Boolean` value to write + */ + def writeKey(x: Boolean): Unit = { + writeOptionalCommaAndIndentionBeforeKey() + writeBytes('"') + writeBoolean(x) + writeParenthesesWithColon() + } + + /** + * Writes a `Byte` value as a JSON key. + * + * @param x the `Byte` value to write + */ + def writeKey(x: Byte): Unit = { + writeOptionalCommaAndIndentionBeforeKey() + writeBytes('"') + writeByte(x) + writeParenthesesWithColon() + } + + /** + * Writes a `Char` value as a JSON key. + * + * @param x the `Char` value to write + * @throws JsonWriterException in case of `Char` value is a part of surrogate pair + */ + def writeKey(x: Char): Unit = { + writeOptionalCommaAndIndentionBeforeKey() + writeChar(x) + writeColon() + } + + /** + * Writes a `Short` value as a JSON key. + * + * @param x the `Short` value to write + */ + def writeKey(x: Short): Unit = { + writeOptionalCommaAndIndentionBeforeKey() + writeBytes('"') + writeShort(x) + writeParenthesesWithColon() + } + + /** + * Writes a `Int` value as a JSON key. + * + * @param x the `Int` value to write + */ + def writeKey(x: Int): Unit = { + writeOptionalCommaAndIndentionBeforeKey() + writeBytes('"') + writeInt(x) + writeParenthesesWithColon() + } + + /** + * Writes a `Long` value as a JSON key. + * + * @param x the `Long` value to write + */ + def writeKey(x: Long): Unit = { + writeOptionalCommaAndIndentionBeforeKey() + writeBytes('"') + writeLong(x) + writeParenthesesWithColon() + } + + /** + * Writes a `Float` value as a JSON key. + * + * @param x the `Float` value to write + * @throws JsonWriterException if the value is non-finite + */ + def writeKey(x: Float): Unit = { + writeOptionalCommaAndIndentionBeforeKey() + writeBytes('"') + writeFloat(x) + writeParenthesesWithColon() + } + + /** + * Writes a `Double` value as a JSON key. + * + * @param x the `Double` value to write + * @throws JsonWriterException if the value is non-finite + */ + def writeKey(x: Double): Unit = { + writeOptionalCommaAndIndentionBeforeKey() + writeBytes('"') + writeDouble(x) + writeParenthesesWithColon() + } + + /** + * Writes a `BigInt` value as a JSON key. + * + * @param x the `BigInt` value to write + */ + def writeKey(x: BigInt): Unit = { + writeOptionalCommaAndIndentionBeforeKey() + writeBytes('"') + if (x.isValidLong) writeLong(x.longValue) + else writeBigInteger(x.bigInteger, null) + writeParenthesesWithColon() + } + + /** + * Writes a `BigDecimal` value as a JSON key. + * + * @param x the `BigDecimal` value to write + */ + def writeKey(x: BigDecimal): Unit = { + writeOptionalCommaAndIndentionBeforeKey() + writeBytes('"') + writeBigDecimal(x.bigDecimal) + writeParenthesesWithColon() + } + + /** + * Writes a [[java.util.UUID]] value as a JSON key. + * + * @param x the [[java.util.UUID]] value to write + */ + def writeKey(x: UUID): Unit = { + writeOptionalCommaAndIndentionBeforeKey() + writeUUID(x.getMostSignificantBits, x.getLeastSignificantBits) + writeColon() + } + + /** + * Writes a `String` value as a JSON key. + * + * @param x the `String` value to write + * @throws JsonWriterException if the provided string has an illegal surrogate pair + */ + def writeKey(x: String): Unit = count = { + val indention = this.indention + var pos = ensureBufCapacity(indention + 10) + val buf = this.buf + if (comma) { + comma = false + buf(pos) = ',' + pos += 1 + if (indention != 0) pos = writeIndention(buf, pos, indention) + } + buf(pos) = '"' + pos += 1 + pos = writeString(x, 0, pos, Math.min(x.length, limit - pos - 1) + pos, escapedChars) + if (pos + 4 >= limit) pos = flushAndGrowBuf(4, pos) + ByteArrayAccess.setInt(this.buf, pos, 0x203A22) + if (indention > 0) pos += 1 + pos + 2 + } + + /** + * Writes a `String` value that doesn't require encoding or escaping as a JSON key. + * + * @note Use [[JsonWriter.isNonEscapedAscii]] for validation if the string is eligable for writing by this method. + * + * @param x the `String` value to write + */ + def writeNonEscapedAsciiKey(x: String): Unit = { + val len = x.length + val indention = this.indention + val required = indention + len + 10 + if (required <= config.preferredBufSize) { + var pos = ensureBufCapacity(required) + val buf = this.buf + if (comma) { + comma = false + buf(pos) = ',' + pos += 1 + if (indention != 0) pos = writeIndention(buf, pos, indention) + } + buf(pos) = '"' + pos += 1 + var i = 0 + while (i < len) { + buf(pos) = x.charAt(i).toByte + pos += 1 + i += 1 + } + ByteArrayAccess.setInt(buf, pos, 0x203A22) + if (indention > 0) pos += 1 + count = pos + 2 + } else writeLongNonEscapedAsciiKey(x) + } + + /** + * Writes a [[java.time.Duration]] value as a JSON key. + * + * @param x the [[java.time.Duration]] value to write + */ + def writeKey(x: Duration): Unit = { + writeOptionalCommaAndIndentionBeforeKey() + writeDuration(x) + writeColon() + } + + /** + * Writes a [[java.time.Duration]] value as a JSON key. + * + * @param x the [[java.time.Duration]] value to write + */ + def writeKey(x: Instant): Unit = { + writeOptionalCommaAndIndentionBeforeKey() + writeInstant(x) + writeColon() + } + + /** + * Writes a [[java.time.LocalDate]] value as a JSON key. + * + * @param x the [[java.time.LocalDate]] value to write + */ + def writeKey(x: LocalDate): Unit = { + writeOptionalCommaAndIndentionBeforeKey() + writeLocalDate(x) + writeColon() + } + + /** + * Writes a [[java.time.LocalDateTime]] value as a JSON key. + * + * @param x the [[java.time.LocalDateTime]] value to write + */ + def writeKey(x: LocalDateTime): Unit = { + writeOptionalCommaAndIndentionBeforeKey() + writeLocalDateTime(x) + writeColon() + } + + /** + * Writes a [[java.time.LocalTime]] value as a JSON key. + * + * @param x the [[java.time.LocalTime]] value to write + */ + def writeKey(x: LocalTime): Unit = { + writeOptionalCommaAndIndentionBeforeKey() + writeLocalTime(x) + writeColon() + } + + /** + * Writes a [[java.time.MonthDay]] value as a JSON key. + * + * @param x the [[java.time.MonthDay]] value to write + */ + def writeKey(x: MonthDay): Unit = { + writeOptionalCommaAndIndentionBeforeKey() + writeMonthDay(x) + writeColon() + } + + /** + * Writes a [[java.time.OffsetDateTime]] value as a JSON key. + * + * @param x the [[java.time.OffsetDateTime]] value to write + */ + def writeKey(x: OffsetDateTime): Unit = { + writeOptionalCommaAndIndentionBeforeKey() + writeOffsetDateTime(x) + writeColon() + } + + /** + * Writes a [[java.time.OffsetTime]] value as a JSON key. + * + * @param x the [[java.time.OffsetTime]] value to write + */ + def writeKey(x: OffsetTime): Unit = { + writeOptionalCommaAndIndentionBeforeKey() + writeOffsetTime(x) + writeColon() + } + + /** + * Writes a [[java.time.Period]] value as a JSON key. + * + * @param x the [[java.time.Period]] value to write + */ + def writeKey(x: Period): Unit = { + writeOptionalCommaAndIndentionBeforeKey() + writePeriod(x) + writeColon() + } + + /** + * Writes a [[java.time.Year]] value as a JSON key. + * + * @param x the [[java.time.Year]] value to write + */ + def writeKey(x: Year): Unit = { + writeOptionalCommaAndIndentionBeforeKey() + writeYear(x) + writeColon() + } + + /** + * Writes a [[java.time.YearMonth]] value as a JSON key. + * + * @param x the [[java.time.YearMonth]] value to write + */ + def writeKey(x: YearMonth): Unit = { + writeOptionalCommaAndIndentionBeforeKey() + writeYearMonth(x) + writeColon() + } + + /** + * Writes a [[java.time.ZonedDateTime]] value as a JSON key. + * + * @param x the [[java.time.ZonedDateTime]] value to write + */ + def writeKey(x: ZonedDateTime): Unit = { + writeOptionalCommaAndIndentionBeforeKey() + writeZonedDateTime(x) + writeColon() + } + + /** + * Writes a [[java.time.ZoneId]] value as a JSON key. + * + * @param x the [[java.time.ZoneId]] value to write + */ + def writeKey(x: ZoneId): Unit = { + writeOptionalCommaAndIndentionBeforeKey() + writeZoneId(x) + writeColon() + } + + /** + * Writes a [[java.time.ZoneOffset]] value as a JSON key. + * + * @param x the [[java.time.ZoneOffset]] value to write + */ + def writeKey(x: ZoneOffset): Unit = { + writeOptionalCommaAndIndentionBeforeKey() + writeZoneOffset(x) + writeColon() + } + + /** + * Throws a [[JsonWriterException]] with the given error message. + * + * @param msg the error message + * @throws JsonWriterException always + */ + def encodeError(msg: String): Nothing = + throw new JsonWriterException(msg, null, config.throwWriterExceptionWithStackTrace) + + /** + * Writes a `BigDecimal` value as a JSON value. + * + * @param x the `BigDecimal` value to write + */ + def writeVal(x: BigDecimal): Unit = { + writeOptionalCommaAndIndentionBeforeValue() + writeBigDecimal(x.bigDecimal) + } + + /** + * Writes a `BigInt` value as a JSON value. + * + * @param x the `BigInt` value to write + */ + def writeVal(x: BigInt): Unit = { + writeOptionalCommaAndIndentionBeforeValue() + if (x.isValidLong) writeLong(x.longValue) + else writeBigInteger(x.bigInteger, null) + } + + /** + * Writes a [[java.util.UUID]] value as a JSON value. + * + * @param x the [[java.util.UUID]] value to write + */ + def writeVal(x: UUID): Unit = { + writeOptionalCommaAndIndentionBeforeValue() + writeUUID(x.getMostSignificantBits, x.getLeastSignificantBits) + } + + /** + * Writes a `String` value as a JSON value. + * + * @param x the `String` value to write + * @throws JsonWriterException if the provided string has an illegal surrogate pair + */ + def writeVal(x: String): Unit = count = { + val indention = this.indention + var pos = ensureBufCapacity(indention + 10) + if (comma) { + buf(pos) = ',' + pos += 1 + if (indention != 0) pos = writeIndention(buf, pos, indention) + } else comma = true + buf(pos) = '"' + pos += 1 + pos = writeString(x, 0, pos, Math.min(x.length, limit - pos - 1) + pos, escapedChars) + buf(pos) = '"' + pos + 1 + } + + /** + * Writes a `String` value that doesn't require encoding or escaping as a JSON value. + * + * @note Use [[JsonWriter.isNonEscapedAscii]] for validation if the string is eligable for writing by this method. + * + * @param x the `String` value to write + */ + def writeNonEscapedAsciiVal(x: String): Unit = { + val len = x.length + val indention = this.indention + val required = indention + len + 10 + if (required <= config.preferredBufSize) { + var pos = ensureBufCapacity(required) + val buf = this.buf + if (comma) { + buf(pos) = ',' + pos += 1 + if (indention != 0) pos = writeIndention(buf, pos, indention) + } else comma = true + buf(pos) = '"' + pos += 1 + var i = 0 + while (i < len) { + buf(pos) = x.charAt(i).toByte + pos += 1 + i += 1 + } + buf(pos) = '"' + count = pos + 1 + } else writeLongNonEscapedAsciiVal(x) + } + + /** + * Writes a [[java.time.Duration]] value as a JSON value. + * + * @param x the [[java.time.Duration]] value to write + */ + def writeVal(x: Duration): Unit = { + writeOptionalCommaAndIndentionBeforeValue() + writeDuration(x) + } + + /** + * Writes a [[java.time.Instant]] value as a JSON value. + * + * @param x the [[java.time.Instant]] value to write + */ + def writeVal(x: Instant): Unit = { + writeOptionalCommaAndIndentionBeforeValue() + writeInstant(x) + } + + /** + * Writes a [[java.time.LocalDate]] value as a JSON value. + * + * @param x the [[java.time.LocalDate]] value to write + */ + def writeVal(x: LocalDate): Unit = { + writeOptionalCommaAndIndentionBeforeValue() + writeLocalDate(x) + } + + /** + * Writes a [[java.time.LocalDateTime]] value as a JSON value. + * + * @param x the [[java.time.LocalDateTime]] value to write + */ + def writeVal(x: LocalDateTime): Unit = { + writeOptionalCommaAndIndentionBeforeValue() + writeLocalDateTime(x) + } + + /** + * Writes a [[java.time.LocalTime]] value as a JSON value. + * + * @param x the [[java.time.LocalTime]] value to write + */ + def writeVal(x: LocalTime): Unit = { + writeOptionalCommaAndIndentionBeforeValue() + writeLocalTime(x) + } + + /** + * Writes a [[java.time.MonthDay]] value as a JSON value. + * + * @param x the [[java.time.MonthDay]] value to write + */ + def writeVal(x: MonthDay): Unit = { + writeOptionalCommaAndIndentionBeforeValue() + writeMonthDay(x) + } + + /** + * Writes a [[java.time.OffsetDateTime]] value as a JSON value. + * + * @param x the [[java.time.OffsetDateTime]] value to write + */ + def writeVal(x: OffsetDateTime): Unit = { + writeOptionalCommaAndIndentionBeforeValue() + writeOffsetDateTime(x) + } + + /** + * Writes a [[java.time.OffsetTime]] value as a JSON value. + * + * @param x the [[java.time.OffsetTime]] value to write + */ + def writeVal(x: OffsetTime): Unit = { + writeOptionalCommaAndIndentionBeforeValue() + writeOffsetTime(x) + } + + /** + * Writes a [[java.time.Period]] value as a JSON value. + * + * @param x the [[java.time.Period]] value to write + */ + def writeVal(x: Period): Unit = { + writeOptionalCommaAndIndentionBeforeValue() + writePeriod(x) + } + + /** + * Writes a [[java.time.Year]] value as a JSON value. + * + * @param x the [[java.time.Year]] value to write + */ + def writeVal(x: Year): Unit = { + writeOptionalCommaAndIndentionBeforeValue() + writeYear(x) + } + + /** + * Writes a [[java.time.YearMonth]] value as a JSON value. + * + * @param x the [[java.time.YearMonth]] value to write + */ + def writeVal(x: YearMonth): Unit = { + writeOptionalCommaAndIndentionBeforeValue() + writeYearMonth(x) + } + + /** + * Writes a [[java.time.ZonedDateTime]] value as a JSON value. + * + * @param x the [[java.time.ZonedDateTime]] value to write + */ + def writeVal(x: ZonedDateTime): Unit = { + writeOptionalCommaAndIndentionBeforeValue() + writeZonedDateTime(x) + } + + /** + * Writes a [[java.time.ZoneId]] value as a JSON value. + * + * @param x the [[java.time.ZoneId]] value to write + */ + def writeVal(x: ZoneId): Unit = { + writeOptionalCommaAndIndentionBeforeValue() + writeZoneId(x) + } + + /** + * Writes a [[java.time.ZoneOffset]] value as a JSON value. + * + * @param x the [[java.time.ZoneOffset]] value to write + */ + def writeVal(x: ZoneOffset): Unit = { + writeOptionalCommaAndIndentionBeforeValue() + writeZoneOffset(x) + } + + /** + * Writes a `Boolean` value as a JSON value. + * + * @param x the `Boolean` value to write + */ + def writeVal(x: Boolean): Unit = { + writeOptionalCommaAndIndentionBeforeValue() + writeBoolean(x) + } + + /** + * Writes a `Byte` value as a JSON value. + * + * @param x the `Byte` value to write + */ + def writeVal(x: Byte): Unit = { + writeOptionalCommaAndIndentionBeforeValue() + writeByte(x) + } + + /** + * Writes a `Short` value as a JSON value. + * + * @param x the `Short` value to write + */ + def writeVal(x: Short): Unit = { + writeOptionalCommaAndIndentionBeforeValue() + writeShort(x) + } + + /** + * Writes a `Char` value as a JSON key. + * + * @param x the `Char` value to write + * @throws JsonWriterException in case of `Char` value is a part of surrogate pair + */ + def writeVal(x: Char): Unit = { + writeOptionalCommaAndIndentionBeforeValue() + writeChar(x) + } + + /** + * Writes a `Int` value as a JSON value. + * + * @param x the `Int` value to write + */ + def writeVal(x: Int): Unit = { + writeOptionalCommaAndIndentionBeforeValue() + writeInt(x) + } + + /** + * Writes a `Long` value as a JSON value. + * + * @param x the `Long` value to write + */ + def writeVal(x: Long): Unit = { + writeOptionalCommaAndIndentionBeforeValue() + writeLong(x) + } + + /** + * Writes a `Float` value as a JSON value. + * + * @param x the `Float` value to write + * @throws JsonWriterException if the value is non-finite + */ + def writeVal(x: Float): Unit = { + writeOptionalCommaAndIndentionBeforeValue() + writeFloat(x) + } + + /** + * Writes a `Double` value as a JSON value. + * + * @param x the `Double` value to write + * @throws JsonWriterException if the value is non-finite + */ + def writeVal(x: Double): Unit = { + writeOptionalCommaAndIndentionBeforeValue() + writeDouble(x) + } + + /** + * Writes a `BigDecimal` value as a JSON string value. + * + * @param x the `BigDecimal` value to write + */ + def writeValAsString(x: BigDecimal): Unit = { + writeOptionalCommaAndIndentionBeforeValue() + writeBytes('"') + writeBigDecimal(x.bigDecimal) + writeBytes('"') + } + + /** + * Writes a `BigInt` value as a JSON string value. + * + * @param x the `BigInt` value to write + */ + def writeValAsString(x: BigInt): Unit = { + writeOptionalCommaAndIndentionBeforeValue() + writeBytes('"') + if (x.isValidLong) writeLong(x.longValue) + else writeBigInteger(x.bigInteger, null) + writeBytes('"') + } + + /** + * Writes a `Boolean` value as a JSON string value. + * + * @param x the `Boolean` value to write + */ + def writeValAsString(x: Boolean): Unit = { + writeOptionalCommaAndIndentionBeforeValue() + writeBytes('"') + writeBoolean(x) + writeBytes('"') + } + + /** + * Writes a `Byte` value as a JSON string value. + * + * @param x the `Byte` value to write + */ + def writeValAsString(x: Byte): Unit = { + writeOptionalCommaAndIndentionBeforeValue() + writeBytes('"') + writeByte(x) + writeBytes('"') + } + + /** + * Writes a `Short` value as a JSON string value. + * + * @param x the `Short` value to write + */ + def writeValAsString(x: Short): Unit = { + writeOptionalCommaAndIndentionBeforeValue() + writeBytes('"') + writeShort(x) + writeBytes('"') + } + + /** + * Writes a `Int` value as a JSON string value. + * + * @param x the `Int` value to write + */ + def writeValAsString(x: Int): Unit = { + writeOptionalCommaAndIndentionBeforeValue() + writeBytes('"') + writeInt(x) + writeBytes('"') + } + + /** + * Writes a `Long` value as a JSON string value. + * + * @param x the `Long` value to write + */ + def writeValAsString(x: Long): Unit = { + writeOptionalCommaAndIndentionBeforeValue() + writeBytes('"') + writeLong(x) + writeBytes('"') + } + + /** + * Writes a `Float` value as a JSON string value. + * + * @param x the `Float` value to write + * @throws JsonWriterException if the value is non-finite + */ + def writeValAsString(x: Float): Unit = { + writeOptionalCommaAndIndentionBeforeValue() + writeBytes('"') + writeFloat(x) + writeBytes('"') + } + + /** + * Writes a `Double` value as a JSON string value. + * + * @param x the `Double` value to write + * @throws JsonWriterException if the value is non-finite + */ + def writeValAsString(x: Double): Unit = { + writeOptionalCommaAndIndentionBeforeValue() + writeBytes('"') + writeDouble(x) + writeBytes('"') + } + + /** + * Writes a byte array as a JSON hexadecimal string value. + * + * @param bs the byte array to write + * @param lowerCase if `true`, outputs lowercase hexadecimal digits + */ + def writeBase16Val(bs: Array[Byte], lowerCase: Boolean): Unit = { + writeOptionalCommaAndIndentionBeforeValue() + val ds = + if (lowerCase) lowerCaseHexDigits + else upperCaseHexDigits + writeBase16Bytes(bs, ds) + } + + /** + * Writes a byte array as a JSON string value encoded in a base-64 format. + * + * @param bs the byte array to write + * @param doPadding if `true`, outputs padding characters (`=`) as needed + */ + def writeBase64Val(bs: Array[Byte], doPadding: Boolean): Unit = { + writeOptionalCommaAndIndentionBeforeValue() + writeBase64Bytes(bs, base64Digits, doPadding) + } + + /** + * Writes a byte array as a JSON string value encoded in a base-64 format for URLs. + * + * @param bs the byte array to write + * @param doPadding if `true`, outputs padding characters (`=`) as needed + */ + def writeBase64UrlVal(bs: Array[Byte], doPadding: Boolean): Unit = { + writeOptionalCommaAndIndentionBeforeValue() + writeBase64Bytes(bs, base64UrlDigits, doPadding) + } + + /** + * Writes a byte array as a JSON raw binary value. + * + * @param bs the byte array to write + */ + def writeRawVal(bs: Array[Byte]): Unit = { + writeOptionalCommaAndIndentionBeforeValue() + writeRawBytes(bs) + } + + /** + * Writes a JSON `null` value. + */ + def writeNull(): Unit = count = { + writeOptionalCommaAndIndentionBeforeValue() + val pos = ensureBufCapacity(4) + ByteArrayAccess.setInt(buf, pos, 0x6C6C756E) + pos + 4 + } + + /** + * Writes a JSON array start marker (`[`). + */ + def writeArrayStart(): Unit = writeNestedStart('[') + + + /** + * Writes a JSON array end marker (`]`). + */ + def writeArrayEnd(): Unit = writeNestedEnd(']') + + /** + * Writes a JSON array start marker (`{`). + */ + def writeObjectStart(): Unit = writeNestedStart('{') + + /** + * Writes a JSON array end marker (`}`). + */ + def writeObjectEnd(): Unit = writeNestedEnd('}') + + /* GWZ -- disable Jsoniter's codec facilities... + /** + * Writes JSON-encoded value of type `A` to an output stream. + * + * @param codec a JSON value codec for type `A` + * @param x the value to encode + * @param out the output stream to write to + * @param config the writer configuration + */ + def write[@sp A](codec: JsonValueCodec[A], x: A, out: OutputStream, config: WriterConfig): Unit = + try { + this.out = out + this.config = config + count = 0 + indention = 0 + comma = false + disableBufGrowing = false + if (limit < config.preferredBufSize) reallocateBufToPreferredSize() + codec.encodeValue(x, this) + out.write(buf, 0, count) + } finally { + this.out = null // don't close output stream + if (limit > config.preferredBufSize) reallocateBufToPreferredSize() + } + + /** + * Encodes a value of type `A` to a byte array. + * + * @param codec a JSON value codec for type `A` + * @param x the value to encode + * @param config the writer configuration + * @return the encoded JSON as a byte array + */ + def write[@sp A](codec: JsonValueCodec[A], x: A, config: WriterConfig): Array[Byte] = + try { + this.config = config + count = 0 + indention = 0 + comma = false + disableBufGrowing = false + codec.encodeValue(x, this) + java.util.Arrays.copyOf(buf, count) + } finally { + if (limit > config.preferredBufSize) reallocateBufToPreferredSize() + } + + /** + * Encodes a value of type `A` to a string. + * + * @param codec a JSON value codec for type `A` + * @param x the value to encode + * @param config the writer configuration + * @return the encoded JSON as a string + */ + def writeToString[@sp A](codec: JsonValueCodec[A], x: A, config: WriterConfig): String = + try { + this.config = config + count = 0 + indention = 0 + comma = false + disableBufGrowing = false + codec.encodeValue(x, this) + new String(buf, 0, count, StandardCharsets.UTF_8) + } finally { + if (limit > config.preferredBufSize) reallocateBufToPreferredSize() + } + + /** + * Encodes a value of type `A` to a string without buffer reallocation. + * + * @note Use only once with a newly allocated writer, so buffer reallocation is not required. + * + * @param codec a JSON value codec for type `A` + * @param x the value to encode + * @param config the writer configuration + * @return the encoded JSON as a string + */ + def writeToStringWithoutBufReallocation[@sp A](codec: JsonValueCodec[A], x: A, config: WriterConfig): String = { + this.config = config + count = 0 + indention = 0 + comma = false + disableBufGrowing = false + codec.encodeValue(x, this) + new String(buf, 0, count, StandardCharsets.UTF_8) + } + + /** + * Encodes a value of type `A` into a pre-allocated byte array slice. + * + * @param codec a JSON value codec for type `A` + * @param x the value to encode + * @param buf the target byte array + * @param from the start index of the target slice (inclusive) + * @param to the end index of the target slice (exclusive) + * @param config the writer configuration + * @return the number of bytes written to the target slice + */ + def write[@sp A](codec: JsonValueCodec[A], x: A, buf: Array[Byte], from: Int, to: Int, config: WriterConfig): Int = { + val currBuf = this.buf + try { + this.buf = buf + this.config = config + count = from + limit = to + indention = 0 + comma = false + disableBufGrowing = true + codec.encodeValue(x, this) + count + } finally { + setBuf(currBuf) + } + } + + /** + * Encodes a value of type `A` into a byte buffer. + * + * @param codec JSON value codec for type `A` + * @param x the value to encode + * @param bbuf the target byte buffer + * @param config the writer configuration + */ + def write[@sp A](codec: JsonValueCodec[A], x: A, bbuf: ByteBuffer, config: WriterConfig): Unit = + if (bbuf.hasArray) { + val offset = bbuf.arrayOffset + val currBuf = this.buf + try { + this.buf = bbuf.array + this.config = config + count = bbuf.position() + offset + limit = bbuf.limit() + offset + indention = 0 + comma = false + disableBufGrowing = true + codec.encodeValue(x, this) + } catch { + case _: ArrayIndexOutOfBoundsException => throw new BufferOverflowException + } finally { + setBuf(currBuf) + bbuf.position(count - offset) + } + } else { + try { + this.bbuf = bbuf + this.config = config + count = 0 + indention = 0 + comma = false + disableBufGrowing = false + if (limit < config.preferredBufSize) reallocateBufToPreferredSize() + codec.encodeValue(x, this) + bbuf.put(buf, 0, count) + } finally { + this.bbuf = null + if (limit > config.preferredBufSize) reallocateBufToPreferredSize() + } + } + */ + + private[this] def writeNestedStart(b: Byte): Unit = { + writeOptionalCommaAndIndentionBeforeKey() + writeBytes(b) + val indentionStep = config.indentionStep + if (indentionStep != 0) { + indention += indentionStep + writeIndention() + } + } + + private[this] def writeNestedEnd(b: Byte): Unit = { + comma = true + if (indention != 0) { + indention -= config.indentionStep + writeIndention() + } + writeBytes(b) + } + + private[this] def writeOptionalCommaAndIndentionBeforeValue(): Unit = + if (comma) { + writeBytes(',') + if (indention != 0) writeIndention() + } else comma = true + + private[this] def writeOptionalCommaAndIndentionBeforeKey(): Unit = + if (comma) { + comma = false + writeBytes(',') + if (indention != 0) writeIndention() + } + + private[this] def writeIndention(): Unit = count = { + val n = indention + val pos = ensureBufCapacity(n + 8) + writeIndention(buf, pos, n) + } + + private[this] def writeIndention(buf: Array[Byte], p: Int, n: Int): Int = { + var pos = p + buf(pos) = '\n' + pos += 1 + val posLim = pos + n + while (pos < posLim) { + ByteArrayAccess.setLong(buf, pos, 0x2020202020202020L) + pos += 8 + } + posLim + } + + private[this] def writeParenthesesWithColon(): Unit = count = { + var pos = ensureBufCapacity(4) // 4 == size of Int in bytes + ByteArrayAccess.setInt(buf, pos, 0x203A22) + if (indention > 0) pos += 1 + pos + 2 + } + + private[this] def writeColon(): Unit = count = { + var pos = ensureBufCapacity(2) + ByteArrayAccess.setShort(buf, pos, 0x203A) + if (indention > 0) pos += 1 + pos + 1 + } + + private[this] def writeBytes(b: Byte): Unit = count = { + var pos = count + if (pos >= limit) pos = flushAndGrowBuf(1, pos) + buf(pos) = b + pos + 1 + } + + private[this] def writeBase16Bytes(bs: Array[Byte], ds: Array[Short]): Unit = count = { + val lenM1 = bs.length - 1 + var posLim = limit - 6 + var pos = count + if (pos >= posLim) { + pos = flushAndGrowBuf(6, pos) + posLim = limit - 5 + } + var buf = this.buf + buf(pos) = '"' + pos += 1 + var offset = 0 + while (offset < lenM1) { + val offsetLim = Math.min((posLim - pos + 1 >> 1) + offset, lenM1) + while (offset < offsetLim) { + val d1 = ds(bs(offset) & 0xFF) + val d2 = ds(bs(offset + 1) & 0xFF) << 16 + ByteArrayAccess.setInt(buf, pos, d1 | d2) + pos += 4 + offset += 2 + } + if (pos >= posLim) { + pos = flushAndGrowBuf(5, pos) + buf = this.buf + posLim = limit - 5 + } + } + if (offset == lenM1) { + ByteArrayAccess.setShort(buf, pos, ds(bs(offset) & 0xFF)) + pos += 2 + } + buf(pos) = '"' + pos + 1 + } + + private[this] def writeBase64Bytes(bs: Array[Byte], ds: Array[Byte], doPadding: Boolean): Unit = count = { + val lenM3 = bs.length - 3 + var posLim = limit - 6 + var pos = count + if (pos >= posLim) { + pos = flushAndGrowBuf(6, pos) + posLim = limit - 5 + } + var buf = this.buf + buf(pos) = '"' + pos += 1 + var offset = 0 + while (offset < lenM3) { + val offsetLim = Math.min((posLim - pos + 3 >> 2) * 3 + offset, lenM3) + while (offset < offsetLim) { + val p = ByteArrayAccess.getIntReversed(bs, offset) + ByteArrayAccess.setInt(buf, pos, + ds(p >> 8 & 0x3F) << 24 | ds(p >> 14 & 0x3F) << 16 | ds(p >> 20 & 0x3F) << 8 | ds(p >>> 26)) + pos += 4 + offset += 3 + } + if (pos >= posLim) { + pos = flushAndGrowBuf(5, pos) + buf = this.buf + posLim = limit - 5 + } + } + if (offset == lenM3) { + val p = (bs(offset) & 0xFF) << 16 | (bs(offset + 1) & 0xFF) << 8 | (bs(offset + 2) & 0xFF) + ByteArrayAccess.setInt(buf, pos, + ds(p & 0x3F) << 24 | ds(p >> 6 & 0x3F) << 16 | ds(p >> 12 & 0x3F) << 8 | ds(p >> 18)) + pos += 4 + } else if (offset == lenM3 + 1) { + val p = (bs(offset) & 0xFF) << 10 | (bs(offset + 1) & 0xFF) << 2 + ByteArrayAccess.setInt(buf, pos, ds(p & 0x3F) << 16 | ds(p >> 6 & 0x3F) << 8 | ds(p >> 12) | 0x3D000000) + pos += 3 + if (doPadding) pos += 1 + } else if (offset == lenM3 + 2) { + val p = bs(offset) + ByteArrayAccess.setInt(buf, pos, ds(p << 4 & 0x3F) << 8 | ds(p >> 2 & 0x3F) | 0x3D3D0000) + pos += 2 + if (doPadding) pos += 2 + } + buf(pos) = '"' + pos + 1 + } + + private[this] def writeRawBytes(bs: Array[Byte]): Unit = count = { + var pos = count + var step = Math.max(config.preferredBufSize, limit - pos) + var remaining = bs.length + var offset = 0 + while (remaining > 0) { + step = Math.min(step, remaining) + if (pos + step > limit) pos = flushAndGrowBuf(step, pos) + System.arraycopy(bs, offset, buf, pos, step) + offset += step + pos += step + remaining -= step + } + pos + } + + private[this] def writeLongNonEscapedAsciiKey(x: String): Unit = { + writeOptionalCommaAndIndentionBeforeKey() + writeBytes('"') + var pos = count + var step = Math.max(config.preferredBufSize, limit - pos) + var remaining = x.length + var offset = 0 + while (remaining > 0) { + step = Math.min(step, remaining) + if (pos + step > limit) pos = flushAndGrowBuf(step, pos) + val newOffset = offset + step + x.getBytes(offset, newOffset, buf, pos) + offset = newOffset + pos += step + remaining -= step + } + count = pos + writeBytes('"') + writeColon() + } + + private[this] def writeLongNonEscapedAsciiVal(x: String): Unit = { + writeOptionalCommaAndIndentionBeforeValue() + writeBytes('"') + var pos = count + var step = Math.max(config.preferredBufSize, limit - pos) + var remaining = x.length + var offset = 0 + while (remaining > 0) { + step = Math.min(step, remaining) + if (pos + step > limit) pos = flushAndGrowBuf(step, pos) + val newOffset = offset + step + x.getBytes(offset, newOffset, buf, pos) + offset = newOffset + pos += step + remaining -= step + } + count = pos + writeBytes('"') + } + + private[this] def writeZoneId(x: ZoneId): Unit = count = { + val s = x.getId + val len = s.length + var pos = ensureBufCapacity(len + 2) + val buf = this.buf + buf(pos) = '"' + pos += 1 + s.getBytes(0, len, buf, pos) + pos += len + buf(pos) = '"' + pos + 1 + } + + private[this] def writeUUID(mostSigBits: Long, leastSigBits: Long): Unit = count = { + val pos = ensureBufCapacity(40) // 40 == 5 * size of Long in bytes + val buf = this.buf + val ds = lowerCaseHexDigits + val mostSigBits1 = (mostSigBits >> 32).toInt + val d1 = ds(mostSigBits1 >>> 24) << 8 + val d2 = ds(mostSigBits1 >> 16 & 0xFF).toLong << 24 + val d3 = ds(mostSigBits1 >> 8 & 0xFF).toLong << 40 + val d4 = ds(mostSigBits1 & 0xFF) + ByteArrayAccess.setLong(buf, pos, d1 | d2 | d3 | d4.toLong << 56 | 0x22) + val mostSigBits2 = mostSigBits.toInt + val d5 = ds(mostSigBits2 >>> 24) << 16 + val d6 = ds(mostSigBits2 >> 16 & 0xFF).toLong << 32 + val d7 = ds(mostSigBits2 >> 8 & 0xFF) + ByteArrayAccess.setLong(buf, pos + 8, d4 >> 8 | d5 | d6 | d7.toLong << 56 | 0x2D000000002D00L) + val d8 = ds(mostSigBits2 & 0xFF) << 8 + val leastSigBits1 = (leastSigBits >> 32).toInt + val d9 = ds(leastSigBits1 >>> 24).toLong << 32 + val d10 = ds(leastSigBits1 >> 16 & 0xFF).toLong << 48 + ByteArrayAccess.setLong(buf, pos + 16, d7 >> 8 | d8 | d9 | d10 | 0x2D000000) + val d11 = ds(leastSigBits1 >> 8 & 0xFF) << 8 + val d12 = ds(leastSigBits1 & 0xFF).toLong << 24 + val leastSigBits2 = leastSigBits.toInt + val d13 = ds(leastSigBits2 >>> 24).toLong << 40 + val d14 = ds(leastSigBits2 >> 16 & 0xFF) + ByteArrayAccess.setLong(buf, pos + 24, d11 | d12| d13 | d14.toLong << 56 | 0x2D) + val d15 = ds(leastSigBits2 >> 8 & 0xFF) << 8 + val d16 = ds(leastSigBits2 & 0xFF).toLong << 24 + ByteArrayAccess.setLong(buf, pos + 32, d14 >> 8 | d15 | d16 | 0x220000000000L) + pos + 38 + } + + @tailrec + private[this] def writeString(s: String, from: Int, pos: Int, minLim: Int, escapedChars: Array[Byte]): Int = + if (pos < minLim) { + val ch = s.charAt(from) + buf(pos) = ch.toByte + if (ch >= 0x80 || escapedChars(ch) != 0) writeEscapedOrEncodedString(s, from, pos, escapedChars) + else writeString(s, from + 1, pos + 1, minLim, escapedChars) + } else { + val remaining = s.length - from + if (remaining > 0) { + val newPos = flushAndGrowBuf(2, pos) + writeString(s, from, newPos, Math.min(remaining, limit - newPos - 1) + newPos, escapedChars) + } else pos + } + + private[this] def writeEscapedOrEncodedString(s: String, from: Int, pos: Int, escapedChars: Array[Byte]): Int = + if (config.escapeUnicode) writeEscapedString(s, from, s.length, pos, limit - 13, escapedChars) + else writeEncodedString(s, from, s.length, pos, limit - 7, escapedChars) + + @tailrec + private[this] def writeEncodedString(s: String, from: Int, to: Int, pos: Int, posLim: Int, escapedChars: Array[Byte]): Int = + if (from >= to) pos + else if (pos >= posLim) writeEncodedString(s, from, to, flushAndGrowBuf(7, pos), limit - 6, escapedChars) + else { + val ch1 = s.charAt(from) + if (ch1 < 0x80) { + val esc = escapedChars(ch1) + if (esc == 0) { // 000000000aaaaaaa (UTF-16 char) -> 0aaaaaaa (UTF-8 byte) + buf(pos) = ch1.toByte + writeEncodedString(s, from + 1, to, pos + 1, posLim, escapedChars) + } else if (esc > 0) { + ByteArrayAccess.setShort(buf, pos, (esc << 8 | 0x5C).toShort) + writeEncodedString(s, from + 1, to, pos + 2, posLim, escapedChars) + } else writeEncodedString(s, from + 1, to, writeEscapedUnicode(ch1.toByte, pos, buf), posLim, escapedChars) + } else if (ch1 < 0x800) { // 00000bbbbbaaaaaa (UTF-16 char) -> 110bbbbb 10aaaaaa (UTF-8 bytes) + ByteArrayAccess.setShort(buf, pos, (ch1 >> 6 | (ch1 << 8 & 0x3F00) | 0x80C0).toShort) + writeEncodedString(s, from + 1, to, pos + 2, posLim, escapedChars) + } else if (ch1 < 0xD800 || ch1 > 0xDFFF) { // ccccbbbbbbaaaaaa (UTF-16 char) -> 1110cccc 10bbbbbb 10aaaaaa (UTF-8 bytes) + ByteArrayAccess.setInt(buf, pos, ch1 >> 12 | (ch1 << 2 & 0x3F00) | (ch1 << 16 & 0x3F0000) | 0x8080E0) + writeEncodedString(s, from + 1, to, pos + 3, posLim, escapedChars) + } else { // 110110uuuuccccbb 110111bbbbaaaaaa (UTF-16 chars) -> 11110ddd 10ddcccc 10bbbbbb 10aaaaaa (UTF-8 bytes), where ddddd = uuuu + 1 + if (ch1 >= 0xDC00 || from + 1 >= to) illegalSurrogateError() + val ch2 = s.charAt(from + 1) + if (ch2 < 0xDC00 || ch2 > 0xDFFF) illegalSurrogateError() + val cp = (ch1 << 10) + (ch2 - 56613888) // -56613888 == 0x10000 - (0xD800 << 10) - 0xDC00 + ByteArrayAccess.setInt(buf, pos, cp >> 18 | (cp >> 4 & 0x3F00) | (cp << 10 & 0x3F0000) | (cp << 24 & 0x3F000000) | 0x808080F0) + writeEncodedString(s, from + 2, to, pos + 4, posLim, escapedChars) + } + } + + @tailrec + private[this] def writeEscapedString(s: String, from: Int, to: Int, pos: Int, posLim: Int, escapedChars: Array[Byte]): Int = + if (from >= to) pos + else if (pos >= posLim) writeEscapedString(s, from, to, flushAndGrowBuf(13, pos), limit - 12, escapedChars) + else { + val ch1 = s.charAt(from) + if (ch1 < 0x80) { + val esc = escapedChars(ch1) + if (esc == 0) { + buf(pos) = ch1.toByte + writeEscapedString(s, from + 1, to, pos + 1, posLim, escapedChars) + } else if (esc > 0) { + ByteArrayAccess.setShort(buf, pos, (esc << 8 | 0x5C).toShort) + writeEscapedString(s, from + 1, to, pos + 2, posLim, escapedChars) + } else writeEscapedString(s, from + 1, to, writeEscapedUnicode(ch1.toByte, pos, buf), posLim, escapedChars) + } else if (ch1 < 0xD800 || ch1 > 0xDFFF) { + writeEscapedString(s, from + 1, to, writeEscapedUnicode(ch1, pos, buf), posLim, escapedChars) + } else { + if (ch1 >= 0xDC00 || from + 1 >= to) illegalSurrogateError() + val ch2 = s.charAt(from + 1) + if (ch2 < 0xDC00 || ch2 > 0xDFFF) illegalSurrogateError() + writeEscapedString(s, from + 2, to, writeEscapedUnicode(ch2, writeEscapedUnicode(ch1, pos, buf), buf), posLim, escapedChars) + } + } + + private[this] def writeChar(ch: Char): Unit = count = { + val pos = ensureBufCapacity(8) // 8 = size of Long in bytes + if (ch < 0x80) { + val esc = escapedChars(ch) + if (esc == 0) { // 000000000aaaaaaa (UTF-16 char) -> 0aaaaaaa (UTF-8 byte) + ByteArrayAccess.setInt(buf, pos, ch << 8 | 0x220022) + pos + 3 + } else if (esc > 0) { + ByteArrayAccess.setInt(buf, pos, esc << 16 | 0x22005C22) + pos + 4 + } else { + val ds = lowerCaseHexDigits + ByteArrayAccess.setLong(buf, pos, ds(ch).toLong << 40 | 0x2200003030755C22L) + pos + 8 + } + } else if (config.escapeUnicode) { + if (ch >= 0xD800 && ch <= 0xDFFF) illegalSurrogateError() + val ds = lowerCaseHexDigits + val d1 = ds(ch >> 8).toLong << 24 + val d2 = ds(ch & 0xFF).toLong << 40 + ByteArrayAccess.setLong(buf, pos, d1 | d2 | 0x2200000000755C22L) + pos + 8 + } else if (ch < 0x800) { // 00000bbbbbaaaaaa (UTF-16 char) -> 110bbbbb 10aaaaaa (UTF-8 bytes) + ByteArrayAccess.setInt(buf, pos, (ch & 0x3F) << 16 | (ch & 0xFC0) << 2 | 0x2280C022) + pos + 4 + } else if (ch < 0xD800 || ch > 0xDFFF) { // ccccbbbbbbaaaaaa (UTF-16 char) -> 1110cccc 10bbbbbb 10aaaaaa (UTF-8 bytes) + ByteArrayAccess.setLong(buf, pos, ((ch & 0x3F) << 24 | (ch & 0xFC0) << 10 | (ch & 0xF000) >> 4) | 0x228080E022L) + pos + 5 + } else illegalSurrogateError() + } + + private[this] def writeEscapedUnicode(ch: Char, pos: Int, buf: Array[Byte]): Int = { + val ds = lowerCaseHexDigits + ByteArrayAccess.setShort(buf, pos, 0x755C) + val d1 = ds(ch >> 8) + val d2 = ds(ch & 0xFF) << 16 + ByteArrayAccess.setInt(buf, pos + 2, d1 | d2) + pos + 6 + } + + private[this] def writeEscapedUnicode(b: Byte, pos: Int, buf: Array[Byte]): Int = { + val ds = lowerCaseHexDigits + ByteArrayAccess.setInt(buf, pos, 0x3030755C) + ByteArrayAccess.setShort(buf, pos + 4, ds(b & 0xFF)) + pos + 6 + } + + private[this] def illegalSurrogateError(): Nothing = encodeError("illegal char sequence of surrogate pair") + + private[this] def writeBigInteger(x: BigInteger, ss: Array[BigInteger]): Unit = + if (x.bitLength < 64) writeLong(x.longValue) + else { + val n = calculateTenPow18SquareNumber(x) + val ss1 = + if (ss eq null) getTenPow18Squares(n) + else ss + val qr = x.divideAndRemainder(ss1(n)) + writeBigInteger(qr(0), ss1) + writeBigIntegerRemainder(qr(1), n - 1, ss1) + } + + private[this] def writeBigIntegerRemainder(x: BigInteger, n: Int, ss: Array[BigInteger]): Unit = + if (n < 0) count = write18Digits(Math.abs(x.longValue), ensureBufCapacity(18), buf, digits) + else { + val qr = x.divideAndRemainder(ss(n)) + writeBigIntegerRemainder(qr(0), n - 1, ss) + writeBigIntegerRemainder(qr(1), n - 1, ss) + } + + private[this] def writeBigDecimal(x: java.math.BigDecimal): Unit = { + val exp = writeBigDecimal(x.unscaledValue, x.scale, 0, null) + if (exp != 0) { + var pos = ensureBufCapacity(12) + val buf = this.buf + val ds = digits + var m: Short = 0x2B45 + var q0 = exp + if (exp < 0) { + m = 0x2D45 + q0 = -exp + } + ByteArrayAccess.setShort(buf, pos, m) + pos += 2 + var q = 0 + var lastPos = pos + if (q0 < 100000000) { + q = q0.toInt + lastPos += digitCount(q0) + count = lastPos + } else { + val q1 = (q0 >> 8) * 1441151881 >> 49 // divide a small positive long by 100000000 + q = q1.toInt + lastPos += digitCount(q1) + count = write8Digits(q0 - q1 * 100000000, lastPos, buf, ds) + } + writePositiveIntDigits(q, lastPos, buf, ds) + } + } + + private[this] def writeBigDecimal(x: BigInteger, scale: Int, blockScale: Int, ss: Array[BigInteger]): Long = + if (x.bitLength < 64) { + val v = x.longValue + val pos = ensureBufCapacity(28) // Long.MinValue.toString.length + 8 (for a leading zero, dot, and padding zeroes) + count = pos + writeLong(v) + val blockLen = (v >> 63).toInt + count - pos + val dotOff = scale.toLong - blockScale + val exp = (blockLen - 1) - dotOff + if (scale >= 0 && exp >= -6) { + if (exp < 0) insertDotWithZeroes(blockLen, -1 - exp.toInt) + else if (dotOff > 0) insertDot(count - dotOff.toInt) + 0 + } else { + if (blockLen > 1 || blockScale > 0) insertDot(count - blockLen + 1) + exp + } + } else { + val n = calculateTenPow18SquareNumber(x) + val ss1 = + if (ss eq null) getTenPow18Squares(n) + else ss + val qr = x.divideAndRemainder(ss1(n)) + val exp = writeBigDecimal(qr(0), scale, blockScale + (18 << n), ss1) + writeBigDecimalRemainder(qr(1), scale, blockScale, n - 1, ss1) + exp + } + + private[this] def writeBigDecimalRemainder(x: BigInteger, scale: Int, blockScale: Int, n: Int, + ss: Array[BigInteger]): Unit = + if (n < 0) { + count = write18Digits(Math.abs(x.longValue), ensureBufCapacity(19), buf, digits) // 18 digits and a place for optional dot + val dotOff = scale - blockScale + if (dotOff > 0 && dotOff <= 18) insertDot(count - dotOff) + } else { + val qr = x.divideAndRemainder(ss(n)) + writeBigDecimalRemainder(qr(0), scale, blockScale + (18 << n), n - 1, ss) + writeBigDecimalRemainder(qr(1), scale, blockScale, n - 1, ss) + } + + private[this] def calculateTenPow18SquareNumber(x: BigInteger): Int = { + val m = Math.max((x.bitLength * 71828554L >> 32).toInt - 1, 1) // Math.max((x.bitLength * Math.log(2) / Math.log(1e18)).toInt - 1, 1) + 31 - java.lang.Integer.numberOfLeadingZeros(m) + } + + private[this] def insertDotWithZeroes(len: Int, pad: Int): Unit = count = { + var pos = count + pad + 1 + val buf = this.buf + val numPos = pos - len + val off = pad + 2 + while (pos > numPos) { + buf(pos) = buf(pos - off) + pos -= 1 + } + val dotPos = pos - pad + while (pos > dotPos) { + buf(pos) = '0' + pos -= 1 + } + ByteArrayAccess.setShort(buf, dotPos - 1, 0x2E30) + count + off + } + + private[this] def insertDot(dotPos: Int): Unit = count = { + var pos = count + val buf = this.buf + while (pos > dotPos) { + buf(pos) = buf(pos - 1) + pos -= 1 + } + buf(dotPos) = '.' + count + 1 + } + + private[this] def writeBoolean(x: Boolean): Unit = count = { + val pos = ensureBufCapacity(8) // bytes in Long + if (x) { + ByteArrayAccess.setInt(buf, pos, 0x65757274) + pos + 4 + } else { + ByteArrayAccess.setLong(buf, pos, 0x65736c6166L) + pos + 5 + } + } + + private[this] def writeByte(x: Byte): Unit = count = { + var pos = ensureBufCapacity(5) // size of Int in bytes + one byte for the sign + val buf = this.buf + val ds = digits + var q0: Int = x + if (q0 < 0) { + buf(pos) = '-' + pos += 1 + q0 = -q0 + } + if (q0 < 10) { + buf(pos) = (q0 + '0').toByte + pos + 1 + } else if (q0 < 100) { + ByteArrayAccess.setShort(buf, pos, ds(q0)) + pos + 2 + } else { + ByteArrayAccess.setInt(buf, pos, ds(q0 - 100) << 8 | 0x31) + pos + 3 + } + } + + private[this] def writeDuration(x: Duration): Unit = count = { + var pos = ensureBufCapacity(40) // 40 == "PT-1111111111111111H-11M-11.111111111S".length + 2 + val buf = this.buf + val totalSecs = x.getSeconds + var nano = x.getNano + ByteArrayAccess.setLong(buf, pos, 0x225330545022L) + if ((totalSecs | nano) == 0) pos + 6 + else { + pos += 3 + val effectiveTotalSecs = + if (totalSecs < 0) (-nano >> 31) - totalSecs + else totalSecs + val hours = Math.multiplyHigh(effectiveTotalSecs >> 4, 655884233731895169L) >> 3 // divide a positive long by 3600 + val secsOfHour = (effectiveTotalSecs - hours * 3600).toInt + val minutes = secsOfHour * 17477 >> 20 // divide a small positive int by 60 + val seconds = secsOfHour - minutes * 60 + val ds = digits + if (hours != 0) { + if (totalSecs < 0) { + buf(pos) = '-' + pos += 1 + } + var q = 0 + var lastPos = pos + if (hours < 100000000) { + q = hours.toInt + lastPos += digitCount(hours) + pos = lastPos + } else { + val q1 = Math.multiplyHigh(hours, 6189700196426901375L) >>> 25 // divide a positive long by 100000000 + q = q1.toInt + lastPos += digitCount(q1) + pos = write8Digits(hours - q1 * 100000000, lastPos, buf, ds) + } + writePositiveIntDigits(q, lastPos, buf, ds) + ByteArrayAccess.setShort(buf, pos, 0x2248) + pos += 1 + } + if (minutes != 0) { + if (totalSecs < 0) { + buf(pos) = '-' + pos += 1 + } + if (minutes < 10) { + buf(pos) = (minutes + '0').toByte + pos += 1 + } else pos = write2Digits(minutes, pos, buf, ds) + ByteArrayAccess.setShort(buf, pos, 0x224D) + pos += 1 + } + if ((seconds | nano) != 0) { + if (totalSecs < 0) { + buf(pos) = '-' + pos += 1 + } + if (seconds < 10) { + buf(pos) = (seconds + '0').toByte + pos += 1 + } else pos = write2Digits(seconds, pos, buf, ds) + if (nano != 0) { + if (totalSecs < 0) nano = 1000000000 - nano + val dotPos = pos + pos = writeSignificantFractionDigits(nano, pos + 9, pos, buf, ds) + buf(dotPos) = '.' + } + ByteArrayAccess.setShort(buf, pos, 0x2253) + pos += 1 + } + pos + 1 + } + } + + private[this] def writeInstant(x: Instant): Unit = { + val epochSecond = x.getEpochSecond + if (epochSecond < 0) writeBeforeEpochInstant(epochSecond, x.getNano) + else { + val epochDay = Math.multiplyHigh(epochSecond, 1749024623285053783L) >> 13 // epochSecond / 86400 + val marchZeroDay = epochDay + 719468 // 719468 == 719528 - 60 == days 0000 to 1970 - days 1st Jan to 1st Mar + var year = (Math.multiplyHigh(marchZeroDay * 400 + 591, 4137408090565272301L) >> 15).toInt // ((marchZeroDay * 400 + 591) / 146097).toInt + var year365 = year * 365L + var year1374389535 = year * 1374389535L + var century = (year1374389535 >> 37).toInt + var marchDayOfYear = (marchZeroDay - year365).toInt - (year >> 2) + century - (century >> 2) + if (marchDayOfYear < 0) { + year365 -= 365 + year1374389535 -= 1374389535 + year -= 1 + century = (year1374389535 >> 37).toInt + marchDayOfYear = (marchZeroDay - year365).toInt - (year >> 2) + century - (century >> 2) + } + val marchMonth = marchDayOfYear * 17135 + 6854 >> 19 // (marchDayOfYear * 5 + 2) / 153 + val day = marchDayOfYear - (marchMonth * 1002762 - 16383 >> 15) // marchDayOfYear - (marchMonth * 306 + 5) / 10 + 1 + val m = 9 - marchMonth >> 4 + val month = (m & -9 | 3) + marchMonth + year -= m + writeInstant(year, month, day, (epochSecond - epochDay * 86400).toInt, x.getNano) + } + } + + private[this] def writeBeforeEpochInstant(epochSecond: Long, nano: Int): Unit = { + val epochDay = (Math.multiplyHigh(epochSecond - 86399, 1749024623285053783L) >> 13) + 1 // (epochSecond - 86399) / 86400 + var marchZeroDay = epochDay + 719468 // 719468 == 719528 - 60 == days 0000 to 1970 - days 1st Jan to 1st Mar + val adjust400YearCycles = ((marchZeroDay + 1) * 7525902 >> 40).toInt // ((marchZeroDay + 1) / 146097).toInt - 1 + marchZeroDay -= adjust400YearCycles * 146097L + var year = { // ((marchZeroDay * 400 + 591) / 146097).toInt + val pa = marchZeroDay * 400 + 591 + ((Math.multiplyHigh(pa, 4137408090565272301L) >> 15) + (pa >> 63)).toInt + } + var year365 = year * 365L + var year1374389535 = year * 1374389535L + var century = (year1374389535 >> 37).toInt + var marchDayOfYear = (marchZeroDay - year365).toInt - (year >> 2) + century - (century >> 2) + if (marchDayOfYear < 0) { + year365 -= 365 + year1374389535 -= 1374389535 + year -= 1 + century = (year1374389535 >> 37).toInt + marchDayOfYear = (marchZeroDay - year365).toInt - (year >> 2) + century - (century >> 2) + } + val marchMonth = marchDayOfYear * 17135 + 6854 >> 19 // (marchDayOfYear * 5 + 2) / 153 + val day = marchDayOfYear - (marchMonth * 1002762 - 16383 >> 15) // marchDayOfYear - (marchMonth * 306 + 5) / 10 + 1 + val m = 9 - marchMonth >> 4 + val month = (m & -9 | 3) + marchMonth + year += adjust400YearCycles * 400 - m + writeInstant(year, month, day, (epochSecond - epochDay * 86400).toInt, nano) + } + + private[this] def writeInstant(year: Int, month: Int, day: Int, secsOfDay: Int, nano: Int): Unit = count = { + var pos = ensureBufCapacity(39) // 39 == Instant.MAX.toString.length + 2 + val buf = this.buf + val ds = digits + buf(pos) = '"' + pos = writeYear(year, pos + 1, buf, ds) + ByteArrayAccess.setLong(buf, pos, ds(month) << 8 | ds(day).toLong << 32 | 0x5400002D00002DL) + pos += 7 + val y1 = secsOfDay * 37283 // Based on James Anhalt's algorithm: https://jk-jeon.github.io/posts/2022/02/jeaiii-algorithm/ + val y2 = (y1 & 0x7FFFFFF) * 15 + val y3 = (y2 & 0x1FFFFFF) * 15 + ByteArrayAccess.setLong(buf, pos, ds(y1 >>> 27) | ds(y2 >> 25).toLong << 24 | ds(y3 >> 23).toLong << 48 | 0x3A00003A0000L) + pos += 8 + if (nano != 0) pos = writeNanos(nano, pos, buf, ds) + ByteArrayAccess.setShort(buf, pos, 0x225A) + pos + 2 + } + + private[this] def writeLocalDate(x: LocalDate): Unit = count = { + var pos = ensureBufCapacity(19) // 19 == java.time.Year.MAX_VALUE.toString.length + 9 + val buf = this.buf + val ds = digits + buf(pos) = '"' + pos = writeYear(x.getYear, pos + 1, buf, ds) + val d1 = ds(x.getMonthValue) << 8 + val d2 = ds(x.getDayOfMonth).toLong << 32 + ByteArrayAccess.setLong(buf, pos, d1 | d2 | 0x2200002D00002DL) + pos + 7 + } + + private[this] def writeLocalDateTime(x: LocalDateTime): Unit = count = { + var pos = ensureBufCapacity(37) // 37 == LocalDateTime.MAX.toString.length + 2 + val buf = this.buf + val ds = digits + buf(pos) = '"' + pos = writeLocalTime(x.toLocalTime, writeLocalDateWithT(x.toLocalDate, pos + 1, buf, ds), buf, ds) + buf(pos) = '"' + pos + 1 + } + + private[this] def writeLocalTime(x: LocalTime): Unit = count = { + var pos = ensureBufCapacity(20) // 20 == LocalTime.MAX.toString.length + 2 + val buf = this.buf + val ds = digits + buf(pos) = '"' + pos = writeLocalTime(x, pos + 1, buf, ds) + buf(pos) = '"' + pos + 1 + } + + private[this] def writeMonthDay(x: MonthDay): Unit = count = { + val pos = ensureBufCapacity(9) // 9 == "--01-01".length + 2 + val buf = this.buf + val ds = digits + buf(pos) = '"' + val d1 = ds(x.getMonthValue) << 16 + val d2 = ds(x.getDayOfMonth).toLong << 40 + ByteArrayAccess.setLong(buf, pos + 1, d1 | d2 | 0x2200002D00002D2DL) + pos + 9 + } + + private[this] def writeOffsetDateTime(x: OffsetDateTime): Unit = count = { + val pos = ensureBufCapacity(46) // 46 == "+999999999-12-31T23:59:59.999999999+00:00:01".length + 2 + val buf = this.buf + val ds = digits + buf(pos) = '"' + writeOffset(x.getOffset, + writeLocalTime(x.toLocalTime, writeLocalDateWithT(x.toLocalDate, pos + 1, buf, ds), buf, ds), buf, ds) + } + + private[this] def writeOffsetTime(x: OffsetTime): Unit = count = { + val pos = ensureBufCapacity(29) // 29 == "00:00:07.999999998+00:00:08".length + 2 + val buf = this.buf + val ds = digits + buf(pos) = '"' + writeOffset(x.getOffset, writeLocalTime(x.toLocalTime, pos + 1, buf, ds), buf, ds) + } + + private[this] def writePeriod(x: Period): Unit = count = { + var pos = ensureBufCapacity(39) // 39 == "P-2147483648Y-2147483648M-2147483648D".length + 2 + val buf = this.buf + val years = x.getYears + val months = x.getMonths + val days = x.getDays + ByteArrayAccess.setLong(buf, pos, 0x2244305022L) + if ((years | months | days) == 0) pos + 5 + else { + pos += 2 + val ds = digits + if (years != 0) pos = writePeriod(years, pos, buf, ds, 0x2259) + if (months != 0) pos = writePeriod(months, pos, buf, ds, 0x224D) + if (days != 0) pos = writePeriod(days, pos, buf, ds, 0x2244) + pos + 1 + } + } + + private[this] def writePeriod(x: Int, p: Int, buf: Array[Byte], ds: Array[Short], bs: Short): Int = { + var pos = p + val q0 = + if (x >= 0) x + else if (x != -2147483648) { + buf(pos) = '-' + pos += 1 + -x + } else { + ByteArrayAccess.setShort(buf, pos, 0x322D) + pos += 2 + 147483648 + } + pos += digitCount(q0) + writePositiveIntDigits(q0, pos, buf, ds) + ByteArrayAccess.setShort(buf, pos, bs) + pos + 1 + } + + private[this] def writeYear(x: Year): Unit = count = { + var pos = ensureBufCapacity(12) // 12 == "+999999999".length + 2 + val buf = this.buf + buf(pos) = '"' + pos = writeYear(x.getValue, pos + 1, buf, digits) + buf(pos) = '"' + pos + 1 + } + + private[this] def writeYearMonth(x: YearMonth): Unit = count = { + var pos = ensureBufCapacity(15) // 15 == "+999999999-12".length + 2 + val buf = this.buf + val ds = digits + buf(pos) = '"' + pos = writeYear(x.getYear, pos + 1, buf, ds) + ByteArrayAccess.setInt(buf, pos, ds(x.getMonthValue) << 8 | 0x2200002D) + pos + 4 + } + + private[this] def writeZonedDateTime(x: ZonedDateTime): Unit = count = { + var pos = ensureBufCapacity(46) // 46 == "+999999999-12-31T23:59:59.999999999+00:00:01".length + 2 + var buf = this.buf + val ds = digits + buf(pos) = '"' + pos = writeOffset(x.getOffset, + writeLocalTime(x.toLocalTime, writeLocalDateWithT(x.toLocalDate, pos + 1, buf, ds), buf, ds), buf, ds) + val zone = x.getZone + if (!zone.isInstanceOf[ZoneOffset]) { + val zoneId = zone.getId + val len = zoneId.length + val required = len + 3 + if (pos + required > limit) { + pos = flushAndGrowBuf(required, pos) + buf = this.buf + } + buf(pos - 1) = '[' + zoneId.getBytes(0, len, buf, pos) + pos += len + ByteArrayAccess.setShort(buf, pos, 0x225D) + pos += 2 + } + pos + } + + private[this] def writeZoneOffset(x: ZoneOffset): Unit = count = { + val pos = ensureBufCapacity(12) // 12 == number of bytes in Long and Int + val buf = this.buf + var y = x.getTotalSeconds + if (y == 0) { + ByteArrayAccess.setInt(buf, pos, 0x225A22) + pos + 3 + } else { + val ds = digits + var m = 0x2230303A00002B22L + if (y < 0) { + y = -y + m = 0x2230303A00002D22L + } + y *= 37283 // Based on James Anhalt's algorithm: https://jk-jeon.github.io/posts/2022/02/jeaiii-algorithm/ + m |= ds(y >>> 27) << 16 + if ((y & 0x7FF8000) == 0) { // check if totalSeconds is divisible by 3600 + ByteArrayAccess.setLong(buf, pos, m) + pos + 8 + } else { + y = (y & 0x7FFFFFF) * 15 + ByteArrayAccess.setLong(buf, pos, ds(y >> 25).toLong << 40 | m) + if ((y & 0x1F80000) == 0) pos + 8 // check if totalSeconds is divisible by 60 + else { + ByteArrayAccess.setInt(buf, pos + 7, ds((y & 0x1FFFFFF) * 15 >> 23) << 8 | 0x2200003A) + pos + 11 + } + } + } + } + + private[this] def writeLocalDateWithT(x: LocalDate, p: Int, buf: Array[Byte], ds: Array[Short]): Int = { + val pos = writeYear(x.getYear, p, buf, ds) + val d1 = ds(x.getMonthValue) << 8 + val d2 = ds(x.getDayOfMonth).toLong << 32 + ByteArrayAccess.setLong(buf, pos, d1 | d2 | 0x5400002D00002DL) + pos + 7 + } + + private[this] def writeYear(year: Int, pos: Int, buf: Array[Byte], ds: Array[Short]): Int = + if (year >= 0 && year < 10000) write4Digits(year, pos, buf, ds) + else writeYearWithSign(year, pos, buf, ds) + + private[this] def writeYearWithSign(year: Int, p: Int, buf: Array[Byte], ds: Array[Short]): Int = { + var q0 = year + var pos = p + var b: Byte = '+' + if (q0 < 0) { + q0 = -q0 + b = '-' + } + buf(pos) = b + pos += 1 + if (q0 < 10000) write4Digits(q0, pos, buf, ds) + else { + pos += digitCount(q0) + writePositiveIntDigits(q0, pos, buf, ds) + pos + } + } + + private[this] def writeLocalTime(x: LocalTime, pos: Int, buf: Array[Byte], ds: Array[Short]): Int = { + val second = x.getSecond + val nano = x.getNano + val d1 = ds(x.getHour) | 0x3A00003A0000L + val d2 = ds(x.getMinute).toLong << 24 + if ((second | nano) == 0) { + ByteArrayAccess.setLong(buf, pos, d1 | d2) + pos + 5 + } else { + val d3 = ds(second).toLong << 48 + ByteArrayAccess.setLong(buf, pos, d1 | d2 | d3) + if (nano == 0) pos + 8 + else writeNanos(nano, pos + 8, buf, ds) + } + } + + private[this] def writeNanos(q0: Long, pos: Int, buf: Array[Byte], ds: Array[Short]): Int = { + val y1 = q0 * 1441151881 // Based on James Anhalt's algorithm for 9 digits: https://jk-jeon.github.io/posts/2022/02/jeaiii-algorithm/ + val y2 = (y1 & 0x1FFFFFFFFFFFFFFL) * 100 + var m = y1 >>> 57 << 8 | ds((y2 >>> 57).toInt) << 16 | 0x302E + if ((y2 & 0x1FFFFF800000000L) == 0) { // check if q0 is divisible by 1000000 + ByteArrayAccess.setInt(buf, pos, m.toInt) + pos + 4 + } else { + val y3 = (y2 & 0x1FFFFFFFFFFFFFFL) * 100 + val y4 = (y3 & 0x1FFFFFFFFFFFFFFL) * 100 + m |= ds((y3 >>> 57).toInt).toLong << 32 + val d = ds((y4 >>> 57).toInt) + ByteArrayAccess.setLong(buf, pos, m | d.toLong << 48) + if ((y4 & 0x1FF000000000000L) == 0 && d <= 0x3039) pos + 7 // check if q0 is divisible by 1000 + else { + ByteArrayAccess.setShort(buf, pos + 8, ds(((y4 & 0x1FFFFFFFFFFFFFFL) * 100 >>> 57).toInt)) + pos + 10 + } + } + } + + private[this] def writeOffset(x: ZoneOffset, pos: Int, buf: Array[Byte], ds: Array[Short]): Int = { + var y = x.getTotalSeconds + if (y == 0) { + ByteArrayAccess.setShort(buf, pos, 0x225A) + pos + 2 + } else { + var m = 0x2230303A00002BL + if (y < 0) { + y = -y + m = 0x2230303A00002DL + } + y *= 37283 // Based on James Anhalt's algorithm: https://jk-jeon.github.io/posts/2022/02/jeaiii-algorithm/ + m |= ds(y >>> 27) << 8 + if ((y & 0x7FF8000) == 0) { // check if totalSeconds is divisible by 3600 + ByteArrayAccess.setLong(buf, pos, m) + pos + 7 + } else { + y = (y & 0x7FFFFFF) * 15 + ByteArrayAccess.setLong(buf, pos, ds(y >> 25).toLong << 32 | m) + if ((y & 0x1F80000) == 0) pos + 7 // check if totalSeconds is divisible by 60 + else { + ByteArrayAccess.setInt(buf, pos + 6, ds((y & 0x1FFFFFF) * 15 >> 23) << 8 | 0x2200003A) + pos + 10 + } + } + } + } + + private[this] def write2Digits(q0: Int, pos: Int, buf: Array[Byte], ds: Array[Short]): Int = { + ByteArrayAccess.setShort(buf, pos, ds(q0)) + pos + 2 + } + + private[this] def write3Digits(q0: Int, pos: Int, buf: Array[Byte], ds: Array[Short]): Int = { + val q1 = q0 * 1311 >> 17 // divide a small positive int by 100 + ByteArrayAccess.setInt(buf, pos, ds(q0 - q1 * 100) << 8 | q1 + '0') + pos + 3 + } + + private[this] def write4Digits(q0: Int, pos: Int, buf: Array[Byte], ds: Array[Short]): Int = { + val q1 = q0 * 5243 >> 19 // divide a small positive int by 100 + val d1 = ds(q0 - q1 * 100) << 16 + val d2 = ds(q1) + ByteArrayAccess.setInt(buf, pos, d1 | d2) + pos + 4 + } + + private[this] def write8Digits(q0: Long, pos: Int, buf: Array[Byte], ds: Array[Short]): Int = { + val y1 = q0 * 140737489 // Based on James Anhalt's algorithm for 8 digits: https://jk-jeon.github.io/posts/2022/02/jeaiii-algorithm/ + val y2 = (y1 & 0x7FFFFFFFFFFFL) * 100 + val y3 = (y2 & 0x7FFFFFFFFFFFL) * 100 + val y4 = (y3 & 0x7FFFFFFFFFFFL) * 100 + val d1 = ds((y1 >> 47).toInt) + val d2 = ds((y2 >> 47).toInt) << 16 + val d3 = ds((y3 >> 47).toInt).toLong << 32 + val d4 = ds((y4 >> 47).toInt).toLong << 48 + ByteArrayAccess.setLong(buf, pos, d1 | d2 | d3 | d4) + pos + 8 + } + + private[this] def write18Digits(q0: Long, pos: Int, buf: Array[Byte], ds: Array[Short]): Int = { + val q1 = Math.multiplyHigh(q0, 6189700196426901375L) >>> 25 // divide a positive long by 100000000 + write8Digits(q0 - q1 * 100000000, { + val q2 = (q1 >> 8) * 1441151881 >> 49 // divide a small positive long by 100000000 + write8Digits(q1 - q2 * 100000000, write2Digits(q2.toInt, pos, buf, ds), buf, ds) + }, buf, ds) + } + + private[this] def writeShort(x: Short): Unit = count = { + var pos = ensureBufCapacity(9) // 8 bytes in long + a byte for the sign + val buf = this.buf + val ds = digits + var q0: Int = x + if (q0 < 0) { + buf(pos) = '-' + pos += 1 + q0 = -q0 + } + if (q0 < 100) { + if (q0 < 10) { + buf(pos) = (q0 + '0').toByte + pos + 1 + } else { + ByteArrayAccess.setShort(buf, pos, ds(q0)) + pos + 2 + } + } else if (q0 < 10000) { + val q1 = q0 * 5243 >> 19 // divide a small positive int by 100 + val d2 = ds(q0 - q1 * 100) + if (q0 < 1000) { + ByteArrayAccess.setInt(buf, pos, q1 + '0' | d2 << 8) + pos + 3 + } else { + ByteArrayAccess.setInt(buf, pos, ds(q1) | d2 << 16) + pos + 4 + } + } else { + val y1 = q0 * 429497L // Based on James Anhalt's algorithm for 5 digits: https://jk-jeon.github.io/posts/2022/02/jeaiii-algorithm/ + val y2 = (y1 & 0xFFFFFFFFL) * 100 + val y3 = (y2 & 0xFFFFFFFFL) * 100 + val d1 = (y1 >> 32).toInt + '0' + val d2 = ds((y2 >> 32).toInt) << 8 + val d3 = ds((y3 >> 32).toInt).toLong << 24 + ByteArrayAccess.setLong(buf, pos, d1 | d2 | d3) + pos + 5 + } + } + + private[this] def writeInt(x: Int): Unit = count = { + var pos = ensureBufCapacity(11) // Int.MinValue.toString.length + val buf = this.buf + val ds = digits + val q0 = + if (x >= 0) x + else if (x != -2147483648) { + buf(pos) = '-' + pos += 1 + -x + } else { + ByteArrayAccess.setShort(buf, pos, 0x322D) + pos += 2 + 147483648 + } + pos += digitCount(q0) + writePositiveIntDigits(q0, pos, buf, ds) + pos + } + + private[this] def writeLong(x: Long): Unit = count = { + var pos = ensureBufCapacity(20) // Long.MinValue.toString.length + val buf = this.buf + val ds = digits + val q0 = + if (x >= 0) x + else if (x != -9223372036854775808L) { + buf(pos) = '-' + pos += 1 + -x + } else { + ByteArrayAccess.setShort(buf, pos, 0x392D) + pos += 2 + 223372036854775808L + } + var q = 0 + var lastPos = pos + if (q0 < 100000000) { + q = q0.toInt + lastPos += digitCount(q0) + pos = lastPos + } else { + val q1 = Math.multiplyHigh(q0, 6189700196426901375L) >>> 25 // divide a positive long by 100000000 + pos = write8Digits(q0 - q1 * 100000000, { + if (q1 < 100000000) { + q = q1.toInt + lastPos += digitCount(q1) + lastPos + } else { + val q2 = (q1 >> 8) * 1441151881 >> 49 // divide a small positive long by 100000000 + q = q2.toInt + lastPos += digitCount(q2) + write8Digits(q1 - q2 * 100000000, lastPos, buf, ds) + } + }, buf, ds) + } + writePositiveIntDigits(q, lastPos, buf, ds) + pos + } + + // Based on the amazing work of Raffaello Giulietti + // "The Schubfach way to render doubles": https://drive.google.com/file/d/1luHhyQF9zKlM8yJ1nebU0OgVYhfC6CBN/view + // Sources with the license are here: https://github.com/c4f7fcce9cb06515/Schubfach/blob/3c92d3c9b1fead540616c918cdfef432bca53dfa/todec/src/math/FloatToDecimal.java + private[this] def writeFloat(x: Float): Unit = count = { + val bits = java.lang.Float.floatToRawIntBits(x) + var pos = ensureBufCapacity(15) + val buf = this.buf + if (bits < 0) { + buf(pos) = '-' + pos += 1 + } + if (x == 0.0f) { + ByteArrayAccess.setInt(buf, pos, 0x302E30) + pos + 3 + } else { + val ieeeExponent = bits >> 23 & 0xFF + val ieeeMantissa = bits & 0x7FFFFF + var e2 = ieeeExponent - 150 + var m2 = ieeeMantissa | 0x800000 + var m10, e10 = 0 + if (e2 == 0) m10 = m2 + else if (e2 >= -23 && e2 < 0 && m2 << e2 == 0) m10 = m2 >> -e2 + else { + var e10Corr, e2Corr = 0 + var cblCorr = 2 + if (ieeeExponent == 0) { + e2 = -149 + m2 = ieeeMantissa + if (ieeeMantissa < 8) { + m2 *= 10 + e10Corr = 1 + } + } else if (ieeeExponent == 255) illegalNumberError(x) + else if (ieeeMantissa == 0 && ieeeExponent > 1) { + e2Corr = 131007 + cblCorr = 1 + } + e10 = e2 * 315653 - e2Corr >> 20 + val g = gs(e10 + 324 << 1) + 1 + val h = (e10 * -108853 >> 15) + e2 + 1 + val cb = m2 << 2 + val vbCorr = (m2 & 0x1) - 1 + val vb = rop(g, cb << h) + val vbl = rop(g, cb - cblCorr << h) + vbCorr + val vbr = rop(g, cb + 2 << h) - vbCorr + if (vb < 400 || { + m10 = (vb * 107374183L >> 32).toInt // divide a positive int by 40 + val vb40 = m10 * 40 + val diff = vbl - vb40 + (vb40 - vbr + 40 ^ diff) >= 0 || { + m10 += ~diff >>> 31 + e10 += 1 + false + } + }) { + m10 = vb >> 2 + val vb4 = vb & 0xFFFFFFFC + var diff = vbl - vb4 + if ((vb4 - vbr + 4 ^ diff) >= 0) diff = (vb & 0x3) + (m10 & 0x1) - 3 + m10 += ~diff >>> 31 + e10 -= e10Corr + } + } + val ds = digits + val len = digitCount(m10) + e10 += len - 1 + if (e10 < -3 || e10 >= 7) { + val lastPos = writeSignificantFractionDigits(m10, pos + len, pos, buf, ds) + ByteArrayAccess.setShort(buf, pos, (buf(pos + 1) | 0x2E00).toShort) + pos = + if (lastPos < pos + 3) { + buf(lastPos) = '0' + lastPos + 1 + } else lastPos + ByteArrayAccess.setShort(buf, pos, 0x2D45) + pos += 1 + if (e10 < 0) { + e10 = -e10 + pos += 1 + } + if (e10 < 10) { + buf(pos) = (e10 + '0').toByte + pos + 1 + } else write2Digits(e10, pos, buf, ds) + } else if (e10 < 0) { + val dotPos = pos + 1 + ByteArrayAccess.setInt(buf, pos, 0x30303030) + pos -= e10 + val lastPos = writeSignificantFractionDigits(m10, pos + len, pos, buf, ds) + buf(dotPos) = '.' + lastPos + } else if (e10 < len - 1) { + val lastPos = writeSignificantFractionDigits(m10, pos + len, pos, buf, ds) + val bs = ByteArrayAccess.getLong(buf, pos) + val s = e10 << 3 + val m = 0xFFFFFFFFFFFF0000L << s + val d1 = (~m & bs) >> 8 + val d2 = 0x2E00L << s + val d3 = m & bs + ByteArrayAccess.setLong(buf, pos, d1 | d2 | d3) + lastPos + } else { + pos += len + writePositiveIntDigits(m10, pos, buf, ds) + ByteArrayAccess.setShort(buf, pos, 0x302E) + pos + 2 + } + } + } + + private[this] def rop(g: Long, cp: Int): Int = { + val x = Math.multiplyHigh(g, cp.toLong << 32) + (x >>> 31).toInt | -x.toInt >>> 31 + } + + // Based on the amazing work of Raffaello Giulietti + // "The Schubfach way to render doubles": https://drive.google.com/file/d/1luHhyQF9zKlM8yJ1nebU0OgVYhfC6CBN/view + // Sources with the license are here: https://github.com/c4f7fcce9cb06515/Schubfach/blob/3c92d3c9b1fead540616c918cdfef432bca53dfa/todec/src/math/DoubleToDecimal.java + private[this] def writeDouble(x: Double): Unit = count = { + val bits = java.lang.Double.doubleToRawLongBits(x) + var pos = ensureBufCapacity(24) + val buf = this.buf + if (bits < 0) { + buf(pos) = '-' + pos += 1 + } + if (x == 0.0) { + ByteArrayAccess.setInt(buf, pos, 0x302E30) + pos + 3 + } else { + val ieeeExponent = (bits >> 52).toInt & 0x7FF + val ieeeMantissa = bits & 0xFFFFFFFFFFFFFL + var e2 = ieeeExponent - 1075 + var m2 = ieeeMantissa | 0x10000000000000L + var m10 = 0L + var e10 = 0 + if (e2 == 0) m10 = m2 + else if (e2 >= -52 && e2 < 0 && m2 << e2 == 0) m10 = m2 >> -e2 + else { + var e10Corr, e2Corr = 0 + var cblCorr = 2 + if (ieeeExponent == 0) { + e2 = -1074 + m2 = ieeeMantissa + if (ieeeMantissa < 3) { + m2 *= 10 + e10Corr = 1 + } + } else if (ieeeExponent == 2047) illegalNumberError(x) + else if (ieeeMantissa == 0 && ieeeExponent > 1) { + e2Corr = 131007 + cblCorr = 1 + } + e10 = e2 * 315653 - e2Corr >> 20 + val i = e10 + 324 << 1 + val g1 = gs(i) + val g0 = gs(i + 1) + val h = (e10 * -108853 >> 15) + e2 + 2 + val cb = m2 << 2 + val vbCorr = (m2.toInt & 0x1) - 1 + val vb = rop(g1, g0, cb << h) + val vbl = rop(g1, g0, cb - cblCorr << h) + vbCorr + val vbr = rop(g1, g0, cb + 2 << h) - vbCorr + if (vb < 400 || { + m10 = Math.multiplyHigh(vb, 461168601842738792L) // divide a positive long by 40 + val vb40 = m10 * 40 + val diff = (vbl - vb40).toInt + ((vb40 - vbr).toInt + 40 ^ diff) >= 0 || { + m10 += ~diff >>> 31 + e10 += 1 + false + } + }) { + m10 = vb >> 2 + val vb4 = vb & 0xFFFFFFFFFFFFFFFCL + var diff = (vbl - vb4).toInt + if (((vb4 - vbr).toInt + 4 ^ diff) >= 0) diff = (vb.toInt & 0x3) + (m10.toInt & 0x1) - 3 + m10 += ~diff >>> 31 + e10 -= e10Corr + } + } + val ds = digits + val len = digitCount(m10) + e10 += len - 1 + if (e10 < -3 || e10 >= 7) { + val lastPos = writeSignificantFractionDigits(m10, pos + len, pos, buf, ds) + ByteArrayAccess.setShort(buf, pos, (buf(pos + 1) | 0x2E00).toShort) + pos = + if (lastPos < pos + 3) { + buf(lastPos) = '0' + lastPos + 1 + } else lastPos + ByteArrayAccess.setShort(buf, pos, 0x2D45) + pos += 1 + if (e10 < 0) { + e10 = -e10 + pos += 1 + } + if (e10 < 10) { + buf(pos) = (e10 + '0').toByte + pos + 1 + } else if (e10 < 100) write2Digits(e10, pos, buf, ds) + else write3Digits(e10, pos, buf, ds) + } else if (e10 < 0) { + val dotPos = pos + 1 + ByteArrayAccess.setInt(buf, pos, 0x30303030) + pos -= e10 + val lastPos = writeSignificantFractionDigits(m10, pos + len, pos, buf, ds) + buf(dotPos) = '.' + lastPos + } else if (e10 < len - 1) { + val lastPos = writeSignificantFractionDigits(m10, pos + len, pos, buf, ds) + val bs = ByteArrayAccess.getLong(buf, pos) + val s = e10 << 3 + val m = 0xFFFFFFFFFFFF0000L << s + val d1 = (~m & bs) >> 8 + val d2 = 0x2E00L << s + val d3 = m & bs + ByteArrayAccess.setLong(buf, pos, d1 | d2 | d3) + lastPos + } else { + pos += len + writePositiveIntDigits(m10.toInt, pos, buf, ds) + ByteArrayAccess.setShort(buf, pos, 0x302E) + pos + 2 + } + } + } + + private[this] def rop(g1: Long, g0: Long, cp: Long): Long = { + val x = Math.multiplyHigh(g0, cp) + (g1 * cp >>> 1) + Math.multiplyHigh(g1, cp) + (x >>> 63) | (-x ^ x) >>> 63 + } + + // Adoption of a nice trick from Daniel Lemire's blog that works for numbers up to 10^18: + // https://lemire.me/blog/2021/06/03/computing-the-number-of-digits-of-an-integer-even-faster/ + private[this] def digitCount(q0: Long): Int = (offsets(java.lang.Long.numberOfLeadingZeros(q0)) + q0 >> 58).toInt + + private[this] def writeSignificantFractionDigits(q: Long, p: Int, pl: Int, buf: Array[Byte], ds: Array[Short]): Int = { + var q0 = q.toInt + var pos = p + var posLim = pl + if (q0 != q) { + val q1 = (Math.multiplyHigh(q, 6189700196426901375L) >>> 25).toInt // divide a positive long by 100000000 + val r1 = (q - q1 * 100000000L).toInt + val posm8 = pos - 8 + if (r1 == 0) { + q0 = q1 + pos = posm8 + } else { + writeFractionDigits(q1, posm8, posLim, buf, ds) + q0 = r1 + posLim = posm8 + } + } + writeSignificantFractionDigits(q0, pos, posLim, buf, ds) + } + + private[this] def writeSignificantFractionDigits(q: Int, p: Int, posLim: Int, buf: Array[Byte], ds: Array[Short]): Int = { + var q0 = q + var q1 = 0 + var pos = p + while ({ + val qp = q0 * 1374389535L + q1 = (qp >> 37).toInt // divide a positive int by 100 + (qp & 0x1FC0000000L) == 0 // check if q is divisible by 100 + }) { + q0 = q1 + pos -= 2 + } + val d = ds(q0 - q1 * 100) + ByteArrayAccess.setShort(buf, pos - 1, d) + writeFractionDigits(q1, pos - 2, posLim, buf, ds) + pos + ((0x3039 - d) >>> 31) + } + + private[this] def writeFractionDigits(q: Int, p: Int, posLim: Int, buf: Array[Byte], ds: Array[Short]): Unit = { + var q0 = q + var pos = p + while (pos > posLim) { + val q1 = (q0 * 1374389535L >> 37).toInt // divide a positive int by 100 + ByteArrayAccess.setShort(buf, pos - 1, ds(q0 - q1 * 100)) + q0 = q1 + pos -= 2 + } + } + + private[this] def writePositiveIntDigits(q: Int, p: Int, buf: Array[Byte], ds: Array[Short]): Unit = { + var q0 = q + var pos = p + while ({ + pos -= 2 + q0 >= 100 + }) { + val q1 = (q0 * 1374389535L >> 37).toInt // divide a positive int by 100 + ByteArrayAccess.setShort(buf, pos, ds(q0 - q1 * 100)) + q0 = q1 + } + if (q0 < 10) buf(pos + 1) = (q0 + '0').toByte + else ByteArrayAccess.setShort(buf, pos, ds(q0)) + } + + private[this] def illegalNumberError(x: Double): Nothing = encodeError("illegal number: " + x) + + private[this] def ensureBufCapacity(required: Int): Int = { + val pos = count + if (pos + required <= limit) pos + else flushAndGrowBuf(required, pos) + } + + private[this] def flushAndGrowBuf(required: Int, pos: Int): Int = + if (bbuf ne null) { + bbuf.put(buf, 0, pos) + if (required > limit) growBuf(required) + 0 + } else if (out ne null) { + out.write(buf, 0, pos) + if (required > limit) growBuf(required) + 0 + } else if (disableBufGrowing) throw new ArrayIndexOutOfBoundsException("`buf` length exceeded") + else { + growBuf(pos + required) + pos + } + + private[this] def growBuf(required: Int): Unit = + setBuf(java.util.Arrays.copyOf(buf, (-1 >>> Integer.numberOfLeadingZeros(limit | required)) + 1)) + + private[this] def reallocateBufToPreferredSize(): Unit = setBuf(new Array[Byte](config.preferredBufSize)) + + private[this] def setBuf(buf: Array[Byte]): Unit = { + this.buf = buf + limit = buf.length + } +} + +object JsonWriter { + /* Use the following code to generate `escapedChars` in Scala REPL: + val es = new Array[Byte](128) + java.util.Arrays.fill(es, 0, 32, -1: Byte) + es('\n') = 'n' + es('\r') = 'r' + es('\t') = 't' + es('\b') = 'b' + es('\f') = 'f' + es('\\') = '\\' + es('\"') = '"' + es(127) = -1 + es.grouped(16).map(_.mkString(", ")).mkString("Array(\n", ",\n", "\n)") + */ + private final val escapedChars: Array[Byte] = Array( + -1, -1, -1, -1, -1, -1, -1, -1, 98, 116, 110, -1, 102, 114, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 0, 0, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 92, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1 + ) + private final val offsets = Array( + 5088146770730811392L, 5088146770730811392L, 5088146770730811392L, 5088146770730811392L, + 5088146770730811392L, 5088146770730811392L, 5088146770730811392L, 5088146770730811392L, + 4889916394579099648L, 4889916394579099648L, 4889916394579099648L, 4610686018427387904L, + 4610686018427387904L, 4610686018427387904L, 4610686018427387904L, 4323355642275676160L, + 4323355642275676160L, 4323355642275676160L, 4035215266123964416L, 4035215266123964416L, + 4035215266123964416L, 3746993889972252672L, 3746993889972252672L, 3746993889972252672L, + 3746993889972252672L, 3458764413820540928L, 3458764413820540928L, 3458764413820540928L, + 3170534127668829184L, 3170534127668829184L, 3170534127668829184L, 2882303760517117440L, + 2882303760517117440L, 2882303760517117440L, 2882303760517117440L, 2594073385265405696L, + 2594073385265405696L, 2594073385265405696L, 2305843009203693952L, 2305843009203693952L, + 2305843009203693952L, 2017612633060982208L, 2017612633060982208L, 2017612633060982208L, + 2017612633060982208L, 1729382256910170464L, 1729382256910170464L, 1729382256910170464L, + 1441151880758548720L, 1441151880758548720L, 1441151880758548720L, 1152921504606845976L, + 1152921504606845976L, 1152921504606845976L, 1152921504606845976L, 864691128455135132L, + 864691128455135132L, 864691128455135132L, 576460752303423478L, 576460752303423478L, + 576460752303423478L, 576460752303423478L, 576460752303423478L, 576460752303423478L, + 576460752303423478L) + /* Use the following code to generate `digits` in Scala REPL: + val ds = new Array[Short](100) + var i, j = 0 + while (j < 10) { + var k = 0 + while (k < 10) { + ds(i) = (((k + '0') << 8) + (j + '0')).toShort + i += 1 + k += 1 + } + j += 1 + } + ds.grouped(10).map(_.mkString(", ")).mkString("Array(\n", ",\n", "\n)") + */ + private final val digits: Array[Short] = Array( + 12336, 12592, 12848, 13104, 13360, 13616, 13872, 14128, 14384, 14640, + 12337, 12593, 12849, 13105, 13361, 13617, 13873, 14129, 14385, 14641, + 12338, 12594, 12850, 13106, 13362, 13618, 13874, 14130, 14386, 14642, + 12339, 12595, 12851, 13107, 13363, 13619, 13875, 14131, 14387, 14643, + 12340, 12596, 12852, 13108, 13364, 13620, 13876, 14132, 14388, 14644, + 12341, 12597, 12853, 13109, 13365, 13621, 13877, 14133, 14389, 14645, + 12342, 12598, 12854, 13110, 13366, 13622, 13878, 14134, 14390, 14646, + 12343, 12599, 12855, 13111, 13367, 13623, 13879, 14135, 14391, 14647, + 12344, 12600, 12856, 13112, 13368, 13624, 13880, 14136, 14392, 14648, + 12345, 12601, 12857, 13113, 13369, 13625, 13881, 14137, 14393, 14649 + ) + /* Use the following code to generate `lowerCaseHexDigits` in Scala REPL: + val ds = new Array[Short](256) + var i, j = 0 + while (j < 16) { + val d1 = + if (j <= 9) j + '0' + else j + 'a' - 10 + var k = 0 + while (k < 16) { + val d2 = + if (k <= 9) k + '0' + else k + 'a' - 10 + ds(i) = ((d2 << 8) + d1).toShort + i += 1 + k += 1 + } + j += 1 + } + ds.grouped(16).map(_.mkString(", ")).mkString("Array(\n", ",\n", "\n)") + */ + private final val lowerCaseHexDigits: Array[Short] = Array( + 12336, 12592, 12848, 13104, 13360, 13616, 13872, 14128, 14384, 14640, 24880, 25136, 25392, 25648, 25904, 26160, + 12337, 12593, 12849, 13105, 13361, 13617, 13873, 14129, 14385, 14641, 24881, 25137, 25393, 25649, 25905, 26161, + 12338, 12594, 12850, 13106, 13362, 13618, 13874, 14130, 14386, 14642, 24882, 25138, 25394, 25650, 25906, 26162, + 12339, 12595, 12851, 13107, 13363, 13619, 13875, 14131, 14387, 14643, 24883, 25139, 25395, 25651, 25907, 26163, + 12340, 12596, 12852, 13108, 13364, 13620, 13876, 14132, 14388, 14644, 24884, 25140, 25396, 25652, 25908, 26164, + 12341, 12597, 12853, 13109, 13365, 13621, 13877, 14133, 14389, 14645, 24885, 25141, 25397, 25653, 25909, 26165, + 12342, 12598, 12854, 13110, 13366, 13622, 13878, 14134, 14390, 14646, 24886, 25142, 25398, 25654, 25910, 26166, + 12343, 12599, 12855, 13111, 13367, 13623, 13879, 14135, 14391, 14647, 24887, 25143, 25399, 25655, 25911, 26167, + 12344, 12600, 12856, 13112, 13368, 13624, 13880, 14136, 14392, 14648, 24888, 25144, 25400, 25656, 25912, 26168, + 12345, 12601, 12857, 13113, 13369, 13625, 13881, 14137, 14393, 14649, 24889, 25145, 25401, 25657, 25913, 26169, + 12385, 12641, 12897, 13153, 13409, 13665, 13921, 14177, 14433, 14689, 24929, 25185, 25441, 25697, 25953, 26209, + 12386, 12642, 12898, 13154, 13410, 13666, 13922, 14178, 14434, 14690, 24930, 25186, 25442, 25698, 25954, 26210, + 12387, 12643, 12899, 13155, 13411, 13667, 13923, 14179, 14435, 14691, 24931, 25187, 25443, 25699, 25955, 26211, + 12388, 12644, 12900, 13156, 13412, 13668, 13924, 14180, 14436, 14692, 24932, 25188, 25444, 25700, 25956, 26212, + 12389, 12645, 12901, 13157, 13413, 13669, 13925, 14181, 14437, 14693, 24933, 25189, 25445, 25701, 25957, 26213, + 12390, 12646, 12902, 13158, 13414, 13670, 13926, 14182, 14438, 14694, 24934, 25190, 25446, 25702, 25958, 26214 + ) + /* Use the following code to generate `upperCaseHexDigits` in Scala REPL: + val ds = new Array[Short](256) + var i, j = 0 + while (j < 16) { + val d1 = + if (j <= 9) j + '0' + else j + 'A' - 10 + var k = 0 + while (k < 16) { + val d2 = + if (k <= 9) k + '0' + else k + 'A' - 10 + ds(i) = ((d2 << 8) + d1).toShort + i += 1 + k += 1 + } + j += 1 + } + ds.grouped(16).map(_.mkString(", ")).mkString("Array(\n", ",\n", "\n)") + */ + private final val upperCaseHexDigits: Array[Short] = Array( + 12336, 12592, 12848, 13104, 13360, 13616, 13872, 14128, 14384, 14640, 16688, 16944, 17200, 17456, 17712, 17968, + 12337, 12593, 12849, 13105, 13361, 13617, 13873, 14129, 14385, 14641, 16689, 16945, 17201, 17457, 17713, 17969, + 12338, 12594, 12850, 13106, 13362, 13618, 13874, 14130, 14386, 14642, 16690, 16946, 17202, 17458, 17714, 17970, + 12339, 12595, 12851, 13107, 13363, 13619, 13875, 14131, 14387, 14643, 16691, 16947, 17203, 17459, 17715, 17971, + 12340, 12596, 12852, 13108, 13364, 13620, 13876, 14132, 14388, 14644, 16692, 16948, 17204, 17460, 17716, 17972, + 12341, 12597, 12853, 13109, 13365, 13621, 13877, 14133, 14389, 14645, 16693, 16949, 17205, 17461, 17717, 17973, + 12342, 12598, 12854, 13110, 13366, 13622, 13878, 14134, 14390, 14646, 16694, 16950, 17206, 17462, 17718, 17974, + 12343, 12599, 12855, 13111, 13367, 13623, 13879, 14135, 14391, 14647, 16695, 16951, 17207, 17463, 17719, 17975, + 12344, 12600, 12856, 13112, 13368, 13624, 13880, 14136, 14392, 14648, 16696, 16952, 17208, 17464, 17720, 17976, + 12345, 12601, 12857, 13113, 13369, 13625, 13881, 14137, 14393, 14649, 16697, 16953, 17209, 17465, 17721, 17977, + 12353, 12609, 12865, 13121, 13377, 13633, 13889, 14145, 14401, 14657, 16705, 16961, 17217, 17473, 17729, 17985, + 12354, 12610, 12866, 13122, 13378, 13634, 13890, 14146, 14402, 14658, 16706, 16962, 17218, 17474, 17730, 17986, + 12355, 12611, 12867, 13123, 13379, 13635, 13891, 14147, 14403, 14659, 16707, 16963, 17219, 17475, 17731, 17987, + 12356, 12612, 12868, 13124, 13380, 13636, 13892, 14148, 14404, 14660, 16708, 16964, 17220, 17476, 17732, 17988, + 12357, 12613, 12869, 13125, 13381, 13637, 13893, 14149, 14405, 14661, 16709, 16965, 17221, 17477, 17733, 17989, + 12358, 12614, 12870, 13126, 13382, 13638, 13894, 14150, 14406, 14662, 16710, 16966, 17222, 17478, 17734, 17990 + ) + /* Use the following code to generate `base64Digits` in Scala REPL: + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".getBytes + .grouped(16).map(_.mkString(", ")).mkString("Array(\n", ",\n", "\n)") + */ + private final val base64Digits: Array[Byte] = Array( + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, + 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, + 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, + 119, 120, 121, 122, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 43, 47 + ) + /* Use the following code to generate `base64UrlDigits` in Scala REPL: + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".getBytes + .grouped(16).map(_.mkString(", ")).mkString("Array(\n", ",\n", "\n)") + */ + private final val base64UrlDigits: Array[Byte] = Array( + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, + 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, + 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, + 119, 120, 121, 122, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 45, 95 + ) + /* Use the following code to generate `gs` in Scala REPL: + val gs = new Array[Long](1234) + var i = 0 + var pow5 = BigInt(1) + while (i < 650) { + val av = (pow5 >> (pow5.bitLength - 126)) + 1 + gs(648 - i) = (av >> 63).longValue & 0x7FFFFFFFFFFFFFFFL + gs(649 - i) = av.longValue & 0x7FFFFFFFFFFFFFFFL + pow5 *= 5 + i += 2 + } + pow5 = BigInt(5) + while (i < 1234) { + val inv = ((BigInt(1) << (pow5.bitLength + 125)) / pow5) + 1 + gs(i) = (inv >> 63).longValue & 0x7FFFFFFFFFFFFFFFL + gs(i + 1) = inv.longValue & 0x7FFFFFFFFFFFFFFFL + pow5 *= 5 + i += 2 + } + gs.grouped(4).map(_.mkString("L, ")).mkString("Array(\n", "L,\n", "L\n)") + */ + private final val gs: Array[Long] = Array( + 5696189077778435540L, 6557778377634271669L, 9113902524445496865L, 1269073367360058862L, + 7291122019556397492L, 1015258693888047090L, 5832897615645117993L, 6346230177223303157L, + 4666318092516094394L, 8766332956520552849L, 7466108948025751031L, 8492109508320019073L, + 5972887158420600825L, 4949013199285060097L, 4778309726736480660L, 3959210559428048077L, + 7645295562778369056L, 6334736895084876923L, 6116236450222695245L, 3223115108696946377L, + 4892989160178156196L, 2578492086957557102L, 7828782656285049914L, 436238524390181040L, + 6263026125028039931L, 2193665226883099993L, 5010420900022431944L, 9133629810990300641L, + 8016673440035891111L, 9079784475471615541L, 6413338752028712889L, 5419153173006337271L, + 5130671001622970311L, 6179996945776024979L, 8209073602596752498L, 6198646298499729642L, + 6567258882077401998L, 8648265853541694037L, 5253807105661921599L, 1384589460720489745L, + 8406091369059074558L, 5904691951894693915L, 6724873095247259646L, 8413102376257665455L, + 5379898476197807717L, 4885807493635177203L, 8607837561916492348L, 438594360332462878L, + 6886270049533193878L, 4040224303007880625L, 5509016039626555102L, 6921528257148214824L, + 8814425663402488164L, 3695747581953323071L, 7051540530721990531L, 4801272472933613619L, + 5641232424577592425L, 1996343570975935733L, 9025971879324147880L, 3194149713561497173L, + 7220777503459318304L, 2555319770849197738L, 5776622002767454643L, 3888930224050313352L, + 4621297602213963714L, 6800492993982161005L, 7394076163542341943L, 5346765568258592123L, + 5915260930833873554L, 7966761269348784022L, 4732208744667098843L, 8218083422849982379L, + 7571533991467358150L, 2080887032334240837L, 6057227193173886520L, 1664709625867392670L, + 4845781754539109216L, 1331767700693914136L, 7753250807262574745L, 7664851543223128102L, + 6202600645810059796L, 6131881234578502482L, 4962080516648047837L, 3060830580291846824L, + 7939328826636876539L, 6742003335837910079L, 6351463061309501231L, 7238277076041283225L, + 5081170449047600985L, 3945947253462071419L, 8129872718476161576L, 6313515605539314269L, + 6503898174780929261L, 3206138077060496254L, 5203118539824743409L, 720236054277441842L, + 8324989663719589454L, 4841726501585817270L, 6659991730975671563L, 5718055608639608977L, + 5327993384780537250L, 8263793301653597505L, 8524789415648859601L, 3998697245790980200L, + 6819831532519087681L, 1354283389261828999L, 5455865226015270144L, 8462124340893283845L, + 8729384361624432231L, 8005375723316388668L, 6983507489299545785L, 4559626171282155773L, + 5586805991439636628L, 3647700937025724618L, 8938889586303418605L, 3991647091870204227L, + 7151111669042734884L, 3193317673496163382L, 5720889335234187907L, 4399328546167885867L, + 9153422936374700651L, 8883600081239572549L, 7322738349099760521L, 5262205657620702877L, + 5858190679279808417L, 2365090118725607140L, 4686552543423846733L, 7426095317093351197L, + 7498484069478154774L, 813706063123630946L, 5998787255582523819L, 2495639257869859918L, + 4799029804466019055L, 3841185813666843096L, 7678447687145630488L, 6145897301866948954L, + 6142758149716504390L, 8606066656235469486L, 4914206519773203512L, 6884853324988375589L, + 7862730431637125620L, 3637067690497580296L, 6290184345309700496L, 2909654152398064237L, + 5032147476247760397L, 483048914547496228L, 8051435961996416635L, 2617552670646949126L, + 6441148769597133308L, 2094042136517559301L, 5152919015677706646L, 5364582523955957764L, + 8244670425084330634L, 4893983223587622099L, 6595736340067464507L, 5759860986241052841L, + 5276589072053971606L, 918539974250931950L, 8442542515286354569L, 7003687180914356604L, + 6754034012229083655L, 7447624152102440445L, 5403227209783266924L, 5958099321681952356L, + 8645163535653227079L, 3998935692578258285L, 6916130828522581663L, 5043822961433561789L, + 5532904662818065330L, 7724407183888759755L, 8852647460508904529L, 3135679457367239799L, + 7082117968407123623L, 4353217973264747001L, 5665694374725698898L, 7171923193353707924L, + 9065110999561118238L, 407030665140201709L, 7252088799648894590L, 4014973346854071690L, + 5801671039719115672L, 3211978677483257352L, 4641336831775292537L, 8103606164099471367L, + 7426138930840468060L, 5587072233075333540L, 5940911144672374448L, 4469657786460266832L, + 4752728915737899558L, 7265075043910123789L, 7604366265180639294L, 556073626030467093L, + 6083493012144511435L, 2289533308195328836L, 4866794409715609148L, 1831626646556263069L, + 7786871055544974637L, 1085928227119065748L, 6229496844435979709L, 6402765803808118083L, + 4983597475548783767L, 6966887050417449628L, 7973755960878054028L, 3768321651184098759L, + 6379004768702443222L, 6704006135689189330L, 5103203814961954578L, 1673856093809441141L, + 8165126103939127325L, 833495342724150664L, 6532100883151301860L, 666796274179320531L, + 5225680706521041488L, 533437019343456425L, 8361089130433666380L, 8232196860433350926L, + 6688871304346933104L, 6585757488346680741L, 5351097043477546483L, 7113280398048299755L, + 8561755269564074374L, 313202192651548637L, 6849404215651259499L, 2095236161492194072L, + 5479523372521007599L, 3520863336564710419L, 8767237396033612159L, 99358116390671185L, + 7013789916826889727L, 1924160900483492110L, 5611031933461511781L, 7073351942499659173L, + 8977651093538418850L, 7628014293257544353L, 7182120874830735080L, 6102411434606035483L, + 5745696699864588064L, 4881929147684828386L, 9193114719783340903L, 2277063414182859933L, + 7354491775826672722L, 5510999546088198270L, 5883593420661338178L, 719450822128648293L, + 4706874736529070542L, 4264909472444828957L, 7530999578446512867L, 8668529563282681493L, + 6024799662757210294L, 3245474835884234871L, 4819839730205768235L, 4441054276078343059L, + 7711743568329229176L, 7105686841725348894L, 6169394854663383341L, 3839875066009323953L, + 4935515883730706673L, 1227225645436504001L, 7896825413969130677L, 118886625327451240L, + 6317460331175304541L, 5629132522374826477L, 5053968264940243633L, 2658631610528906020L, + 8086349223904389813L, 2409136169475294470L, 6469079379123511850L, 5616657750322145900L, + 5175263503298809480L, 4493326200257716720L, 8280421605278095168L, 7189321920412346751L, + 6624337284222476135L, 217434314217011916L, 5299469827377980908L, 173947451373609533L, + 8479151723804769452L, 7657013551681595899L, 6783321379043815562L, 2436262026603366396L, + 5426657103235052449L, 7483032843395558602L, 8682651365176083919L, 6438829327320028278L, + 6946121092140867135L, 6995737869226977784L, 5556896873712693708L, 5596590295381582227L, + 8891034997940309933L, 7109870065239576402L, 7112827998352247947L, 153872830078795637L, + 5690262398681798357L, 5657121486175901994L, 9104419837890877372L, 1672696748397622544L, + 7283535870312701897L, 6872180620830963520L, 5826828696250161518L, 1808395681922860493L, + 4661462957000129214L, 5136065360280198718L, 7458340731200206743L, 2683681354335452463L, + 5966672584960165394L, 5836293898210272294L, 4773338067968132315L, 6513709525939172997L, + 7637340908749011705L, 1198563204647900987L, 6109872726999209364L, 958850563718320789L, + 4887898181599367491L, 2611754858345611793L, 7820637090558987986L, 489458958611068546L, + 6256509672447190388L, 7770264796372675483L, 5005207737957752311L, 682188614985274902L, + 8008332380732403697L, 6625525006089305327L, 6406665904585922958L, 1611071190129533939L, + 5125332723668738366L, 4978205766845537474L, 8200532357869981386L, 4275780412210949635L, + 6560425886295985109L, 1575949922397804547L, 5248340709036788087L, 3105434345289198799L, + 8397345134458860939L, 6813369359833673240L, 6717876107567088751L, 7295369895237893754L, + 5374300886053671001L, 3991621508819359841L, 8598881417685873602L, 2697245599369065423L, + 6879105134148698881L, 7691819701608117823L, 5503284107318959105L, 4308781353915539097L, + 8805254571710334568L, 6894050166264862555L, 7044203657368267654L, 9204588947753800367L, + 5635362925894614123L, 9208345565573995455L, 9016580681431382598L, 3665306460692661759L, + 7213264545145106078L, 6621593983296039730L, 5770611636116084862L, 8986624001378742108L, + 4616489308892867890L, 3499950386361083363L, 7386382894228588624L, 5599920618177733380L, + 5909106315382870899L, 6324610901913141866L, 4727285052306296719L, 6904363128901468655L, + 7563656083690074751L, 5512957784129484362L, 6050924866952059801L, 2565691819932632328L, + 4840739893561647841L, 207879048575150701L, 7745183829698636545L, 5866629699833106606L, + 6196147063758909236L, 4693303759866485285L, 4956917651007127389L, 1909968600522233067L, + 7931068241611403822L, 6745298575577483229L, 6344854593289123058L, 1706890045720076260L, + 5075883674631298446L, 5054860851317971332L, 8121413879410077514L, 4398428547366843807L, + 6497131103528062011L, 5363417245264430207L, 5197704882822449609L, 2446059388840589004L, + 8316327812515919374L, 7603043836886852730L, 6653062250012735499L, 7927109476880437346L, + 5322449800010188399L, 8186361988875305038L, 8515919680016301439L, 7564155960087622576L, + 6812735744013041151L, 7895999175441053223L, 5450188595210432921L, 4472124932981887417L, + 8720301752336692674L, 3466051078029109543L, 6976241401869354139L, 4617515269794242796L, + 5580993121495483311L, 5538686623206349399L, 8929588994392773298L, 5172549782388248714L, + 7143671195514218638L, 7827388640652509295L, 5714936956411374911L, 727887690409141951L, + 9143899130258199857L, 6698643526767492606L, 7315119304206559886L, 1669566006672083762L, + 5852095443365247908L, 8714350434821487656L, 4681676354692198327L, 1437457125744324640L, + 7490682167507517323L, 4144605808561874585L, 5992545734006013858L, 7005033461591409992L, + 4794036587204811087L, 70003547160262509L, 7670458539527697739L, 1956680082827375175L, + 6136366831622158191L, 3410018473632855302L, 4909093465297726553L, 883340371535329080L, + 7854549544476362484L, 8792042223940347174L, 6283639635581089987L, 8878308186523232901L, + 5026911708464871990L, 3413297734476675998L, 8043058733543795184L, 5461276375162681596L, + 6434446986835036147L, 6213695507501100438L, 5147557589468028918L, 1281607591258970028L, + 8236092143148846269L, 205897738643396882L, 6588873714519077015L, 2009392598285672668L, + 5271098971615261612L, 1607514078628538134L, 8433758354584418579L, 4416696933176616176L, + 6747006683667534863L, 5378031953912248102L, 5397605346934027890L, 7991774377871708805L, + 8636168555094444625L, 3563466967739958280L, 6908934844075555700L, 2850773574191966624L, + 5527147875260444560L, 2280618859353573299L, 8843436600416711296L, 3648990174965717279L, + 7074749280333369037L, 1074517732601618662L, 5659799424266695229L, 6393637408194160414L, + 9055679078826712367L, 4695796630997791177L, 7244543263061369894L, 67288490056322619L, + 5795634610449095915L, 1898505199416013257L, 4636507688359276732L, 1518804159532810606L, + 7418412301374842771L, 4274761062623452130L, 5934729841099874217L, 1575134442727806543L, + 4747783872879899373L, 6794130776295110719L, 7596454196607838997L, 9025934834701221989L, + 6077163357286271198L, 3531399053019067268L, 4861730685829016958L, 6514468057157164137L, + 7778769097326427133L, 8578474484080507458L, 6223015277861141707L, 1328756365151540482L, + 4978412222288913365L, 6597028314234097870L, 7965459555662261385L, 1331873265919780784L, + 6372367644529809108L, 1065498612735824627L, 5097894115623847286L, 4541747704930570025L, + 8156630584998155658L, 3577447513147001717L, 6525304467998524526L, 6551306825259511697L, + 5220243574398819621L, 3396371052836654196L, 8352389719038111394L, 1744844869796736390L, + 6681911775230489115L, 3240550303208344274L, 5345529420184391292L, 2592440242566675419L, + 8552847072295026067L, 5992578795477635832L, 6842277657836020854L, 1104714221640198342L, + 5473822126268816683L, 2728445784683113836L, 8758115402030106693L, 2520838848122026975L, + 7006492321624085354L, 5706019893239531903L, 5605193857299268283L, 6409490321962580684L, + 8968310171678829253L, 8410510107769173933L, 7174648137343063403L, 1194384864102473662L, + 5739718509874450722L, 4644856706023889253L, 9183549615799121156L, 53073100154402158L, + 7346839692639296924L, 7421156109607342373L, 5877471754111437539L, 7781599295056829060L, + 4701977403289150031L, 8069953843416418410L, 7523163845262640050L, 9222577334724359132L, + 6018531076210112040L, 7378061867779487306L, 4814824860968089632L, 5902449494223589845L, + 7703719777548943412L, 2065221561273923105L, 6162975822039154729L, 7186200471132003969L, + 4930380657631323783L, 7593634784276558337L, 7888609052210118054L, 1081769210616762369L, + 6310887241768094443L, 2710089775864365057L, 5048709793414475554L, 5857420635433402369L, + 8077935669463160887L, 3837849794580578305L, 6462348535570528709L, 8604303057777328129L, + 5169878828456422967L, 8728116853592817665L, 8271806125530276748L, 6586289336264687617L, + 6617444900424221398L, 8958380283753660417L, 5293955920339377119L, 1632681004890062849L, + 8470329472543003390L, 6301638422566010881L, 6776263578034402712L, 5041310738052808705L, + 5421010862427522170L, 343699775700336641L, 8673617379884035472L, 549919641120538625L, + 6938893903907228377L, 5973958935009296385L, 5551115123125782702L, 1089818333265526785L, + 8881784197001252323L, 3588383740595798017L, 7105427357601001858L, 6560055807218548737L, + 5684341886080801486L, 8937393460516749313L, 9094947017729282379L, 1387108685230112769L, + 7275957614183425903L, 2954361355555045377L, 5820766091346740722L, 6052837899185946625L, + 4656612873077392578L, 1152921504606846977L, 7450580596923828125L, 1L, + 5960464477539062500L, 1L, 4768371582031250000L, 1L, + 7629394531250000000L, 1L, 6103515625000000000L, 1L, + 4882812500000000000L, 1L, 7812500000000000000L, 1L, + 6250000000000000000L, 1L, 5000000000000000000L, 1L, + 8000000000000000000L, 1L, 6400000000000000000L, 1L, + 5120000000000000000L, 1L, 8192000000000000000L, 1L, + 6553600000000000000L, 1L, 5242880000000000000L, 1L, + 8388608000000000000L, 1L, 6710886400000000000L, 1L, + 5368709120000000000L, 1L, 8589934592000000000L, 1L, + 6871947673600000000L, 1L, 5497558138880000000L, 1L, + 8796093022208000000L, 1L, 7036874417766400000L, 1L, + 5629499534213120000L, 1L, 9007199254740992000L, 1L, + 7205759403792793600L, 1L, 5764607523034234880L, 1L, + 4611686018427387904L, 1L, 7378697629483820646L, 3689348814741910324L, + 5902958103587056517L, 1106804644422573097L, 4722366482869645213L, 6419466937650923963L, + 7555786372591432341L, 8426472692870523179L, 6044629098073145873L, 4896503746925463381L, + 4835703278458516698L, 7606551812282281028L, 7737125245533626718L, 1102436455425918676L, + 6189700196426901374L, 4571297979082645264L, 4951760157141521099L, 5501712790637071373L, + 7922816251426433759L, 3268717242906448711L, 6338253001141147007L, 4459648201696114131L, + 5070602400912917605L, 9101741783469756789L, 8112963841460668169L, 5339414816696835055L, + 6490371073168534535L, 6116206260728423206L, 5192296858534827628L, 4892965008582738565L, + 8307674973655724205L, 5984069606361426541L, 6646139978924579364L, 4787255685089141233L, + 5316911983139663491L, 5674478955442268148L, 8507059173023461586L, 5389817513965718714L, + 6805647338418769269L, 2467179603801619810L, 5444517870735015415L, 3818418090412251009L, + 8711228593176024664L, 6109468944659601615L, 6968982874540819731L, 6732249563098636453L, + 5575186299632655785L, 3541125243107954001L, 8920298079412249256L, 5665800388972726402L, + 7136238463529799405L, 2687965903807225960L, 5708990770823839524L, 2150372723045780768L, + 9134385233318143238L, 7129945171615159552L, 7307508186654514591L, 169932915179262157L, + 5846006549323611672L, 7514643961627230372L, 4676805239458889338L, 2322366354559873974L, + 7482888383134222941L, 1871111759924843197L, 5986310706507378352L, 8875587037423695204L, + 4789048565205902682L, 3411120815197045840L, 7662477704329444291L, 7302467711686228506L, + 6129982163463555433L, 3997299761978027643L, 4903985730770844346L, 6887188624324332438L, + 7846377169233350954L, 7330152984177021577L, 6277101735386680763L, 7708796794712572423L, + 5021681388309344611L, 633014213657192454L, 8034690221294951377L, 6546845963964373411L, + 6427752177035961102L, 1548127956429588405L, 5142201741628768881L, 6772525587256536209L, + 8227522786606030210L, 7146692124868547611L, 6582018229284824168L, 5717353699894838089L, + 5265614583427859334L, 8263231774657780795L, 8424983333484574935L, 7687147617339583786L, + 6739986666787659948L, 6149718093871667029L, 5391989333430127958L, 8609123289839243947L, + 8627182933488204734L, 2706550819517059345L, 6901746346790563787L, 4009915062984602637L, + 5521397077432451029L, 8741955272500547595L, 8834235323891921647L, 8453105213888010667L, + 7067388259113537318L, 3073135356368498210L, 5653910607290829854L, 6147857099836708891L, + 9046256971665327767L, 4302548137625868741L, 7237005577332262213L, 8976061732213560478L, + 5789604461865809771L, 1646826163657982898L, 4631683569492647816L, 8696158560410206965L, + 7410693711188236507L, 1001132845059645012L, 5928554968950589205L, 6334929498160581494L, + 4742843975160471364L, 5067943598528465196L, 7588550360256754183L, 2574686535532678828L, + 6070840288205403346L, 5749098043168053386L, 4856672230564322677L, 2754604027163487547L, + 7770675568902916283L, 6252040850832535236L, 6216540455122333026L, 8690981495407938512L, + 4973232364097866421L, 5108110788955395648L, 7957171782556586274L, 4483628447586722714L, + 6365737426045269019L, 5431577165440333333L, 5092589940836215215L, 6189936139723221828L, + 8148143905337944345L, 680525786702379117L, 6518515124270355476L, 544420629361903293L, + 5214812099416284380L, 7814234132973343281L, 8343699359066055009L, 3279402575902573442L, + 6674959487252844007L, 4468196468093013915L, 5339967589802275205L, 9108580396587276617L, + 8543948143683640329L, 5350356597684866779L, 6835158514946912263L, 6124959685518848585L, + 5468126811957529810L, 8589316563156989191L, 8749002899132047697L, 4519534464196406897L, + 6999202319305638157L, 9149650793469991003L, 5599361855444510526L, 3630371820034082479L, + 8958978968711216842L, 2119246097312621643L, 7167183174968973473L, 7229420099962962799L, + 5733746539975178779L, 249512857857504755L, 9173994463960286046L, 4088569387313917931L, + 7339195571168228837L, 1426181102480179183L, 5871356456934583069L, 6674968104097008831L, + 4697085165547666455L, 7184648890648562227L, 7515336264876266329L, 2272066188182923754L, + 6012269011901013063L, 3662327357917294165L, 4809815209520810450L, 6619210701075745655L, + 7695704335233296721L, 1367365084866417240L, 6156563468186637376L, 8472589697376954439L, + 4925250774549309901L, 4933397350530608390L, 7880401239278895842L, 4204086946107063100L, + 6304320991423116673L, 8897292778998515965L, 5043456793138493339L, 1583811001085947287L, + 8069530869021589342L, 6223446416479425982L, 6455624695217271474L, 1289408318441630463L, + 5164499756173817179L, 2876201062124259532L, 8263199609878107486L, 8291270514140725574L, + 6610559687902485989L, 4788342003941625298L, 5288447750321988791L, 5675348010524255400L, + 8461516400515182066L, 5391208002096898316L, 6769213120412145653L, 2468291994306563491L, + 5415370496329716522L, 5663982410187161116L, 8664592794127546436L, 1683674226815637140L, + 6931674235302037148L, 8725637010936330358L, 5545339388241629719L, 1446486386636198802L, + 8872543021186607550L, 6003727033359828406L, 7098034416949286040L, 4802981626687862725L, + 5678427533559428832L, 3842385301350290180L, 9085484053695086131L, 7992490889531419449L, + 7268387242956068905L, 4549318304254180398L, 5814709794364855124L, 3639454643403344318L, + 4651767835491884099L, 4756238122093630616L, 7442828536787014559L, 2075957773236943501L, + 5954262829429611647L, 3505440625960509963L, 4763410263543689317L, 8338375722881273455L, + 7621456421669902908L, 5962703527126216881L, 6097165137335922326L, 8459511636442883828L, + 4877732109868737861L, 4922934901783351901L, 7804371375789980578L, 4187347028111452718L, + 6243497100631984462L, 7039226437231072498L, 4994797680505587570L, 1942032335042947675L, + 7991676288808940112L, 3107251736068716280L, 6393341031047152089L, 8019824610967838509L, + 5114672824837721671L, 8260534096145225969L, 8183476519740354675L, 304133702235675419L, + 6546781215792283740L, 243306961788540335L, 5237424972633826992L, 194645569430832268L, + 8379879956214123187L, 2156107318460286790L, 6703903964971298549L, 7258909076881094917L, + 5363123171977038839L, 7651801668875831096L, 8580997075163262143L, 6708859448088464268L, + 6864797660130609714L, 9056436373212681737L, 5491838128104487771L, 9089823505941100552L, + 8786941004967180435L, 1630996757909074751L, 7029552803973744348L, 1304797406327259801L, + 5623642243178995478L, 4733186739803718164L, 8997827589086392765L, 5728424376314993901L, + 7198262071269114212L, 4582739501051995121L, 5758609657015291369L, 9200214822954461581L, + 9213775451224466191L, 9186320494614273045L, 7371020360979572953L, 5504381988320463275L, + 5896816288783658362L, 8092854405398280943L, 4717453031026926690L, 2784934709576714431L, + 7547924849643082704L, 4455895535322743090L, 6038339879714466163L, 5409390835629149634L, + 4830671903771572930L, 8016861483245230030L, 7729075046034516689L, 3603606336337592240L, + 6183260036827613351L, 4727559476441028954L, 4946608029462090681L, 1937373173781868001L, + 7914572847139345089L, 8633820300163854287L, 6331658277711476071L, 8751730647502038591L, + 5065326622169180857L, 5156710110630675711L, 8104522595470689372L, 872038547525260492L, + 6483618076376551497L, 6231654060133073878L, 5186894461101241198L, 1295974433364548779L, + 8299031137761985917L, 228884686012322885L, 6639224910209588733L, 5717130970922723793L, + 5311379928167670986L, 8263053591480089358L, 8498207885068273579L, 308164894771456841L, + 6798566308054618863L, 2091206323188120634L, 5438853046443695090L, 5362313873292406831L, + 8702164874309912144L, 8579702197267850929L, 6961731899447929715L, 8708436165185235905L, + 5569385519558343772L, 6966748932148188724L, 8911016831293350036L, 3768100661953281312L, + 7128813465034680029L, 1169806122191669888L, 5703050772027744023L, 2780519305124291072L, + 9124881235244390437L, 2604156480827910553L, 7299904988195512349L, 7617348406775193928L, + 5839923990556409879L, 7938553132791110304L, 4671939192445127903L, 8195516913603843405L, + 7475102707912204646L, 2044780617540418478L, 5980082166329763716L, 9014522123516155429L, + 4784065733063810973L, 5366943291441969181L, 7654505172902097557L, 6742434858936195528L, + 6123604138321678046L, 1704599072407046100L, 4898883310657342436L, 8742376887409457526L, + 7838213297051747899L, 1075082168258445910L, 6270570637641398319L, 2704740141977711890L, + 5016456510113118655L, 4008466520953124674L, 8026330416180989848L, 6413546433524999478L, + 6421064332944791878L, 8820185961561909905L, 5136851466355833503L, 1522125547136662440L, + 8218962346169333605L, 590726468047704741L, 6575169876935466884L, 472581174438163793L, + 5260135901548373507L, 2222739346921486196L, 8416217442477397611L, 5401057362445333075L, + 6732973953981918089L, 2476171482585311299L, 5386379163185534471L, 3825611593439204201L, + 8618206661096855154L, 2431629734760816398L, 6894565328877484123L, 3789978195179608280L, + 5515652263101987298L, 6721331370885596947L, 8825043620963179677L, 8909455786045999954L, + 7060034896770543742L, 3438215814094889640L, 5648027917416434993L, 8284595873388777197L, + 9036844667866295990L, 2187306953196312545L, 7229475734293036792L, 1749845562557050036L, + 5783580587434429433L, 6933899672158505514L, 4626864469947543547L, 13096515613938926L, + 7402983151916069675L, 1865628832353257443L, 5922386521532855740L, 1492503065882605955L, + 4737909217226284592L, 1194002452706084764L, 7580654747562055347L, 3755078331700690783L, + 6064523798049644277L, 8538085887473418112L, 4851619038439715422L, 3141119895236824166L, + 7762590461503544675L, 6870466239749873827L, 6210072369202835740L, 5496372991799899062L, + 4968057895362268592L, 4397098393439919250L, 7948892632579629747L, 8880031836874825961L, + 6359114106063703798L, 3414676654757950445L, 5087291284850963038L, 6421090138548270680L, + 8139666055761540861L, 8429069814306277926L, 6511732844609232689L, 4898581444074067179L, + 5209386275687386151L, 5763539562630208905L, 8335018041099817842L, 5532314485466423924L, + 6668014432879854274L, 736502773631228816L, 5334411546303883419L, 2433876626275938215L, + 8535058474086213470L, 7583551416783411467L, 6828046779268970776L, 6066841133426729173L, + 5462437423415176621L, 3008798499370428177L, 8739899877464282594L, 1124728784250774760L, + 6991919901971426075L, 2744457434771574970L, 5593535921577140860L, 2195565947817259976L, + 8949657474523425376L, 3512905516507615961L, 7159725979618740301L, 965650005835137607L, + 5727780783694992240L, 8151217634151930732L, 9164449253911987585L, 3818576177788313364L, + 7331559403129590068L, 3054860942230650691L, 5865247522503672054L, 6133237568526430876L, + 4692198018002937643L, 6751264462192099863L, 7507516828804700229L, 8957348732136404618L, + 6006013463043760183L, 9010553393080078856L, 4804810770435008147L, 1674419492351197600L, + 7687697232696013035L, 4523745595132871322L, 6150157786156810428L, 3618996476106297057L, + 4920126228925448342L, 6584545995626947969L, 7872201966280717348L, 3156575963519296104L, + 6297761573024573878L, 6214609585557347207L, 5038209258419659102L, 8661036483187788089L, + 8061134813471454564L, 6478960743616640295L, 6448907850777163651L, 7027843002264267398L, + 5159126280621730921L, 3777599994440458757L, 8254602048994769474L, 2354811176362823687L, + 6603681639195815579L, 3728523348461214111L, 5282945311356652463L, 4827493086139926451L, + 8452712498170643941L, 5879314530452927160L, 6762169998536515153L, 2858777216991386566L, + 5409735998829212122L, 5976370588335019576L, 8655577598126739396L, 2183495311852210675L, + 6924462078501391516L, 9125493878965589187L, 5539569662801113213L, 5455720695801516188L, + 8863311460481781141L, 6884478705911470739L, 7090649168385424913L, 3662908557358221429L, + 5672519334708339930L, 6619675660628487467L, 9076030935533343889L, 1368109020150804139L, + 7260824748426675111L, 2939161623491598473L, 5808659798741340089L, 506654891422323617L, + 4646927838993072071L, 2249998320508814055L, 7435084542388915313L, 9134020534926967972L, + 5948067633911132251L, 1773193205828708893L, 4758454107128905800L, 8797252194146787761L, + 7613526571406249281L, 4852231473780084609L, 6090821257124999425L, 2037110771653112526L, + 4872657005699999540L, 1629688617322490021L, 7796251209119999264L, 2607501787715984033L, + 6237000967295999411L, 3930675837543742388L, 4989600773836799529L, 1299866262664038749L, + 7983361238138879246L, 5769134835004372321L, 6386688990511103397L, 2770633460632542696L, + 5109351192408882717L, 7750529990618899641L, 8174961907854212348L, 5022150355506418780L, + 6539969526283369878L, 7707069099147045347L, 5231975621026695903L, 631632057204770793L, + 8371160993642713444L, 8389308921011453915L, 6696928794914170755L, 8556121544180118293L, + 5357543035931336604L, 6844897235344094635L, 8572068857490138567L, 5417812354437685931L, + 6857655085992110854L, 644901068808238421L, 5486124068793688683L, 2360595262417545899L, + 8777798510069901893L, 1932278012497118276L, 7022238808055921514L, 5235171224739604944L, + 5617791046444737211L, 6032811387162639117L, 8988465674311579538L, 5963149404718312264L, + 7190772539449263630L, 8459868338516560134L, 5752618031559410904L, 6767894670813248108L, + 9204188850495057447L, 5294608251188331487L + ) + @volatile private[this] var tenPow18Squares: Array[BigInteger] = Array(BigInteger.valueOf(1000000000000000000L)) + + final private def getTenPow18Squares(n: Int): Array[BigInteger] = { + var ss = tenPow18Squares + var i = ss.length + if (n >= i) { + var s = ss(i - 1) + ss = java.util.Arrays.copyOf(ss, n + 1) + while (i <= n) { + s = s.multiply(s) + ss(i) = s + i += 1 + } + tenPow18Squares = ss + } + ss + } + + /** + * Checks if a character does not require JSON escaping or encoding. + * + * @param ch the character to check + * @return `true` if the character is a basic ASCII character (code point less than `0x80`) that does not need JSON escaping + */ + final def isNonEscapedAscii(ch: Char): Boolean = ch < 0x80 && escapedChars(ch) == 0 +} \ No newline at end of file diff --git a/rnd/src/main/scala/co.blocke.scalajack/JsonWriterException.scala b/rnd/src/main/scala/co.blocke.scalajack/JsonWriterException.scala new file mode 100644 index 00000000..2e12b70a --- /dev/null +++ b/rnd/src/main/scala/co.blocke.scalajack/JsonWriterException.scala @@ -0,0 +1,4 @@ +package co.blocke.scalajack + +class JsonWriterException (msg: String, cause: Throwable, withStackTrace: Boolean) + extends RuntimeException(msg, cause, true, withStackTrace) diff --git a/rnd/src/main/scala/co.blocke.scalajack/Run.scala b/rnd/src/main/scala/co.blocke.scalajack/Run.scala new file mode 100644 index 00000000..39e0e965 --- /dev/null +++ b/rnd/src/main/scala/co.blocke.scalajack/Run.scala @@ -0,0 +1,27 @@ +package co.blocke.scalajack + +object RunMe extends App: + + val cfg = WriterConfig + .withEscapeUnicode(false) + .withIndentionStep(0) + .withPreferredBufSize(32768) // 2^15 + .withThrowWriterExceptionWithStackTrace(true) + + val writer = JsonWriter(new Array[Byte](256), 0, 0, 0, false, false, null, null, cfg) + + writer.writeArrayStart() + (0 to 300).map(writer.writeVal(_)) + writer.writeArrayEnd() + + println("Result: "+writer.result) + writer.reset + writer.writeArrayStart() + writer.writeVal("Greogry") + writer.writeVal("William") + writer.writeVal("Zoller") + writer.writeArrayEnd() + println("Name: "+writer.result) + + println("Done.") + diff --git a/rnd/src/main/scala/co.blocke.scalajack/WriterConfig.scala b/rnd/src/main/scala/co.blocke.scalajack/WriterConfig.scala new file mode 100644 index 00000000..7ba0fa94 --- /dev/null +++ b/rnd/src/main/scala/co.blocke.scalajack/WriterConfig.scala @@ -0,0 +1,53 @@ +package co.blocke.scalajack + +/** + * Configuration for [[com.github.plokhotnyuk.jsoniter_scala.core.JsonWriter]] that contains params for formatting of + * output JSON and for tuning of preferred size for internal byte buffer that is created on the writer instantiation + * and reused in runtime for serialization of messages using [[java.io.OutputStream]] or [[java.nio.DirectByteBuffer]]. + *
+ * All configuration params already initialized to default values, but in some cases they should be altered: + *
    + *
  • turn on pretty printing by specifying of indention step that is greater than 0
  • + *
  • turn on escaping of Unicode characters to serialize with only ASCII characters
  • + *
  • increase preferred size of an internal byte buffer to reduce allocation rate of grown and then reduced buffers + * when writing to [[java.io.OutputStream]] or [[java.nio.DirectByteBuffer]] lot of large (>16Kb) + * [[scala.math.BigDecimal]] or [[scala.math.BigInt]] or other non escaped ASCII strings written using + * `JsonWriter.writeNonEscapedAsciiKey` or `JsonWriter.writeNonEscapedAsciiVal`
  • + *
+ * @param throwWriterExceptionWithStackTrace a flag that allows to turn on a stack traces for debugging purposes in + * development + * @param indentionStep a size of indention for pretty-printed formatting or 0 for compact output + * @param escapeUnicode a flag to turn on hexadecimal escaping of all non-ASCII chars + * @param preferredBufSize a preferred size (in bytes) of an internal byte buffer when writing to + * [[java.io.OutputStream]] or [[java.nio.DirectByteBuffer]] + */ +class WriterConfig private (val indentionStep: Int, val preferredBufSize: Int, val escapeUnicode: Boolean, + val throwWriterExceptionWithStackTrace: Boolean) extends Serializable { + def withThrowWriterExceptionWithStackTrace(throwWriterExceptionWithStackTrace: Boolean): WriterConfig = + copy(throwWriterExceptionWithStackTrace = throwWriterExceptionWithStackTrace) + + def withIndentionStep(indentionStep: Int): WriterConfig = { + if (indentionStep < 0) throw new IllegalArgumentException("'indentionStep' should be not less than 0") + copy(indentionStep = indentionStep) + } + + def withEscapeUnicode(escapeUnicode: Boolean): WriterConfig = + copy(escapeUnicode = escapeUnicode) + + def withPreferredBufSize(preferredBufSize: Int): WriterConfig = { + if (preferredBufSize <= 0) throw new IllegalArgumentException("'preferredBufSize' should be not less than 1") + copy(preferredBufSize = preferredBufSize) + } + + private[this] def copy(indentionStep: Int = indentionStep, preferredBufSize: Int = preferredBufSize, + throwWriterExceptionWithStackTrace: Boolean = throwWriterExceptionWithStackTrace, + escapeUnicode: Boolean = escapeUnicode): WriterConfig = + new WriterConfig( + indentionStep = indentionStep, + preferredBufSize = preferredBufSize, + escapeUnicode = escapeUnicode, + throwWriterExceptionWithStackTrace = throwWriterExceptionWithStackTrace) +} + +object WriterConfig extends WriterConfig(indentionStep = 0, preferredBufSize = 32768, escapeUnicode = false, + throwWriterExceptionWithStackTrace = false) \ No newline at end of file diff --git a/src/main/java/co/blocke/scalajack/json/ByteArrayAccess.java b/src/main/java/co/blocke/scalajack/json/ByteArrayAccess.java new file mode 100644 index 00000000..99771032 --- /dev/null +++ b/src/main/java/co/blocke/scalajack/json/ByteArrayAccess.java @@ -0,0 +1,46 @@ +package co.blocke.scalajack.json; + +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; +import java.nio.ByteOrder; + +class ByteArrayAccess { // FIXME: Use Java wrapper as w/a for missing support of @PolymorphicSignature methods in Scala 3, see: https://github.com/lampepfl/dotty/issues/11332 + private static final VarHandle VH_LONG = + MethodHandles.byteArrayViewVarHandle(long[].class, ByteOrder.LITTLE_ENDIAN); + private static final VarHandle VH_INT = + MethodHandles.byteArrayViewVarHandle(int[].class, ByteOrder.LITTLE_ENDIAN); + private static final VarHandle VH_SHORT = + MethodHandles.byteArrayViewVarHandle(short[].class, ByteOrder.LITTLE_ENDIAN); + private static final VarHandle VH_LONG_REVERSED = + MethodHandles.byteArrayViewVarHandle(long[].class, ByteOrder.BIG_ENDIAN); + private static final VarHandle VH_INT_REVERSED = + MethodHandles.byteArrayViewVarHandle(int[].class, ByteOrder.BIG_ENDIAN); + + static void setLong(byte[] buf, int pos, long value) { + VH_LONG.set(buf, pos, value); + } + + static long getLong(byte[] buf, int pos) { + return (long) VH_LONG.get(buf, pos); + } + + static void setInt(byte[] buf, int pos, int value) { + VH_INT.set(buf, pos, value); + } + + static int getInt(byte[] buf, int pos) { + return (int) VH_INT.get(buf, pos); + } + + static void setShort(byte[] buf, int pos, short value) { + VH_SHORT.set(buf, pos, value); + } + + static void setLongReversed(byte[] buf, int pos, long value) { + VH_LONG_REVERSED.set(buf, pos, value); + } + + static int getIntReversed(byte[] buf, int pos) { + return (int) VH_INT_REVERSED.get(buf, pos); + } +} \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/ScalaJack.scala b/src/main/scala/co.blocke.scalajack/ScalaJack.scala index e6d4f5bc..badd1970 100644 --- a/src/main/scala/co.blocke.scalajack/ScalaJack.scala +++ b/src/main/scala/co.blocke.scalajack/ScalaJack.scala @@ -5,19 +5,38 @@ import co.blocke.scala_reflection.reflect.ReflectOnType import co.blocke.scala_reflection.reflect.rtypeRefs.ClassRef import scala.collection.mutable.{HashMap, Map} import scala.quoted.* +import scala.reflect.ClassTag + import quoted.Quotes import json.* -object sj: // Shorter and "lighter" than "ScalaJack" everywhere. +object ScalaJack: + + inline def inspect[T]: sj[T] = ${ inspectImpl[T] } + + def inspectImpl[T](using q: Quotes, tt: Type[T]): Expr[sj[T]] = + import q.reflect.* + val classRef = ReflectOnType[T](quotes)(TypeRepr.of[T], true)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) + JsonReader.refRead[T](classRef) + + // inline def sj[T]: sj[T] = ${ sjImpl[T] } + + // def sjImpl[T](using q: Quotes, tt: Type[T]): Expr[sj[T]] = + // import q.reflect.* + // val classRef = ReflectOnType[T](quotes)(TypeRepr.of[T], true)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) + // JsonReader.refRead[T](classRef) + + // --------------------------------------------------------------------- inline def write[T](a: T)(using cfg: JsonConfig = JsonConfig()): String = ${ writeImpl[T]('a, 'cfg) } def writeImpl[T](aE: Expr[T], cfg: Expr[JsonConfig])(using q: Quotes, tt: Type[T]): Expr[String] = import q.reflect.* val ref = ReflectOnType(q)(TypeRepr.of[T], true)(using Map.empty[TypedName, Boolean]).asInstanceOf[RTypeRef[T]] - val sbE = '{ new StringBuilder() } val classesSeen = Map.empty[TypedName, RTypeRef[?]] - '{ ${ JsonWriter.refWrite[T](cfg, ref, aE, sbE)(using classesSeen) }.toString } + + val sbE = '{ new StringBuilder() } + '{ ${ JsonWriter.refWrite[T](cfg, ref, aE, sbE)(using classesSeen) }.result } // --------------------------------------------------------------------- @@ -25,16 +44,27 @@ object sj: // Shorter and "lighter" than "ScalaJack" everywhere. def readImpl[T: Type](js: Expr[String], cfg: Expr[JsonConfig])(using q: Quotes): Expr[Either[ParseError, T]] = import quotes.reflect.* - - try { - val classRef = ReflectOnType[T](quotes)(TypeRepr.of[T], true)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) - val decoder = JsonReader.refRead[T](classRef) - '{ - $decoder.decodeJson($js) - } - } catch { - case t: Throwable => - val error = Expr(t.getClass.getName()) - val msg = Expr(t.getMessage()) - '{ Left(ParseError($error + " was thrown with message " + $msg)) } + val classRef = ReflectOnType[T](quotes)(TypeRepr.of[T], true)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) + val decoder = JsonReader.refRead[T](classRef) + '{ + $decoder.decodeJson($js) } + + /* + + implicit val codec: sj[Record] = ScalaJack.inspect[Record] + + (optional given cfg) + + sj[Record].read(js) + sj[Record].readYml(yml) + sj[Record].readMP(msgPack) + + sj[Record].write(thing) + sj[Record].writeYml(thing) + sj[Record].writeMP(thing) + + + trait sj[A] extends JSModule with YMLModule with MPModule: + ... + */ diff --git a/src/main/scala/co.blocke.scalajack/json/ClassDecoder.scala b/src/main/scala/co.blocke.scalajack/json/ClassDecoder.scala index 32a6d635..dcb99c3c 100644 --- a/src/main/scala/co.blocke.scalajack/json/ClassDecoder.scala +++ b/src/main/scala/co.blocke.scalajack/json/ClassDecoder.scala @@ -4,13 +4,13 @@ package json import scala.annotation.* object ClassDecoder: - // def apply[A: scala.reflect.ClassTag](fields: Array[String], fieldDecoders: Array[JsonDecoder[_]], instantiator: Array[?] => A) = new JsonDecoder[A] { + // def apply[A: scala.reflect.ClassTag](fields: Array[String], fieldDecoders: Array[sj[_]], instantiator: Array[?] => A) = new sj[A] { def apply[A]( - fields: Array[String], - fieldDecoders: Array[JsonDecoder[_]], - instantiator: Array[?] => A, - fieldValues: Array[Any] - ) = new JsonDecoder[A] { + fields: Array[String], + fieldDecoders: Array[sj[_]], + instantiator: Array[?] => A, + fieldValues: Array[Any] + ) = new sj[A] { val fieldMatrix = new StringMatrix(fields) diff --git a/src/main/scala/co.blocke.scalajack/json/JsonDecoder.scalax b/src/main/scala/co.blocke.scalajack/json/JsonDecoder.scalax deleted file mode 100644 index a1cd7e26..00000000 --- a/src/main/scala/co.blocke.scalajack/json/JsonDecoder.scalax +++ /dev/null @@ -1,81 +0,0 @@ -package co.blocke.scalajack -package json - -import scala.annotation.* - -trait JsonDecoder[A]: - final def decodeJson(str: CharSequence): Either[JsonParseError, A] = - try Right(unsafeDecode(new JsonSource(str))) - catch { - case jpe: JsonParseError => Left(jpe) - } - - def unsafeDecode(in: JsonSource): A - -//------------------------------------------------------------ - -object JsonDecoder extends DecoderLowPriority1: - - def apply[A](implicit a: JsonDecoder[A]): JsonDecoder[A] = a - - // Primitive support... - implicit val string: JsonDecoder[String] = new JsonDecoder[String] { - def unsafeDecode(in: JsonSource): String = - JsonParser.parseString(in).toString - } - - implicit val boolean: JsonDecoder[Boolean] = new JsonDecoder[Boolean] { - def unsafeDecode(in: JsonSource): Boolean = - JsonParser.parseBoolean(in) - } - - implicit val int: JsonDecoder[Int] = number(JsonParser.parseInt, _.intValueExact()) - private def number[A]( - f: (JsonSource) => A, - fromBigDecimal: java.math.BigDecimal => A - ): JsonDecoder[A] = - new JsonDecoder[A] { - def unsafeDecode(in: JsonSource): A = - (in.readSkipWhitespace(): @switch) match { - case '"' => - val i = f(in) - JsonParser.char(in, '"') - i - case _ => - in.retract() - f(in) - } - } - - private[json] def builder[A, T[_]]( - in: JsonSource, - builder: scala.collection.mutable.Builder[A, T[A]] - )(implicit A: JsonDecoder[A]): T[A] = { - JsonParser.charWithWS(in, '[') - var i: Int = 0 - if JsonParser.firstArrayElement(in) then - while { - { - builder += A.unsafeDecode(in) - i += 1 - }; JsonParser.nextArrayElement(in) - } do () - builder.result() - } - -//------------------------------------------------------------ - -private trait DecoderLowPriority1: - this: JsonDecoder.type => - - // TODO: Experiment with other Seq *NOT* explicitly provided as separate "implicit def"s. See if this will - // convert them to the correct subtype. - implicit def seq[A: JsonDecoder]: JsonDecoder[Seq[A]] = new JsonDecoder[Seq[A]] { - def unsafeDecode(in: JsonSource): Seq[A] = - builder(in, scala.collection.immutable.Seq.newBuilder[A]) - } - - implicit def list[A: JsonDecoder]: JsonDecoder[List[A]] = new JsonDecoder[List[A]] { - def unsafeDecode(in: JsonSource): List[A] = - builder(in, new scala.collection.mutable.ListBuffer[A]) - } diff --git a/src/main/scala/co.blocke.scalajack/json/JsonReader.scala b/src/main/scala/co.blocke.scalajack/json/JsonReader.scala index 4bfb51a0..f408af1c 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonReader.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonReader.scala @@ -9,7 +9,7 @@ import scala.collection.mutable.HashMap import scala.util.{Failure, Success, Try} import scala.collection.Factory -/** We depart from ZIO-Json here. ZIO-Json uses implicits to marshal the right JsonDecoder. This works great for primitive types +/** We depart from ZIO-Json here. ZIO-Json uses implicits to marshal the right sj. This works great for primitive types * but I had issues trying to get it to work with macros. Since we have all the necessary elements to explicitly provide * decoders for "constructed" types (collections, classes, ...) we just provided them explicitly. */ @@ -17,21 +17,21 @@ object JsonReader: def refRead[T]( ref: RTypeRef[T] - )(using q: Quotes, tt: Type[T]): Expr[JsonDecoder[T]] = + )(using q: Quotes, tt: Type[T]): Expr[sj[T]] = import quotes.reflect.* ref match case r: PrimitiveRef[?] => - Expr.summon[JsonDecoder[T]].getOrElse(throw JsonTypeError("No JsonDecoder defined for type " + TypeRepr.of[T].typeSymbol.name)) + Expr.summon[sj[T]].getOrElse(throw JsonTypeError("No sj defined for type " + TypeRepr.of[T].typeSymbol.name)) case r: SeqRef[?] => r.refType match case '[t] => r.elementRef.refType match case '[e] => - val elemDecoder = Expr.summon[JsonDecoder[e]].getOrElse(refRead[e](r.elementRef.asInstanceOf[RTypeRef[e]])) + val elemDecoder = Expr.summon[sj[e]].getOrElse(refRead[e](r.elementRef.asInstanceOf[RTypeRef[e]])) '{ - JsonDecoder.seq[e]($elemDecoder) map (_.to(${ Expr.summon[Factory[e, T]].get })) + sj.seq[e]($elemDecoder) map (_.to(${ Expr.summon[Factory[e, T]].get })) } case r: ScalaClassRef[?] => @@ -40,38 +40,33 @@ object JsonReader: r.fields.map(f => f.fieldRef.refType match case '[e] => - Expr.summon[JsonDecoder[e]].getOrElse(refRead[e](f.fieldRef.asInstanceOf[RTypeRef[e]])) + Expr.summon[sj[e]].getOrElse(refRead[e](f.fieldRef.asInstanceOf[RTypeRef[e]])) ) ) val instantiator = JsonReaderUtil.classInstantiator[T](r.asInstanceOf[ClassRef[T]]) val optionalFields = Expr(r.fields.zipWithIndex.collect { case (f, i) if f.fieldRef.isInstanceOf[OptionRef[_]] => i }.toArray) val fieldsE = Expr.ofList(r.fields.asInstanceOf[List[ScalaFieldInfoRef]].map(_.expr)) - // TODO: See if we can't get the default values at compile-time. Mix with null/None for a predefined value array: - // Use the Select.... business to fire the default accessors and get an Expr[Any] value back, that we can unscrable in '{} + // Constructor argument list, preloaded with optional 'None' values and any default values specified + val preloaded = Expr + .ofList(r.fields.map { f => + val scalaF = f.asInstanceOf[ScalaFieldInfoRef] + if scalaF.defaultValueAccessorName.isDefined then + r.refType match + case '[t] => + val tpe = TypeRepr.of[t].widen + val sym = tpe.typeSymbol + val companionBody = sym.companionClass.tree.asInstanceOf[ClassDef].body + val companion = Ref(sym.companionModule) + companionBody + .collect { + case defaultMethod @ DefDef(name, _, _, _) if name.startsWith("$lessinit$greater$default$" + (f.index + 1)) => + companion.select(defaultMethod.symbol).appliedToTypes(tpe.typeArgs).asExpr + } + .headOption + .getOrElse(Expr(null.asInstanceOf[Boolean])) + else if scalaF.fieldRef.isInstanceOf[OptionRef[_]] then Expr(None) + else Expr(null.asInstanceOf[Int]) + }) - '{ - val preloadedFieldValues: Array[Any] = $fieldsE - .asInstanceOf[List[ScalaFieldInfo]] - .map(_ match - case optFld if optFld.fieldType.isInstanceOf[OptionRType[_]] => None - case defFld if defFld.defaultValueAccessorName.isDefined => - val (companion, accessor) = defFld.defaultValueAccessorName.get - // Have to use Java reflection here to get default value--Scala compiler won't have access to companion - // or accessor if we do a ${} block, and using compiler staging would murder performance. - { - val c = Class.forName(companion) - val cons = c.getDeclaredConstructor() - cons.setAccessible(true) - val m = c.getMethod(accessor) - m.setAccessible(true) - m.invoke(cons.newInstance()) - } - case _ => null - ) - .toArray - ClassDecoder[T]($fieldNames, $fieldDecoders.toArray, $instantiator, preloadedFieldValues) - } - - // We'd love to pass this in--preloaded with defaults/None values - // val fieldValues = new Array[Any](fields.length) + '{ ClassDecoder[T]($fieldNames, $fieldDecoders.toArray, $instantiator, $preloaded.toArray) } diff --git a/src/main/scala/co.blocke.scalajack/json/package.scala b/src/main/scala/co.blocke.scalajack/json/package.scala index 5fd5d8e1..8250d4a4 100644 --- a/src/main/scala/co.blocke.scalajack/json/package.scala +++ b/src/main/scala/co.blocke.scalajack/json/package.scala @@ -1,5 +1,19 @@ package co.blocke.scalajack package json +import scala.util.Failure + val BUFFER_EXCEEDED: Char = 7 // Old "BELL" ASCII value, used as a marker when we've run off the end of the known world val END_OF_STRING: Char = 3 + +inline def lastPart(n: String) = n.split('.').last.stripSuffix("$") + +// Tests whether we should write something or not--mainly in the case of Option, or wrapped Option +def isOkToWrite(a: Any, cfg: JsonConfig) = + a match + case None if !cfg.noneAsNull => false + case o: java.util.Optional[?] if o.isEmpty && !cfg.noneAsNull => false + case Left(None) if !cfg.noneAsNull => false + case Right(None) if !cfg.noneAsNull => false + case Failure(_) if cfg.tryFailureHandling == TryOption.NO_WRITE => false + case _ => true diff --git a/src/main/scala/co.blocke.scalajack/json/JsonDecoder.scala b/src/main/scala/co.blocke.scalajack/json/sj.scala similarity index 74% rename from src/main/scala/co.blocke.scalajack/json/JsonDecoder.scala rename to src/main/scala/co.blocke.scalajack/json/sj.scala index 4b37232a..b44d83e6 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonDecoder.scala +++ b/src/main/scala/co.blocke.scalajack/json/sj.scala @@ -3,7 +3,7 @@ package json import scala.annotation.* -trait JsonDecoder[A]: +trait sj[A]: self => final def decodeJson(str: CharSequence): Either[JsonParseError, A] = try Right(unsafeDecode(new JsonSource(str))) @@ -11,8 +11,8 @@ trait JsonDecoder[A]: case jpe: JsonParseError => Left(jpe) } - final def map[B](f: A => B): JsonDecoder[B] = - new JsonDecoder[B] { + final def map[B](f: A => B): sj[B] = + new sj[B] { def unsafeDecode(in: JsonSource): B = f(self.unsafeDecode(in)) } @@ -21,27 +21,27 @@ trait JsonDecoder[A]: //------------------------------------------------------------ -object JsonDecoder extends DecoderLowPriority1: +object sj extends DecoderLowPriority1: -// def apply[A](implicit a: JsonDecoder[A]): JsonDecoder[A] = a + def apply[A](implicit a: sj[A]): sj[A] = a // Primitive support... - implicit val string: JsonDecoder[String] = new JsonDecoder[String] { + implicit val string: sj[String] = new sj[String] { def unsafeDecode(in: JsonSource): String = JsonParser.parseString(in).toString } - implicit val boolean: JsonDecoder[Boolean] = new JsonDecoder[Boolean] { + implicit val boolean: sj[Boolean] = new sj[Boolean] { def unsafeDecode(in: JsonSource): Boolean = JsonParser.parseBoolean(in) } - implicit val int: JsonDecoder[Int] = number(JsonParser.parseInt, _.intValueExact()) + implicit val int: sj[Int] = number(JsonParser.parseInt, _.intValueExact()) private def number[A]( f: (JsonSource) => A, fromBigDecimal: java.math.BigDecimal => A - ): JsonDecoder[A] = - new JsonDecoder[A] { + ): sj[A] = + new sj[A] { def unsafeDecode(in: JsonSource): A = (in.readSkipWhitespace(): @switch) match { case '"' => @@ -56,7 +56,7 @@ object JsonDecoder extends DecoderLowPriority1: private[json] def builder[A, T[_]]( in: JsonSource, - elemDecoder: JsonDecoder[A], + elemDecoder: sj[A], builder: scala.collection.mutable.Builder[A, T[A]] ): T[A] = { JsonParser.charWithWS(in, '[') @@ -74,9 +74,9 @@ object JsonDecoder extends DecoderLowPriority1: //------------------------------------------------------------ private trait DecoderLowPriority1: - this: JsonDecoder.type => + this: sj.type => - def seq[A](elemDecoder: JsonDecoder[A]): JsonDecoder[Seq[A]] = new JsonDecoder[Seq[A]] { + def seq[A](elemDecoder: sj[A]): sj[Seq[A]] = new sj[Seq[A]] { def unsafeDecode(in: JsonSource): Seq[A] = builder(in, elemDecoder, scala.collection.immutable.Seq.newBuilder[A]) } diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index 5a424718..e648cd7e 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -3,6 +3,7 @@ package run import co.blocke.scala_reflection.* import scala.jdk.CollectionConverters.* +import scala.reflect.ClassTag object RunMe extends App: @@ -16,79 +17,22 @@ object RunMe extends App: import json.* - val js = """[{ - "street": "123 Main Street", - "city": "Anytown", - "state": "CA", - "postal_code": "12345" - }]""" - println(ScalaJack.read[Record](jsData)) + // val thing = Foo("Greg", 57) - /* StringMatrix experiments.... - */ + val jjs = """{"name":"Greg","age":57}""" + // println(ScalaJack.read[Foo](jjs)) - /* - val matrix = new StringMatrix(List("foo", "bar", "baz").toArray, Array(("boom", 0))) - val r = JsonReader("boom\" asdf ") - var i: Int = 0 - var bs: Long = matrix.initial - var c: Int = -1 - while { c = r.readEscapedString(); c != END_OF_STRING } do { - bs = matrix.update(bs, i, c) - i += 1 - } - bs = matrix.exact(bs, i) - println("HERE: " + matrix.first(bs)) - */ + val record = ScalaJack.read[Record](jsData) match + case Right(r) => r + case Left(t) => throw t - // val numList = """[["1","2,3"],["4","5"]]""" - // val dec = JsonDecoder[List[List[String]]] - // println(dec.decodeJson(numList)) + println(record) - /* - implicit val addrDecoder: JsonDecoder[Address] = ClassDecoder( - Array("street", "city", "state", "postal_code"), - Array(JsonDecoder[String], JsonDecoder[String], JsonDecoder[String], JsonDecoder[String]) - ) - implicit val friendDecoder: JsonDecoder[Friend] = ClassDecoder( - Array("name", "age", "email"), - Array(JsonDecoder[String], JsonDecoder[Int], JsonDecoder[String]) - ) - implicit val petDecoder: JsonDecoder[Pet] = ClassDecoder( - Array("name", "species", "age"), - Array(JsonDecoder[String], JsonDecoder[String], JsonDecoder[Int]) - ) - implicit val PersonDecoder: JsonDecoder[Person] = ClassDecoder( - Array("name", "age", "address", "email", "phone_numbers", "is_employed"), - Array(JsonDecoder[String], JsonDecoder[Int], JsonDecoder[Address], JsonDecoder[String], JsonDecoder[List[String]], JsonDecoder[Boolean]) - ) - implicit val RecordDecoder: JsonDecoder[Record] = ClassDecoder( - Array("person", "hobbies", "friends", "pets"), - Array(JsonDecoder[Person], JsonDecoder[List[String]], JsonDecoder[List[Friend]], JsonDecoder[List[Pet]]) - ) + val r2 = ScalaJack.read[Record](jsData) + println(ScalaJack.write(record)) - val addr = """{"street":"319 Hampton Ct","city":"Coppell","state":"TX","postal_code":"75019"}""" - val dec2 = JsonDecoder[Address] - println(JsonDecoder[Record].decodeJson(jsData)) - */ - - /* - import parser.* - import json.* - - // val inst = SeqInstruction[Int, List[Int]](IntInstruction()) - // val inst2 = SeqInstruction[List[Int], Set[Seq[Int]]](inst) - val js = """{"hey":"wowowow","age":57,"other":[1,2,3]}""" - // 0123456789012345678901234567890123456789012234567890 - - println(ScalaJack.read[Record](jsData)) - - val jp = json.JsonParser3(jsData) - println(">>> " + jp.parse()) - - // val p = JsonParser2(js, JsonConfig()) - // println("Result: " + p.parse(inst2)) - */ + implicit val z: json.sj[Record] = ScalaJack.inspect[Record] + println(sj[Record].decodeJson(jsData)) catch { case t: Throwable => diff --git a/src/main/scala/co.blocke.scalajack/run/Record.scala b/src/main/scala/co.blocke.scalajack/run/Record.scala index aa726dd1..426001db 100644 --- a/src/main/scala/co.blocke.scalajack/run/Record.scala +++ b/src/main/scala/co.blocke.scalajack/run/Record.scala @@ -36,7 +36,7 @@ case class Record( pets: List[Pet] ) -case class Foo(name: String, age: Int) +case class Foo(name: String, maybe: Option[Int], age: Int, expected: String = "nada", gotit: Option[Int] = Some(5)) val jsData = """{ From f016750c888f49626c9c5d440cfc329703bd1450 Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Sat, 18 Nov 2023 23:19:20 -0600 Subject: [PATCH 26/65] performance jump on writes --- benchmark/README.md | 3 +- benchmark/build.sbt | 2 +- .../src/main/scala/co.blocke/Benchmark.scala | 4 +- .../src/main/scala/co.blocke/ScalaJack.scala | 14 +- .../scala/co.blocke.scalajack/ScalaJack.scala | 44 ++++-- .../internal/CodePrinter.scala | 25 ++++ .../co.blocke.scalajack/json/JsonCodec.scalax | 13 ++ .../co.blocke.scalajack/json/JsonError.scala | 2 +- .../json/{ => reading}/ClassDecoder.scala | 7 +- .../{ => reading}/FastStringBuilder.scala | 1 + .../{sj.scala => reading/JsonDecoder.scala} | 27 ++-- .../json/{ => reading}/JsonParser.scala | 1 + .../json/{ => reading}/JsonReader.scala | 19 +-- .../json/{ => reading}/JsonReaderUtil.scala | 1 + .../json/{ => reading}/JsonSource.scala | 1 + .../json/{ => reading}/Numbers.scala | 2 + .../json/{ => reading}/StringMatrix.scala | 1 + .../co.blocke.scalajack/json/writing/JW.scala | 135 ++++++++++++++++++ .../json/{ => writing}/JsonWriter.scala | 1 + .../json/{ => writing}/JsonWriterRT.scala | 0 .../scala/co.blocke.scalajack/run/Play.scala | 30 ++-- .../co.blocke.scalajack/run/Record.scala | 10 +- 22 files changed, 285 insertions(+), 58 deletions(-) create mode 100644 src/main/scala/co.blocke.scalajack/internal/CodePrinter.scala create mode 100644 src/main/scala/co.blocke.scalajack/json/JsonCodec.scalax rename src/main/scala/co.blocke.scalajack/json/{ => reading}/ClassDecoder.scala (83%) rename src/main/scala/co.blocke.scalajack/json/{ => reading}/FastStringBuilder.scala (97%) rename src/main/scala/co.blocke.scalajack/json/{sj.scala => reading/JsonDecoder.scala} (74%) rename src/main/scala/co.blocke.scalajack/json/{ => reading}/JsonParser.scala (99%) rename src/main/scala/co.blocke.scalajack/json/{ => reading}/JsonReader.scala (72%) rename src/main/scala/co.blocke.scalajack/json/{ => reading}/JsonReaderUtil.scala (99%) rename src/main/scala/co.blocke.scalajack/json/{ => reading}/JsonSource.scala (99%) rename src/main/scala/co.blocke.scalajack/json/{ => reading}/Numbers.scala (99%) rename src/main/scala/co.blocke.scalajack/json/{ => reading}/StringMatrix.scala (99%) create mode 100644 src/main/scala/co.blocke.scalajack/json/writing/JW.scala rename src/main/scala/co.blocke.scalajack/json/{ => writing}/JsonWriter.scala (99%) rename src/main/scala/co.blocke.scalajack/json/{ => writing}/JsonWriterRT.scala (100%) diff --git a/benchmark/README.md b/benchmark/README.md index f7d9ffac..906ae883 100644 --- a/benchmark/README.md +++ b/benchmark/README.md @@ -32,8 +32,9 @@ sbt "jmh:run -i 10 -wi 10 -f 2 -t 1 co.blocke.*" | Jsoniter | thrpt | 20 | 2843150.452 | ± 21478.503 | ops/s | | Hand-Tooled | thrpt | 20 | 2732571.374 | ± 15129.007 | ops/s | | Circe | thrpt | 20 | 1958244.437 | ± 23965.817 | ops/s | -|**ScalaJack 8** | thrpt | 20 | **1729426.328** | ± 4484.721 | ops/s | +|**ScalaJack 8 (fast mode)** | thrpt | 20 | **1729426.328** | ± 4484.721 | ops/s | | ZIO JSON | thrpt | 20 | 794352.301 | ± 32336.852 | ops/s | +|**ScalaJack 8 (easy mode)** | thrpt | 20 | **705318.598** | ± 19932.389 | ops/s | | Argonaut | thrpt | 20 | 690269.697 | ± 6348.882 | ops/s | | Play JSON | thrpt | 20 | 438650.022 | ± 23800.221 | ops/s | diff --git a/benchmark/build.sbt b/benchmark/build.sbt index 41de8e2e..a93ec4cb 100644 --- a/benchmark/build.sbt +++ b/benchmark/build.sbt @@ -36,7 +36,7 @@ lazy val benchmark = project libraryDependencies ++= Seq( "org.playframework" %% "play-json" % "3.0.1", "io.argonaut" %% "argonaut" % "6.3.9", - "co.blocke" %% "scalajack" % "caaa5e_unknown", // New-New + "co.blocke" %% "scalajack" % "4a7b71_unknown", // New-New "co.blocke" %% "scala-reflection" % "sj_fixes_edbef8", "dev.zio" %% "zio-json" % "0.6.1", "org.typelevel" %% "fabric-core" % "1.12.6", diff --git a/benchmark/src/main/scala/co.blocke/Benchmark.scala b/benchmark/src/main/scala/co.blocke/Benchmark.scala index 670d7c0e..045faa89 100644 --- a/benchmark/src/main/scala/co.blocke/Benchmark.scala +++ b/benchmark/src/main/scala/co.blocke/Benchmark.scala @@ -55,9 +55,9 @@ class ReadingBenchmark @OutputTimeUnit(TimeUnit.SECONDS) class WritingBenchmark // extends HandTooledWritingBenchmark - // with CirceZ.CirceWritingBenchmark + // extends CirceZ.CirceWritingBenchmark extends ScalaJackZ.ScalaJackWritingBenchmark - // with JsoniterZ.JsoniterWritingBenchmark + // extends JsoniterZ.JsoniterWritingBenchmark // with ZIOZ.ZIOJsonWritingBenchmark // with PlayZ.PlayWritingBenchmark // with ArgonautZ.ArgonautWritingBenchmark diff --git a/benchmark/src/main/scala/co.blocke/ScalaJack.scala b/benchmark/src/main/scala/co.blocke/ScalaJack.scala index c019649d..84faf749 100644 --- a/benchmark/src/main/scala/co.blocke/ScalaJack.scala +++ b/benchmark/src/main/scala/co.blocke/ScalaJack.scala @@ -3,19 +3,19 @@ package co.blocke import org.openjdk.jmh.annotations._ object ScalaJackZ: - import co.blocke.scalajack.* - - import json.* + import co.blocke.scalajack.ScalaJack.* + import co.blocke.scalajack.* - implicit val blah: sj[Record] = ScalaJack.inspect[Record] + implicit val blah: ScalaJack[Record] = sj[Record] trait ScalaJackReadingBenchmark{ @Benchmark - // def readRecordScalaJack = ScalaJack.read[Record](jsData) - def readRecordScalaJack = sj[Record].decodeJson(jsData) + // def readRecordScalaJack = sj[Record].fromJson(jsData) + def readRecordScalaJack = ScalaJack[Record].fromJson(jsData) } trait ScalaJackWritingBenchmark { @Benchmark - def writeRecordScalaJack = ScalaJack.write(record) + // def writeRecordScalaJack = sj[Record].toJson(record) // 677K score + def writeRecordScalaJack = ScalaJack[Record].toJson(record) // 1.7M score <- faster } diff --git a/src/main/scala/co.blocke.scalajack/ScalaJack.scala b/src/main/scala/co.blocke.scalajack/ScalaJack.scala index badd1970..3a2b3382 100644 --- a/src/main/scala/co.blocke.scalajack/ScalaJack.scala +++ b/src/main/scala/co.blocke.scalajack/ScalaJack.scala @@ -2,32 +2,49 @@ package co.blocke.scalajack import co.blocke.scala_reflection.{RTypeRef, TypedName} import co.blocke.scala_reflection.reflect.ReflectOnType -import co.blocke.scala_reflection.reflect.rtypeRefs.ClassRef -import scala.collection.mutable.{HashMap, Map} import scala.quoted.* -import scala.reflect.ClassTag import quoted.Quotes import json.* +case class ScalaJack[T](jsonDecoder: reading.JsonDecoder[T], jsonEncoder: (T, StringBuilder, JsonConfig) => String): // extends JsonCodec[T] //with YamlCodec with MsgPackCodec + def fromJson(js: String)(using cfg: JsonConfig = JsonConfig()): Either[JsonParseError, T] = + jsonDecoder.decodeJson(js) + + def toJson(a: T)(using cfg: JsonConfig = JsonConfig()): String = + jsonEncoder(a, new StringBuilder(), cfg) + +// --------------------------------------- + object ScalaJack: - inline def inspect[T]: sj[T] = ${ inspectImpl[T] } + def apply[A](implicit a: ScalaJack[A]): ScalaJack[A] = a - def inspectImpl[T](using q: Quotes, tt: Type[T]): Expr[sj[T]] = + inline def sj[T]: ScalaJack[T] = ${ sjImpl[T] } + + def sjImpl[T](using q: Quotes, tt: Type[T]): Expr[ScalaJack[T]] = import q.reflect.* val classRef = ReflectOnType[T](quotes)(TypeRepr.of[T], true)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) - JsonReader.refRead[T](classRef) + val jsonDecoder = reading.JsonReader.refRead(classRef) + val jsonEncoder = writing.JW.refRead(classRef) + + '{ ScalaJack($jsonDecoder, $jsonEncoder) } - // inline def sj[T]: sj[T] = ${ sjImpl[T] } + // refRead[T](classRef) - // def sjImpl[T](using q: Quotes, tt: Type[T]): Expr[sj[T]] = - // import q.reflect.* - // val classRef = ReflectOnType[T](quotes)(TypeRepr.of[T], true)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) - // JsonReader.refRead[T](classRef) + // private def refRead[T](ref: RTypeRef[T])(using Quotes): Expr[ScalaJack[T]] = ??? +/* // --------------------------------------------------------------------- + + inline def inspect[T]: sj[T] = ${ inspectImpl[T] } + + def inspectImpl[T](using q: Quotes, tt: Type[T]): Expr[sj[T]] = + import q.reflect.* + val classRef = ReflectOnType[T](quotes)(TypeRepr.of[T], true)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) + JsonReader.refRead[T](classRef) + inline def write[T](a: T)(using cfg: JsonConfig = JsonConfig()): String = ${ writeImpl[T]('a, 'cfg) } def writeImpl[T](aE: Expr[T], cfg: Expr[JsonConfig])(using q: Quotes, tt: Type[T]): Expr[String] = @@ -49,8 +66,9 @@ object ScalaJack: '{ $decoder.decodeJson($js) } + */ - /* +/* implicit val codec: sj[Record] = ScalaJack.inspect[Record] @@ -67,4 +85,4 @@ object ScalaJack: trait sj[A] extends JSModule with YMLModule with MPModule: ... - */ + */ diff --git a/src/main/scala/co.blocke.scalajack/internal/CodePrinter.scala b/src/main/scala/co.blocke.scalajack/internal/CodePrinter.scala new file mode 100644 index 00000000..4d2d8549 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/internal/CodePrinter.scala @@ -0,0 +1,25 @@ +package co.blocke.scalajack +package internal + +import scala.quoted.* + +object CodePrinter { + inline def structure[A](inline value: A) = ${ structureMacro('value) } + + private def structureMacro[A: Type](value: Expr[A])(using Quotes): Expr[A] = { + import quotes.reflect.* + + val struct = Printer.TreeStructure.show(value.asTerm) + report.info(struct) + value.asTerm.changeOwner(Symbol.spliceOwner).asExprOf[A] + } + + inline def code[A](inline value: A): A = ${ codeMacro('value) } + + private def codeMacro[A: Type](value: Expr[A])(using Quotes): Expr[A] = { + import quotes.reflect.* + val struct = Printer.TreeShortCode.show(value.asTerm) + report.info(struct) + value + } +} \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json/JsonCodec.scalax b/src/main/scala/co.blocke.scalajack/json/JsonCodec.scalax new file mode 100644 index 00000000..cb15290b --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/JsonCodec.scalax @@ -0,0 +1,13 @@ +package co.blocke.scalajack +package json + +class JsonCodec[T]: + parent: ScalaJack[T] => + + inline def fromJson( js: String )(using cfg: JsonConfig = JsonConfig()): Either[JsonParseError, T] = + parent.jsonDecoder.decodeJson(js) + + inline def toJson( a: T )(using cfg: JsonConfig = JsonConfig()): String = + parent.jsonEncoder.encode(a, new StringBuilder(), cfg) + + \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json/JsonError.scala b/src/main/scala/co.blocke.scalajack/json/JsonError.scala index 22c481e0..b5f127cf 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonError.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonError.scala @@ -11,7 +11,7 @@ case class JsonTypeError(override val msg: String) extends ParseError(msg): override val show: String = "" // Thrown at runtime only! -case class JsonParseError(override val msg: String, context: JsonSource) extends ParseError(msg + " at position " + context.pos): +case class JsonParseError(override val msg: String, context: reading.JsonSource) extends ParseError(msg + " at position " + context.pos): override val show: String = val js = context.js.toString val (clip, dashes) = context.pos match { diff --git a/src/main/scala/co.blocke.scalajack/json/ClassDecoder.scala b/src/main/scala/co.blocke.scalajack/json/reading/ClassDecoder.scala similarity index 83% rename from src/main/scala/co.blocke.scalajack/json/ClassDecoder.scala rename to src/main/scala/co.blocke.scalajack/json/reading/ClassDecoder.scala index dcb99c3c..ff12cf56 100644 --- a/src/main/scala/co.blocke.scalajack/json/ClassDecoder.scala +++ b/src/main/scala/co.blocke.scalajack/json/reading/ClassDecoder.scala @@ -1,16 +1,17 @@ package co.blocke.scalajack package json +package reading import scala.annotation.* object ClassDecoder: - // def apply[A: scala.reflect.ClassTag](fields: Array[String], fieldDecoders: Array[sj[_]], instantiator: Array[?] => A) = new sj[A] { + // def apply[A: scala.reflect.ClassTag](fields: Array[String], fieldDecoders: Array[JsonDecoder[_]], instantiator: Array[?] => A) = new JsonDecoder[A] { def apply[A]( fields: Array[String], - fieldDecoders: Array[sj[_]], + fieldDecoders: Array[JsonDecoder[_]], instantiator: Array[?] => A, fieldValues: Array[Any] - ) = new sj[A] { + ) = new JsonDecoder[A] { val fieldMatrix = new StringMatrix(fields) diff --git a/src/main/scala/co.blocke.scalajack/json/FastStringBuilder.scala b/src/main/scala/co.blocke.scalajack/json/reading/FastStringBuilder.scala similarity index 97% rename from src/main/scala/co.blocke.scalajack/json/FastStringBuilder.scala rename to src/main/scala/co.blocke.scalajack/json/reading/FastStringBuilder.scala index f027bf1f..b7c67f25 100644 --- a/src/main/scala/co.blocke.scalajack/json/FastStringBuilder.scala +++ b/src/main/scala/co.blocke.scalajack/json/reading/FastStringBuilder.scala @@ -1,5 +1,6 @@ package co.blocke.scalajack package json +package reading import java.nio.CharBuffer import java.util.Arrays diff --git a/src/main/scala/co.blocke.scalajack/json/sj.scala b/src/main/scala/co.blocke.scalajack/json/reading/JsonDecoder.scala similarity index 74% rename from src/main/scala/co.blocke.scalajack/json/sj.scala rename to src/main/scala/co.blocke.scalajack/json/reading/JsonDecoder.scala index b44d83e6..a6496204 100644 --- a/src/main/scala/co.blocke.scalajack/json/sj.scala +++ b/src/main/scala/co.blocke.scalajack/json/reading/JsonDecoder.scala @@ -1,9 +1,10 @@ package co.blocke.scalajack package json +package reading import scala.annotation.* -trait sj[A]: +trait JsonDecoder[A]: self => final def decodeJson(str: CharSequence): Either[JsonParseError, A] = try Right(unsafeDecode(new JsonSource(str))) @@ -11,8 +12,8 @@ trait sj[A]: case jpe: JsonParseError => Left(jpe) } - final def map[B](f: A => B): sj[B] = - new sj[B] { + final def map[B](f: A => B): JsonDecoder[B] = + new JsonDecoder[B] { def unsafeDecode(in: JsonSource): B = f(self.unsafeDecode(in)) } @@ -21,27 +22,27 @@ trait sj[A]: //------------------------------------------------------------ -object sj extends DecoderLowPriority1: +object JsonDecoder extends DecoderLowPriority1: - def apply[A](implicit a: sj[A]): sj[A] = a + // def apply[A](implicit a: JsonDecoder[A]): JsonDecoder[A] = a // Primitive support... - implicit val string: sj[String] = new sj[String] { + implicit val string: JsonDecoder[String] = new JsonDecoder[String] { def unsafeDecode(in: JsonSource): String = JsonParser.parseString(in).toString } - implicit val boolean: sj[Boolean] = new sj[Boolean] { + implicit val boolean: JsonDecoder[Boolean] = new JsonDecoder[Boolean] { def unsafeDecode(in: JsonSource): Boolean = JsonParser.parseBoolean(in) } - implicit val int: sj[Int] = number(JsonParser.parseInt, _.intValueExact()) + implicit val int: JsonDecoder[Int] = number(JsonParser.parseInt, _.intValueExact()) private def number[A]( f: (JsonSource) => A, fromBigDecimal: java.math.BigDecimal => A - ): sj[A] = - new sj[A] { + ): JsonDecoder[A] = + new JsonDecoder[A] { def unsafeDecode(in: JsonSource): A = (in.readSkipWhitespace(): @switch) match { case '"' => @@ -56,7 +57,7 @@ object sj extends DecoderLowPriority1: private[json] def builder[A, T[_]]( in: JsonSource, - elemDecoder: sj[A], + elemDecoder: JsonDecoder[A], builder: scala.collection.mutable.Builder[A, T[A]] ): T[A] = { JsonParser.charWithWS(in, '[') @@ -74,9 +75,9 @@ object sj extends DecoderLowPriority1: //------------------------------------------------------------ private trait DecoderLowPriority1: - this: sj.type => + this: JsonDecoder.type => - def seq[A](elemDecoder: sj[A]): sj[Seq[A]] = new sj[Seq[A]] { + def seq[A](elemDecoder: JsonDecoder[A]): JsonDecoder[Seq[A]] = new JsonDecoder[Seq[A]] { def unsafeDecode(in: JsonSource): Seq[A] = builder(in, elemDecoder, scala.collection.immutable.Seq.newBuilder[A]) } diff --git a/src/main/scala/co.blocke.scalajack/json/JsonParser.scala b/src/main/scala/co.blocke.scalajack/json/reading/JsonParser.scala similarity index 99% rename from src/main/scala/co.blocke.scalajack/json/JsonParser.scala rename to src/main/scala/co.blocke.scalajack/json/reading/JsonParser.scala index 2c8a62f2..9768e74b 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonParser.scala +++ b/src/main/scala/co.blocke.scalajack/json/reading/JsonParser.scala @@ -1,5 +1,6 @@ package co.blocke.scalajack package json +package reading import scala.annotation.* diff --git a/src/main/scala/co.blocke.scalajack/json/JsonReader.scala b/src/main/scala/co.blocke.scalajack/json/reading/JsonReader.scala similarity index 72% rename from src/main/scala/co.blocke.scalajack/json/JsonReader.scala rename to src/main/scala/co.blocke.scalajack/json/reading/JsonReader.scala index f408af1c..7524bb13 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonReader.scala +++ b/src/main/scala/co.blocke.scalajack/json/reading/JsonReader.scala @@ -1,5 +1,6 @@ package co.blocke.scalajack package json +package reading import co.blocke.scala_reflection.{RTypeRef, TypedName} import co.blocke.scala_reflection.rtypes.{OptionRType, ScalaFieldInfo} @@ -9,29 +10,31 @@ import scala.collection.mutable.HashMap import scala.util.{Failure, Success, Try} import scala.collection.Factory -/** We depart from ZIO-Json here. ZIO-Json uses implicits to marshal the right sj. This works great for primitive types - * but I had issues trying to get it to work with macros. Since we have all the necessary elements to explicitly provide - * decoders for "constructed" types (collections, classes, ...) we just provided them explicitly. +/** We depart from ZIO-Json here. ZIO-Json uses implicits to marshal the right JsonDecoder. This works great for primitive types + * but I had issues trying to get it to work with macros for more complex types. Since we actually have all the necessary elements + * to explicitly provide decoders for "constructed" types (collections, classes, ...) we just provided them explicitly. + * + * The job of JsonReader is to accept an RTypeRef[T] and produce a JsonDecoder[T] for type T. */ object JsonReader: def refRead[T]( ref: RTypeRef[T] - )(using q: Quotes, tt: Type[T]): Expr[sj[T]] = + )(using q: Quotes, tt: Type[T]): Expr[JsonDecoder[T]] = import quotes.reflect.* ref match case r: PrimitiveRef[?] => - Expr.summon[sj[T]].getOrElse(throw JsonTypeError("No sj defined for type " + TypeRepr.of[T].typeSymbol.name)) + Expr.summon[JsonDecoder[T]].getOrElse(throw JsonTypeError("No JsonDecoder defined for type " + TypeRepr.of[T].typeSymbol.name)) case r: SeqRef[?] => r.refType match case '[t] => r.elementRef.refType match case '[e] => - val elemDecoder = Expr.summon[sj[e]].getOrElse(refRead[e](r.elementRef.asInstanceOf[RTypeRef[e]])) + val elemDecoder = Expr.summon[JsonDecoder[e]].getOrElse(refRead[e](r.elementRef.asInstanceOf[RTypeRef[e]])) '{ - sj.seq[e]($elemDecoder) map (_.to(${ Expr.summon[Factory[e, T]].get })) + JsonDecoder.seq[e]($elemDecoder) map (_.to(${ Expr.summon[Factory[e, T]].get })) } case r: ScalaClassRef[?] => @@ -40,7 +43,7 @@ object JsonReader: r.fields.map(f => f.fieldRef.refType match case '[e] => - Expr.summon[sj[e]].getOrElse(refRead[e](f.fieldRef.asInstanceOf[RTypeRef[e]])) + Expr.summon[JsonDecoder[e]].getOrElse(refRead[e](f.fieldRef.asInstanceOf[RTypeRef[e]])) ) ) val instantiator = JsonReaderUtil.classInstantiator[T](r.asInstanceOf[ClassRef[T]]) diff --git a/src/main/scala/co.blocke.scalajack/json/JsonReaderUtil.scala b/src/main/scala/co.blocke.scalajack/json/reading/JsonReaderUtil.scala similarity index 99% rename from src/main/scala/co.blocke.scalajack/json/JsonReaderUtil.scala rename to src/main/scala/co.blocke.scalajack/json/reading/JsonReaderUtil.scala index f28ad84b..42979c52 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonReaderUtil.scala +++ b/src/main/scala/co.blocke.scalajack/json/reading/JsonReaderUtil.scala @@ -1,5 +1,6 @@ package co.blocke.scalajack package json +package reading import scala.quoted.* import co.blocke.scala_reflection.reflect.rtypeRefs.* diff --git a/src/main/scala/co.blocke.scalajack/json/JsonSource.scala b/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala similarity index 99% rename from src/main/scala/co.blocke.scalajack/json/JsonSource.scala rename to src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala index 547bbf26..fb90eac4 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonSource.scala +++ b/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala @@ -1,5 +1,6 @@ package co.blocke.scalajack package json +package reading import scala.annotation.* diff --git a/src/main/scala/co.blocke.scalajack/json/Numbers.scala b/src/main/scala/co.blocke.scalajack/json/reading/Numbers.scala similarity index 99% rename from src/main/scala/co.blocke.scalajack/json/Numbers.scala rename to src/main/scala/co.blocke.scalajack/json/reading/Numbers.scala index 99b1845a..f21bd047 100644 --- a/src/main/scala/co.blocke.scalajack/json/Numbers.scala +++ b/src/main/scala/co.blocke.scalajack/json/reading/Numbers.scala @@ -15,6 +15,8 @@ */ package co.blocke.scalajack package json +package reading + import java.io.* import scala.util.control.NoStackTrace diff --git a/src/main/scala/co.blocke.scalajack/json/StringMatrix.scala b/src/main/scala/co.blocke.scalajack/json/reading/StringMatrix.scala similarity index 99% rename from src/main/scala/co.blocke.scalajack/json/StringMatrix.scala rename to src/main/scala/co.blocke.scalajack/json/reading/StringMatrix.scala index ffbb799f..aaf7219a 100644 --- a/src/main/scala/co.blocke.scalajack/json/StringMatrix.scala +++ b/src/main/scala/co.blocke.scalajack/json/reading/StringMatrix.scala @@ -1,5 +1,6 @@ package co.blocke.scalajack package json +package reading // A data structure encoding a simple algorithm for Trie pruning: Given a list // of strings, and a sequence of incoming characters, find the strings that diff --git a/src/main/scala/co.blocke.scalajack/json/writing/JW.scala b/src/main/scala/co.blocke.scalajack/json/writing/JW.scala new file mode 100644 index 00000000..5b496560 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/writing/JW.scala @@ -0,0 +1,135 @@ +package co.blocke.scalajack +package json +package writing + +import co.blocke.scala_reflection.{RTypeRef, TypedName} +import co.blocke.scala_reflection.reflect.rtypeRefs.* +import scala.quoted.* +import scala.collection.mutable.{Map=>MMap} + +object JW: + + private def shouldTestForOkToWrite( r: RTypeRef[_] ): Boolean = + r match + case _: OptionRef[?] => true + case _: LeftRightRef[?] => true + case _: TryRef[?] => true + case _ => false + + def refRead[T]( + ref: RTypeRef[T] + )(using q: Quotes, tt: Type[T]): Expr[(T, StringBuilder, JsonConfig)=>String] = + import quotes.reflect.* + '{ + (a: T, sb: StringBuilder, cfg: JsonConfig) => ${refWrite(ref, '{a}, '{sb}, '{cfg})(using MMap.empty[TypedName,RTypeRef[?]])}.toString + } + + private def refWrite[T]( + ref: RTypeRef[T], + aE: Expr[T], + sbE: Expr[StringBuilder], + cfgE: Expr[JsonConfig], + isMapKey: Boolean = false + )(using classesSeen: MMap[TypedName, RTypeRef[?]])(using q: Quotes, tt: Type[T]) : Expr[StringBuilder] = + import quotes.reflect.* + + ref match + case t: PrimitiveRef[?] if t.family == PrimFamily.Stringish => + '{ if $aE == null then $sbE.append("null") else $sbE.append(s"\"${$aE}\"") } + + case t: PrimitiveRef[?] => + val isNullable = Expr(t.isNullable) + if isMapKey then + '{ + if $isNullable && $aE == null then $sbE.append("\"null\"") + else $sbE.append(s"\"${$aE.toString}\"") + } + else '{ if $isNullable && $aE == null then $sbE.append("null") else $sbE.append($aE.toString) } + + case t: SeqRef[?] => + if isMapKey then throw new JsonError("Seq instances cannot be map keys") + t.elementRef.refType match + case '[e] => + val bE = aE.asInstanceOf[Expr[Seq[e]]] // pull type-cast into complie-time for performance gains + '{ + if $aE == null then $sbE.append("null") + else + $sbE.append('[') + val sbLen = $sbE.length() + + $bE.foreach { one => + ${ + if shouldTestForOkToWrite(t.elementRef) then // A lot of drama to avoid 1 'if' stmt--it matters for max performance + '{ + if isOkToWrite(one, $cfgE) then + ${ refWrite[e](t.elementRef.asInstanceOf[RTypeRef[e]], '{ one }, sbE, cfgE) } + $sbE.append(',') + } + else + '{ + ${ refWrite[e](t.elementRef.asInstanceOf[RTypeRef[e]], '{ one }, sbE, cfgE) } + $sbE.append(',') + } + } + } + if sbLen == $sbE.length() then $sbE.append(']') + else $sbE.setCharAt($sbE.length() - 1, ']') + } + + case t: ScalaClassRef[?] => + classesSeen.put(t.typedName, t) + val isCase = Expr(t.isCaseClass) + '{ + if $aE == null then $sbE.append("null") + else + $sbE.append('{') + val sbLen = $sbE.length() + ${ + Expr.ofList(t.fields.map{ f => + f.fieldRef.refType match + case '[e] => + val fieldValue = Select.unique(aE.asTerm, f.name).asExprOf[e] + val name = Expr(f.name) + if shouldTestForOkToWrite(f.fieldRef) then // A lot of drama to avoid 1 'if' stmt--it matters for max performance + '{ + if isOkToWrite($fieldValue, $cfgE) then + $sbE.append(s""""${$name}":""") + ${ refWrite[e](f.fieldRef.asInstanceOf[RTypeRef[e]], fieldValue, sbE, cfgE) } + $sbE.append(',') + else $sbE + } + else + '{ + $sbE.append(s""""${$name}":""") + ${ refWrite[e](f.fieldRef.asInstanceOf[RTypeRef[e]], fieldValue, sbE, cfgE) } + $sbE.append(',') + } + }) + } + // write out any non-constructor fields (non-case "plain" classes) + if ! $isCase && $cfgE.writeNonConstructorFields then + ${ + Expr.ofList(t.nonConstructorFields.map{ f => + f.fieldRef.refType match + case '[e] => + val fieldValue = Select.unique(aE.asTerm, f.getterLabel).asExprOf[e] + val name = Expr(f.name) + if shouldTestForOkToWrite(f.fieldRef) then // A lot of drama to avoid 1 'if' stmt--it matters for max performance + '{ + if isOkToWrite($fieldValue, $cfgE) then + $sbE.append(s"\"${$name}\":") + ${ refWrite[e](f.fieldRef.asInstanceOf[RTypeRef[e]], fieldValue, sbE, cfgE) } + $sbE.append(',') + else $sbE + } + else + '{ + $sbE.append(s"\"${$name}\":") + ${ refWrite[e](f.fieldRef.asInstanceOf[RTypeRef[e]], fieldValue, sbE, cfgE) } + $sbE.append(',') + } + }) + } + if sbLen == $sbE.length() then $sbE.append('}') + else $sbE.setCharAt($sbE.length() - 1, '}') + } \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala b/src/main/scala/co.blocke.scalajack/json/writing/JsonWriter.scala similarity index 99% rename from src/main/scala/co.blocke.scalajack/json/JsonWriter.scala rename to src/main/scala/co.blocke.scalajack/json/writing/JsonWriter.scala index 869240c1..f2091b7b 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonWriter.scala +++ b/src/main/scala/co.blocke.scalajack/json/writing/JsonWriter.scala @@ -48,6 +48,7 @@ object JsonWriter: final inline def lastPart(n: String) = n.split('.').last.stripSuffix("$") // Tests whether we should write something or not--mainly in the case of Option, or wrapped Option + // Affected types: Option, java.util.Optional, Left/Right, Try/Failure def isOkToWrite(a: Any, cfg: JsonConfig) = a match case None if !cfg.noneAsNull => false diff --git a/src/main/scala/co.blocke.scalajack/json/JsonWriterRT.scala b/src/main/scala/co.blocke.scalajack/json/writing/JsonWriterRT.scala similarity index 100% rename from src/main/scala/co.blocke.scalajack/json/JsonWriterRT.scala rename to src/main/scala/co.blocke.scalajack/json/writing/JsonWriterRT.scala diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index e648cd7e..6dce9896 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -16,23 +16,37 @@ object RunMe extends App: try import json.* + import ScalaJack.* + + internal.CodePrinter.code { + sj[Record] + } // val thing = Foo("Greg", 57) + println(sj[Record].toJson(record)) + println("------") + println(sj[Record].fromJson(jsData)) + // println(sj[Foo].fromJson("""{"name":"Greg","age":57}""")) + + /* val jjs = """{"name":"Greg","age":57}""" // println(ScalaJack.read[Foo](jjs)) + */ - val record = ScalaJack.read[Record](jsData) match - case Right(r) => r - case Left(t) => throw t + // val record = sj[Record].fromJson(jsData) match + // case Right(r) => r + // case Left(t: Throwable) => throw t - println(record) + // println(record) + // println("------") + // println(sj[Record].toJson(record)) - val r2 = ScalaJack.read[Record](jsData) - println(ScalaJack.write(record)) + // val r2 = ScalaJack.read[Record](jsData) + // println(ScalaJack.write(record)) - implicit val z: json.sj[Record] = ScalaJack.inspect[Record] - println(sj[Record].decodeJson(jsData)) + // implicit val z: json.sj[Record] = ScalaJack.inspect[Record] + // println(sj[Record].decodeJson(jsData)) catch { case t: Throwable => diff --git a/src/main/scala/co.blocke.scalajack/run/Record.scala b/src/main/scala/co.blocke.scalajack/run/Record.scala index 426001db..9d3c4ee5 100644 --- a/src/main/scala/co.blocke.scalajack/run/Record.scala +++ b/src/main/scala/co.blocke.scalajack/run/Record.scala @@ -36,7 +36,8 @@ case class Record( pets: List[Pet] ) -case class Foo(name: String, maybe: Option[Int], age: Int, expected: String = "nada", gotit: Option[Int] = Some(5)) +// case class Foo(name: String, maybe: Option[Int], age: Int, expected: String = "nada", gotit: Option[Int] = Some(5)) +case class Foo(name: String, age: Int, expected: String = "nada") val jsData = """{ @@ -86,3 +87,10 @@ val jsData = } ] }""" + +val record = Record( + Person("John Doe", 30, Address("123 Main Street", "Anytown", "CA", "12345"), "john.doe@example.com", List("555-555-5555", "555-123-4567"), true), + List("reading", "swimming", "traveling"), + List(Friend("Jane Smith", 28, "jane.smith@example.com"), Friend("Bob Johnson", 32, "bob.johnson@example.com")), + List(Pet("Fido", "Dog", 5), Pet("Whiskers", "Cat", 3)) +) From fa32cf3e3f997d2a3085c0e7f50f537df09887c7 Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Sun, 19 Nov 2023 19:50:28 -0600 Subject: [PATCH 27/65] big perf bump --- TODO | 16 + benchmark/README.md | 2 +- benchmark/build.sbt | 2 +- benchmark/src/main/scala/co.blocke/Run.scala | 19 +- .../scala/co.blocke.scalajack/ScalaJack.scala | 6 +- .../internal/CodePrinter.scala | 9 +- .../co.blocke.scalajack/json/package.scala | 10 - .../json/reading/JsonReader.scala | 13 +- .../json/reading/Numbers.scala | 1 - .../json/writing/JsonOutput.scala | 149 ++++++ .../json/writing/JsonWriter.scala | 503 +++--------------- .../writing/{JW.scala => JsonWriter2.scalax} | 94 ++-- ...JsonWriterRT.scala => JsonWriterRT.scalax} | 0 .../json/writing/JsonWriter_old.scalax | 492 +++++++++++++++++ .../scala/co.blocke.scalajack/run/Play.scala | 6 +- 15 files changed, 817 insertions(+), 505 deletions(-) create mode 100644 TODO create mode 100644 src/main/scala/co.blocke.scalajack/json/writing/JsonOutput.scala rename src/main/scala/co.blocke.scalajack/json/writing/{JW.scala => JsonWriter2.scalax} (57%) rename src/main/scala/co.blocke.scalajack/json/writing/{JsonWriterRT.scala => JsonWriterRT.scalax} (100%) create mode 100644 src/main/scala/co.blocke.scalajack/json/writing/JsonWriter_old.scalax diff --git a/TODO b/TODO new file mode 100644 index 00000000..73dee13c --- /dev/null +++ b/TODO @@ -0,0 +1,16 @@ +Path to Performance +------------------- + + + +[ ] - Create def() for each class (and maybe each collection?) found, to output json as seamlessly as possible + + -- Possibly a multi-pass generation? Once to identify all the classes and generate def fn names, and another + to actually generate the code in dependency order + +[*] - Create JsonOutput (like Jsoniter's JsonWriter, but the name conflicts w/mine). Initially continue to use + StringBuilder, but use some of Jsoniter's clever ',' handling + +[ ] - Explore dark magic Jsoniter mechanisms for all that bitwise/byte stuff to drive extreme efficiency + +[*] - In scala-reflection, explode PrimitiveRTypeRef into separate Refs, like they have for primitive RTypes \ No newline at end of file diff --git a/benchmark/README.md b/benchmark/README.md index 906ae883..43583add 100644 --- a/benchmark/README.md +++ b/benchmark/README.md @@ -32,7 +32,7 @@ sbt "jmh:run -i 10 -wi 10 -f 2 -t 1 co.blocke.*" | Jsoniter | thrpt | 20 | 2843150.452 | ± 21478.503 | ops/s | | Hand-Tooled | thrpt | 20 | 2732571.374 | ± 15129.007 | ops/s | | Circe | thrpt | 20 | 1958244.437 | ± 23965.817 | ops/s | -|**ScalaJack 8 (fast mode)** | thrpt | 20 | **1729426.328** | ± 4484.721 | ops/s | +|**ScalaJack 8 (fast mode)** | thrpt | 20 | **1736631.384** | ± 4484.721 | ops/s | | ZIO JSON | thrpt | 20 | 794352.301 | ± 32336.852 | ops/s | |**ScalaJack 8 (easy mode)** | thrpt | 20 | **705318.598** | ± 19932.389 | ops/s | | Argonaut | thrpt | 20 | 690269.697 | ± 6348.882 | ops/s | diff --git a/benchmark/build.sbt b/benchmark/build.sbt index a93ec4cb..dd177f6b 100644 --- a/benchmark/build.sbt +++ b/benchmark/build.sbt @@ -36,7 +36,7 @@ lazy val benchmark = project libraryDependencies ++= Seq( "org.playframework" %% "play-json" % "3.0.1", "io.argonaut" %% "argonaut" % "6.3.9", - "co.blocke" %% "scalajack" % "4a7b71_unknown", // New-New + "co.blocke" %% "scalajack" % "f01675_unknown", // New-New "co.blocke" %% "scala-reflection" % "sj_fixes_edbef8", "dev.zio" %% "zio-json" % "0.6.1", "org.typelevel" %% "fabric-core" % "1.12.6", diff --git a/benchmark/src/main/scala/co.blocke/Run.scala b/benchmark/src/main/scala/co.blocke/Run.scala index bc1ddea9..33451f47 100644 --- a/benchmark/src/main/scala/co.blocke/Run.scala +++ b/benchmark/src/main/scala/co.blocke/Run.scala @@ -3,11 +3,24 @@ package co.blocke import com.github.plokhotnyuk.jsoniter_scala.core._ import com.github.plokhotnyuk.jsoniter_scala.macros._ +// import io.circe.syntax.* +// import io.circe.* +// import io.circe.generic.semiauto.* +// import io.circe.parser.* + object RunMe extends App: - given codec: JsonValueCodec[Record] = JsonCodecMaker.make - println(readFromString[Record](jsData)) + // Circe generates a gonzo qty of type information--no actual "code" + // co.blocke.scalajack.internal.CodePrinter.code { + // deriveEncoder[Record] + // } + + co.blocke.scalajack.internal.CodePrinter.code { + given codec: JsonValueCodec[Record] = JsonCodecMaker.make + } + // given codec: JsonValueCodec[Record] = JsonCodecMaker.make + // println(readFromString[Record](jsData)) - println(writeToString(record)) + // println(writeToString(record)) println("\nDone") \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/ScalaJack.scala b/src/main/scala/co.blocke.scalajack/ScalaJack.scala index 3a2b3382..16c95edd 100644 --- a/src/main/scala/co.blocke.scalajack/ScalaJack.scala +++ b/src/main/scala/co.blocke.scalajack/ScalaJack.scala @@ -7,12 +7,12 @@ import scala.quoted.* import quoted.Quotes import json.* -case class ScalaJack[T](jsonDecoder: reading.JsonDecoder[T], jsonEncoder: (T, StringBuilder, JsonConfig) => String): // extends JsonCodec[T] //with YamlCodec with MsgPackCodec +case class ScalaJack[T](jsonDecoder: reading.JsonDecoder[T], jsonEncoder: (T, writing.JsonOutput, JsonConfig) => String): // extends JsonCodec[T] //with YamlCodec with MsgPackCodec def fromJson(js: String)(using cfg: JsonConfig = JsonConfig()): Either[JsonParseError, T] = jsonDecoder.decodeJson(js) def toJson(a: T)(using cfg: JsonConfig = JsonConfig()): String = - jsonEncoder(a, new StringBuilder(), cfg) + jsonEncoder(a, writing.JsonOutput(), cfg) // --------------------------------------- @@ -26,7 +26,7 @@ object ScalaJack: import q.reflect.* val classRef = ReflectOnType[T](quotes)(TypeRepr.of[T], true)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) val jsonDecoder = reading.JsonReader.refRead(classRef) - val jsonEncoder = writing.JW.refRead(classRef) + val jsonEncoder = writing.JsonWriter.refRead(classRef) '{ ScalaJack($jsonDecoder, $jsonEncoder) } diff --git a/src/main/scala/co.blocke.scalajack/internal/CodePrinter.scala b/src/main/scala/co.blocke.scalajack/internal/CodePrinter.scala index 4d2d8549..162bee24 100644 --- a/src/main/scala/co.blocke.scalajack/internal/CodePrinter.scala +++ b/src/main/scala/co.blocke.scalajack/internal/CodePrinter.scala @@ -3,6 +3,13 @@ package internal import scala.quoted.* +/** This is a fantastic object contributed by Aleksander Rainko. + * It will display the code generated by a macro to the console on compile: + * + * internal.CodePrinter.code { + * sj[Record] + * } + */ object CodePrinter { inline def structure[A](inline value: A) = ${ structureMacro('value) } @@ -22,4 +29,4 @@ object CodePrinter { report.info(struct) value } -} \ No newline at end of file +} diff --git a/src/main/scala/co.blocke.scalajack/json/package.scala b/src/main/scala/co.blocke.scalajack/json/package.scala index 8250d4a4..8bf78f50 100644 --- a/src/main/scala/co.blocke.scalajack/json/package.scala +++ b/src/main/scala/co.blocke.scalajack/json/package.scala @@ -7,13 +7,3 @@ val BUFFER_EXCEEDED: Char = 7 // Old "BELL" ASCII value, used as a marker when w val END_OF_STRING: Char = 3 inline def lastPart(n: String) = n.split('.').last.stripSuffix("$") - -// Tests whether we should write something or not--mainly in the case of Option, or wrapped Option -def isOkToWrite(a: Any, cfg: JsonConfig) = - a match - case None if !cfg.noneAsNull => false - case o: java.util.Optional[?] if o.isEmpty && !cfg.noneAsNull => false - case Left(None) if !cfg.noneAsNull => false - case Right(None) if !cfg.noneAsNull => false - case Failure(_) if cfg.tryFailureHandling == TryOption.NO_WRITE => false - case _ => true diff --git a/src/main/scala/co.blocke.scalajack/json/reading/JsonReader.scala b/src/main/scala/co.blocke.scalajack/json/reading/JsonReader.scala index 7524bb13..d06707fa 100644 --- a/src/main/scala/co.blocke.scalajack/json/reading/JsonReader.scala +++ b/src/main/scala/co.blocke.scalajack/json/reading/JsonReader.scala @@ -11,9 +11,9 @@ import scala.util.{Failure, Success, Try} import scala.collection.Factory /** We depart from ZIO-Json here. ZIO-Json uses implicits to marshal the right JsonDecoder. This works great for primitive types - * but I had issues trying to get it to work with macros for more complex types. Since we actually have all the necessary elements + * but I had issues trying to get it to work with macros for more complex types. Since we actually have all the necessary elements * to explicitly provide decoders for "constructed" types (collections, classes, ...) we just provided them explicitly. - * + * * The job of JsonReader is to accept an RTypeRef[T] and produce a JsonDecoder[T] for type T. */ object JsonReader: @@ -24,8 +24,13 @@ object JsonReader: import quotes.reflect.* ref match - case r: PrimitiveRef[?] => - Expr.summon[JsonDecoder[T]].getOrElse(throw JsonTypeError("No JsonDecoder defined for type " + TypeRepr.of[T].typeSymbol.name)) + case r: PrimitiveRef => + r.refType match + case '[t] => + Expr + .summon[JsonDecoder[t]] + .getOrElse(throw JsonTypeError("No JsonDecoder defined for type " + TypeRepr.of[t].typeSymbol.name)) + .asInstanceOf[Expr[JsonDecoder[T]]] case r: SeqRef[?] => r.refType match diff --git a/src/main/scala/co.blocke.scalajack/json/reading/Numbers.scala b/src/main/scala/co.blocke.scalajack/json/reading/Numbers.scala index f21bd047..ba9736e8 100644 --- a/src/main/scala/co.blocke.scalajack/json/reading/Numbers.scala +++ b/src/main/scala/co.blocke.scalajack/json/reading/Numbers.scala @@ -17,7 +17,6 @@ package co.blocke.scalajack package json package reading - import java.io.* import scala.util.control.NoStackTrace diff --git a/src/main/scala/co.blocke.scalajack/json/writing/JsonOutput.scala b/src/main/scala/co.blocke.scalajack/json/writing/JsonOutput.scala new file mode 100644 index 00000000..a8deb55c --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/writing/JsonOutput.scala @@ -0,0 +1,149 @@ +package co.blocke.scalajack +package json +package writing + +case class JsonOutput(): + val internal: StringBuilder = new StringBuilder() + + private var comma: Boolean = false + + def result = internal.result + + inline def startObject() = + internal.append('{') + comma = false + + inline def endObject() = + internal.append('}') + this + + inline def startArray() = + internal.append('[') + comma = false + + inline def endArray() = + internal.append(']') + this + + inline def maybeComma() = + if comma then + internal.append(',') + comma = false + + inline def burpNull() = + internal.append("null") + this + + inline def label( s: String ) = + maybeComma() + internal.append("\""+s+"\":") + + inline def label( s: Long ) = + maybeComma() + internal.append("\""+s+"\":") + + //----------------------- Primitive/Simple type support + + // TODO: BigDecimal, BigInt and Java equiv. + + inline def value(v: Boolean) = + internal.append(v) + comma = true + this + + inline def value(v: Byte) = + internal.append(v) + comma = true + this + + inline def value(v: Char) = + internal.append("\""+v+"\"") + comma = true + this + + inline def value(v: Double) = + internal.append(v) + comma = true + this + + inline def value(v: Float) = + internal.append(v) + comma = true + this + + inline def value(v: Int) = + internal.append(v) + comma = true + this + + inline def value(v: Long) = + internal.append(v) + comma = true + this + + inline def value(v: Short) = + internal.append(v) + comma = true + this + + inline def value(v: String) = + if v == null then internal.append("null") + else internal.append("\""+v+"\"") + comma = true + this + + inline def value(v: java.lang.Boolean) = + if v == null then internal.append("null") + else internal.append(v) + comma = true + this + + inline def value(v: java.lang.Byte) = + if v == null then internal.append("null") + else internal.append(v) + comma = true + this + + inline def value(v: java.lang.Character) = + if v == null then internal.append("null") + else internal.append("\""+v+"\"") + comma = true + this + + inline def value(v: java.lang.Double) = + if v == null then internal.append("null") + else internal.append(v) + comma = true + this + + inline def value(v: java.lang.Float) = + if v == null then internal.append("null") + else internal.append(v) + comma = true + this + + inline def value(v: java.lang.Integer) = + if v == null then internal.append("null") + else internal.append(v) + comma = true + this + + inline def value(v: java.lang.Long) = + if v == null then internal.append("null") + else internal.append(v) + comma = true + this + + inline def value(v: java.lang.Short) = + if v == null then internal.append("null") + else internal.append(v) + comma = true + this + + inline def value(v: java.lang.Number) = + if v == null then internal.append("null") + else internal.append(v) + comma = true + this + + // TODO: UUID \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json/writing/JsonWriter.scala b/src/main/scala/co.blocke.scalajack/json/writing/JsonWriter.scala index f2091b7b..ff2ae12c 100644 --- a/src/main/scala/co.blocke.scalajack/json/writing/JsonWriter.scala +++ b/src/main/scala/co.blocke.scalajack/json/writing/JsonWriter.scala @@ -1,55 +1,26 @@ package co.blocke.scalajack package json +package writing -import scala.quoted.* -import co.blocke.scala_reflection.* -import co.blocke.scala_reflection.rtypes.{EnumRType, NonConstructorFieldInfo, ScalaClassRType, TraitRType} -import co.blocke.scala_reflection.reflect.{ReflectOnType, TypeSymbolMapper} +import co.blocke.scala_reflection.{RTypeRef, TypedName} import co.blocke.scala_reflection.reflect.rtypeRefs.* -import scala.jdk.CollectionConverters.* -import scala.util.{Failure, Success, Try} -import scala.quoted.staging.* - -/* - TODO: - [*] - Scala non-case class - [*] - Java class (Do I still want to support this???) - [*] - Enum - [*] - Enumeration - [*] - Java Enum - [*] - Java Collections - [*] - Java Map - [*] - Intersection - [*] - Union - [*] - Either - [*] - Object (How???) - [*] - Trait (How???) - [*] - Sealed trait - [*] - sealed abstract class (handle like sealed trait....) - [*] - SelfRef - [*] - Tuple - [*] - Unknown (throw exception) - [*] - Scala 2 (throw exception) - [*] - TypeSymbol (throw exception) - [*] - Value class - - [*] -- correct all the 'if $aE == null...' - [*] -- type hint label mapping - [*] -- type hint value mapping - [*] -- Discontinue use of inTermsOf "open" (non-sealed) trait support (?!) - [*] -- update runtime-size TraitRType handling to match new compile-time code - - [ ] -- Streaming JSON write support - [ ] -- BigJSON support (eg. multi-gig file) - */ +import co.blocke.scala_reflection.rtypes.* +import scala.quoted.* +import scala.collection.mutable.Map as MMap +import scala.util.Failure object JsonWriter: - final inline def lastPart(n: String) = n.split('.').last.stripSuffix("$") + private def shouldTestForOkToWrite(r: RTypeRef[?]): Boolean = + r match + case _: OptionRef[?] => true + case _: LeftRightRef[?] => true + case _: TryRef[?] => true + case _ => false // Tests whether we should write something or not--mainly in the case of Option, or wrapped Option // Affected types: Option, java.util.Optional, Left/Right, Try/Failure - def isOkToWrite(a: Any, cfg: JsonConfig) = + private def isOkToWrite(a: Any, cfg: JsonConfig) = a match case None if !cfg.noneAsNull => false case o: java.util.Optional[?] if o.isEmpty && !cfg.noneAsNull => false @@ -58,435 +29,83 @@ object JsonWriter: case Failure(_) if cfg.tryFailureHandling == TryOption.NO_WRITE => false case _ => true - def refWrite[T]( - cfgE: Expr[JsonConfig], + def refRead[T]( + ref: RTypeRef[T] + )(using q: Quotes, tt: Type[T]): Expr[(T, JsonOutput, JsonConfig) => String] = + import quotes.reflect.* + '{ (a: T, out: JsonOutput, cfg: JsonConfig) => + ${ refWrite(ref, '{ a }, '{ out }, '{ cfg })(using MMap.empty[TypedName, RTypeRef[?]]) }.result + } + + private def refWrite[T]( ref: RTypeRef[T], aE: Expr[T], - sbE: Expr[StringBuilder], + outE: Expr[JsonOutput], + cfgE: Expr[JsonConfig], isMapKey: Boolean = false - )(using classesSeen: scala.collection.mutable.Map[TypedName, RTypeRef[?]])(using Quotes, Type[T]): Expr[StringBuilder] = + )(using classesSeen: MMap[TypedName, RTypeRef[?]])(using q: Quotes, tt: Type[T]): Expr[JsonOutput] = import quotes.reflect.* ref match - case t: PrimitiveRef[?] if t.family == PrimFamily.Stringish => '{ if $aE == null then $sbE.append("null") else $sbE.append("\"" + $aE.toString + "\"") } - case t: PrimitiveRef[?] => - val isNullable = Expr(t.isNullable) - if isMapKey then - '{ - if $isNullable && $aE == null then $sbE.append("\"null\"") - else - $sbE.append('"') - $sbE.append($aE.toString) - $sbE.append('"') - } - else '{ if $isNullable && $aE == null then $sbE.append("null") else $sbE.append($aE.toString) } + case t: BooleanRef => '{ $outE.value($aE) } + case t: IntRef => '{ $outE.value($aE) } + case t: StringRef => '{ $outE.value($aE) } case t: SeqRef[?] => if isMapKey then throw new JsonError("Seq instances cannot be map keys") - - t.elementRef.refType match - case '[e] => - '{ - val sb = $sbE - if $aE == null then sb.append("null") - else - sb.append('[') - val sbLen = sb.length - - $aE.asInstanceOf[Seq[e]].foldLeft(sb) { (acc, one) => - if isOkToWrite(one, $cfgE) then - ${ refWrite[e](cfgE, t.elementRef.asInstanceOf[RTypeRef[e]], '{ one }, '{ acc }) } - sb.append(',') - else sb - } - - if sbLen == sb.length then sb.append(']') - else sb.setCharAt(sb.length() - 1, ']') - } - case t: ArrayRef[?] => - if isMapKey then throw new JsonError("Arrays cannot be map keys") - t.elementRef.refType match case '[e] => + val bE = aE.asInstanceOf[Expr[Seq[e]]] // pull type-cast into complie-time for performance gains '{ - val sb = $sbE - if $aE == null then sb.append("null") + if $aE == null then $outE.burpNull() else - sb.append('[') - val sbLen = sb.length - - $aE.asInstanceOf[Array[e]].foldLeft(sb) { (acc, one) => - if isOkToWrite(one, $cfgE) then - ${ refWrite[e](cfgE, t.elementRef.asInstanceOf[RTypeRef[e]], '{ one }, '{ acc }) } - sb.append(',') - else sb + $outE.startArray() + $bE.foreach { one => + ${ + if shouldTestForOkToWrite(t.elementRef) then // A lot of drama to avoid 1 'if' stmt--it matters for max performance + '{ + if isOkToWrite(one, $cfgE) then ${ refWrite[e](t.elementRef.asInstanceOf[RTypeRef[e]], '{ one }, outE, cfgE) } + } + else refWrite[e](t.elementRef.asInstanceOf[RTypeRef[e]], '{ one }, outE, cfgE) + } } - - if sbLen == sb.length then sb.append(']') - else sb.setCharAt(sb.length() - 1, ']') + $outE.endArray() } - case t: ScalaClassRef[?] if t.isSealed && t.isAbstractClass => - classesSeen.put(t.typedName, t) - if t.childrenAreObject then - // case object -> just write the simple name of the object - '{ - if $aE == null then $sbE.append("null") - else - $sbE.append('"') - $sbE.append(lastPart($aE.getClass.getName)) - $sbE.append('"') - } - else - // Wow! If this is a sealed trait, all the children already have correctly-typed parameters--no need for expensive inTermsOf() - val rt = t.expr.asInstanceOf[Expr[ScalaClassRType[T]]] - '{ - if $aE == null then $sbE.append("null") - else - val className = $aE.getClass.getName - $rt.sealedChildren - .find(_.name == className) - .map(foundKid => - val augmented = foundKid.asInstanceOf[ScalaClassRType[foundKid.T]].copy(renderTrait = Some($rt.name)).asInstanceOf[RType[foundKid.T]] - JsonWriterRT.refWriteRT[foundKid.T]($cfgE, augmented, $aE.asInstanceOf[foundKid.T], $sbE)(using scala.collection.mutable.Map.empty[TypedName, RType[?]]) - ) - .getOrElse(throw new JsonError(s"Unrecognized child $className of seald trait " + $rt.name)) - } - - case t: ScalaClassRef[?] if t.isValueClass => - val theField = t.fields.head.fieldRef - theField.refType match - case '[e] => - val fieldValue = Select.unique(aE.asTerm, t.fields.head.name).asExprOf[e] - refWrite[e](cfgE, theField.asInstanceOf[RTypeRef[e]], fieldValue, sbE) - case t: ScalaClassRef[?] => - if t.isAbstractClass then throw new JsonError("Cannot serialize an abstract class") classesSeen.put(t.typedName, t) val isCase = Expr(t.isCaseClass) '{ - val sb = $sbE - if $aE == null then sb.append("null") + // Experiment (works!) to generate def fn(in:T, sb: JsonOut). The goal is to pre-calc all the ugly stuff so that the + // macro generated is as pure/fast as possible. + // + // def foo(in: T): Unit = + // ${ + // Expr.ofList(t.fields.map { f => + // '{ println("Field: " + ${ Select.unique('in.asTerm, f.name).asExprOf[Any] }) } + // }) + // } + if $aE == null then $outE.burpNull() else - sb.append('{') - val sbLen = sb.length + $outE.startObject() ${ - t.fields.foldLeft('{ sb }) { (accE, f) => + Expr.ofList(t.fields.map { f => f.fieldRef.refType match case '[e] => val fieldValue = Select.unique(aE.asTerm, f.name).asExprOf[e] val name = Expr(f.name) - '{ - val acc = $accE - if isOkToWrite($fieldValue, $cfgE) then - acc.append('"') - acc.append($name) - acc.append('"') - acc.append(':') - ${ refWrite[e](cfgE, f.fieldRef.asInstanceOf[RTypeRef[e]], fieldValue, '{ acc }) } - acc.append(',') - else acc - } - } - } - if ! $isCase && $cfgE.writeNonConstructorFields then - ${ - t.nonConstructorFields.foldLeft('{ sb }) { (accE, f) => - f.fieldRef.refType match - case '[e] => - val fieldValue = Select.unique(aE.asTerm, f.getterLabel).asExprOf[e] - val name = Expr(f.name) + if shouldTestForOkToWrite(f.fieldRef) then // A lot of drama to avoid 1 'if' stmt--it matters for max performance '{ - val acc = $accE if isOkToWrite($fieldValue, $cfgE) then - acc.append('"') - acc.append($name) - acc.append('"') - acc.append(':') - ${ refWrite[e](cfgE, f.fieldRef.asInstanceOf[RTypeRef[e]], fieldValue, '{ acc }) } - acc.append(',') - else acc + $outE.label($name) + ${ refWrite[e](f.fieldRef.asInstanceOf[RTypeRef[e]], fieldValue, outE, cfgE) } } - } - } - if sbLen == sb.length then sb.append('}') - else sb.setCharAt(sb.length() - 1, '}') - } - - case t: TraitRef[?] => - classesSeen.put(t.typedName, t) - val rt = t.expr.asInstanceOf[Expr[TraitRType[T]]] - if t.childrenAreObject then - // case object -> just write the simple name of the object - '{ - if $aE == null then $sbE.append("null") - else - $sbE.append('"') - $sbE.append(lastPart($aE.getClass.getName)) - $sbE.append('"') - } - else if t.isSealed then - // Wow! If this is a sealed trait, all the children already have correctly-typed parameters--no need for expensive inTermsOf() - '{ - if $aE == null then $sbE.append("null") - else - val className = $aE.getClass.getName - $rt.sealedChildren - .find(_.name == className) - .map(foundKid => - val augmented = foundKid.asInstanceOf[ScalaClassRType[foundKid.T]].copy(renderTrait = Some($rt.name)).asInstanceOf[RType[foundKid.T]] - JsonWriterRT.refWriteRT[foundKid.T]($cfgE, augmented, $aE.asInstanceOf[foundKid.T], $sbE)(using scala.collection.mutable.Map.empty[TypedName, RType[?]]) - ) - .getOrElse(throw new JsonError(s"Unrecognized child $className of seald trait " + $rt.name)) - } - else throw new JsonError("non-sealed traits are not supported") - // '{ - // if $aE == null then $sbE.append("null") - // else - // given Compiler = Compiler.make($aE.getClass.getClassLoader) - // val fn = (q: Quotes) ?=> { - // import q.reflect.* - // val sb = $sbE - // val classRType = RType.inTermsOf[T]($aE.getClass).asInstanceOf[ScalaClassRType[T]].copy(renderTrait = Some($rt.name)).asInstanceOf[RType[T]] - // JsonWriterRT.refWriteRT[classRType.T]($cfgE, classRType, $aE.asInstanceOf[classRType.T], $sbE)(using scala.collection.mutable.Map.empty[TypedName, RType[?]]) - // Expr(1) // do-nothing... '{} requires Expr(something) be returned, so... - // } - // quoted.staging.run(fn) - // $sbE - // } - - case t: OptionRef[?] => - if isMapKey then throw new JsonError("Option valuess cannot be map keys") - t.optionParamType.refType match - case '[e] => - '{ - if $aE == null then $sbE.append("null") - else - $aE match - case None => $sbE.append("null") - case Some(v) => - ${ refWrite[e](cfgE, t.optionParamType.asInstanceOf[RTypeRef[e]], '{ v }.asInstanceOf[Expr[e]], sbE) } - } - - case t: MapRef[?] => - if isMapKey then throw new JsonError("Map values cannot be map keys") - t.elementRef.refType match - case '[k] => - t.elementRef2.refType match - case '[v] => - '{ - val sb = $sbE - if $aE == null then sb.append("null") - else - sb.append('{') - val sbLen = sb.length - $aE.asInstanceOf[Map[?, ?]].foreach { case (key, value) => - if isOkToWrite(value, $cfgE) then - val b = ${ refWrite[k](cfgE, t.elementRef.asInstanceOf[RTypeRef[k]], '{ key }.asInstanceOf[Expr[k]], sbE, true) } - b.append(':') - val b2 = ${ refWrite[v](cfgE, t.elementRef2.asInstanceOf[RTypeRef[v]], '{ value }.asInstanceOf[Expr[v]], sbE) } - b2.append(',') - } - if sbLen == sb.length then sb.append('}') - else sb.setCharAt(sb.length() - 1, '}') - } - - case t: LeftRightRef[?] => - if isMapKey then throw new JsonError("Union, Intersection, or Either-typed values cannot be map keys.") - t.leftRef.refType match - case '[lt] => - t.rightRef.refType match - case '[rt] => - val isEither = Expr(t.lrkind == LRKind.EITHER) - '{ - if $isEither then - $aE match - case Left(v) => - val vv = v.asInstanceOf[lt] - ${ refWrite[lt](cfgE, t.leftRef.asInstanceOf[RTypeRef[lt]], '{ vv }.asInstanceOf[Expr[lt]], sbE) } - case Right(v) => - val vv = v.asInstanceOf[rt] - ${ refWrite[rt](cfgE, t.rightRef.asInstanceOf[RTypeRef[rt]], '{ vv }.asInstanceOf[Expr[rt]], sbE) } - else - // Intersection/Union types....take your best shot! It's all we've got. No definitive info here. - val trial = new StringBuilder() - val lrSb = scala.util.Try { - val vv = $aE.asInstanceOf[rt] - ${ - refWrite[rt](cfgE, t.rightRef.asInstanceOf[RTypeRef[rt]], '{ vv }, '{ trial }) + else + '{ + $outE.label($name) + ${ refWrite[e](f.fieldRef.asInstanceOf[RTypeRef[e]], fieldValue, outE, cfgE) } } - } match - case Success(trialSb) => trialSb - case Failure(_) => - trial.clear - val vv = $aE.asInstanceOf[lt] - ${ - refWrite[lt](cfgE, t.leftRef.asInstanceOf[RTypeRef[lt]], '{ vv }, '{ trial }) - } - $sbE ++= lrSb - } - - case t: TryRef[?] => - if isMapKey then throw new JsonError("Try values (Succeed/Fail) cannot be map keys") - t.tryRef.refType match - case '[e] => - '{ - if $aE == null then $sbE.append("null") - else - $aE match - case Success(v) => - ${ refWrite[e](cfgE, t.tryRef.asInstanceOf[RTypeRef[e]], '{ v }.asInstanceOf[Expr[e]], sbE) } - case Failure(_) if $cfgE.tryFailureHandling == TryOption.AS_NULL => $sbE.append("null") - case Failure(_) if $cfgE.tryFailureHandling == TryOption.AS_NULL => $sbE.append("null") - case Failure(f) if $cfgE.tryFailureHandling == TryOption.THROW_EXCEPTION => - throw new JsonError("A try value was Failure with message: " + f.getMessage()) - case Failure(v) => - $sbE.append("\"Failure(") - $sbE.append(v.getMessage) - $sbE.append(")\"") - } - - case t: TupleRef[?] => - if isMapKey then throw new JsonError("Tuples cannot be map keys") - '{ - val sb = $sbE - if $aE == null then sb.append("null") - else - sb.append('[') - val sbLen = sb.length - ${ - val tupleBuf = t.tupleRefs.zipWithIndex.foldLeft(sbE) { case (accE, (ref, i)) => - ref.refType match - case '[e] => - val fieldValue = Select.unique(aE.asTerm, "_" + (i + 1)).asExprOf[e] - '{ - val acc = $accE - ${ refWrite[e](cfgE, ref.asInstanceOf[RTypeRef[e]], fieldValue, '{ acc }) } - acc.append(',') - } - } - tupleBuf - } - if sbLen == sb.length then sb.append(']') - else sb.setCharAt(sb.length() - 1, ']') - } - - case t: JavaCollectionRef[?] => - if isMapKey then throw new JsonError("Collections cannot be map keys.") - t.elementRef.refType match - case '[e] => - '{ - val sb = $sbE - if $aE == null then sb.append("null") - sb.append('[') - val sbLen = sb.length - $aE.asInstanceOf[java.util.Collection[?]].toArray.foreach { elem => - if isOkToWrite(elem, $cfgE) then - ${ refWrite[e](cfgE, t.elementRef.asInstanceOf[RTypeRef[e]], '{ elem }.asInstanceOf[Expr[e]], sbE) } - sb.append(',') - } - if sbLen == sb.length then sb.append(']') - else sb.setCharAt(sb.length() - 1, ']') - } - - case t: JavaMapRef[?] => - if isMapKey then throw new JsonError("Maps cannot be map keys.") - t.elementRef.refType match - case '[k] => - t.elementRef2.refType match - case '[v] => - '{ - val sb = $sbE - if $aE == null then sb.append("null") - sb.append('{') - val sbLen = sb.length - $aE.asInstanceOf[java.util.Map[?, ?]].asScala.foreach { case (key, value) => - if isOkToWrite(value, $cfgE) then - ${ refWrite[k](cfgE, t.elementRef.asInstanceOf[RTypeRef[k]], '{ key }.asInstanceOf[Expr[k]], sbE, true) } - sb.append(':') - ${ refWrite[v](cfgE, t.elementRef2.asInstanceOf[RTypeRef[v]], '{ value }.asInstanceOf[Expr[v]], sbE) } - sb.append(',') - } - if sbLen == sb.length then sb.append('}') - else sb.setCharAt(sb.length() - 1, '}') - } - - case t: AliasRef[?] => - t.unwrappedType.refType match - case '[e] => - refWrite[e](cfgE, t.unwrappedType.asInstanceOf[RTypeRef[e]], aE.asInstanceOf[Expr[e]], sbE) - - case t: SelfRefRef[?] => - if isMapKey then throw new JsonError("Classes or traits cannot be map keys.") - import quotes.reflect.* - val againE = classesSeen.getOrElse(t.typedName, throw new JsonError("Dangling self-reference: " + t.name)).asInstanceOf[RTypeRef[T]].expr - '{ - val again = $againE.asInstanceOf[RType[T]] - JsonWriterRT.refWriteRT[T]($cfgE, again, $aE.asInstanceOf[T], $sbE)(using scala.collection.mutable.Map.empty[TypedName, RType[?]]) - $sbE - } - - case t: EnumRef[?] => - val enumE = t.expr - val isMapKeyE = Expr(isMapKey) - '{ - if $aE == null then $sbE.append("null") - else - val enumRT = $enumE.asInstanceOf[EnumRType[T]] - val enumAsId = $cfgE.enumsAsIds match - case '*' => true - case aList: List[String] if aList.contains(enumRT.name) => true - case _ => false - if enumAsId then - val enumVal = enumRT.ordinal($aE.toString).getOrElse(throw new JsonError("Value " + $aE.toString + s" is not a valid enum value for ${enumRT.name}")) - if $isMapKeyE then - $sbE.append('"') - $sbE.append(enumVal.toString) - $sbE.append('"') - else $sbE.append(enumVal.toString) - else - $sbE.append('"') - $sbE.append($aE.toString) - $sbE.append('"') - } - - // Just handle Java classes 100% runtime since we need to leverage Java reflection entirely anyway - case t: JavaClassRef[?] => - t.refType match - case '[e] => - '{ - JsonWriterRT.refWriteRT[e]($cfgE, ${ t.expr }.asInstanceOf[RType[e]], $aE.asInstanceOf[e], $sbE)(using scala.collection.mutable.Map.empty[TypedName, RType[?]]) + }) } - - case t: ObjectRef => - val tname = Expr(t.name) - '{ - $sbE.append("\"" + $tname + "\"") - } - - case t: Scala2Ref[?] => - val tname = Expr(t.name) - '{ - $cfgE.undefinedFieldHandling match - case UndefinedValueOption.AS_NULL => $sbE.append("null") - case UndefinedValueOption.AS_SYMBOL => $sbE.append("\"" + $tname + "\"") - case UndefinedValueOption.THROW_EXCEPTION => throw new JsonError("Unknown/unsupported Scala 2 type " + $tname) - } - - case t: UnknownRef[?] => - val tname = Expr(t.name) - '{ - $cfgE.undefinedFieldHandling match - case UndefinedValueOption.AS_NULL => $sbE.append("null") - case UndefinedValueOption.AS_SYMBOL => $sbE.append("\"" + $tname + "\"") - case UndefinedValueOption.THROW_EXCEPTION => throw new JsonError("Unknown/unsupported type " + $tname) - } - - case t: TypeSymbolRef => - val tname = Expr(t.name) - '{ - $cfgE.undefinedFieldHandling match - case UndefinedValueOption.AS_NULL => $sbE.append("null") - case UndefinedValueOption.AS_SYMBOL => $sbE.append("\"" + $tname + "\"") - case UndefinedValueOption.THROW_EXCEPTION => throw new JsonError("Unknown/unsupported type " + $tname + ". (Class didn't fully define all its type parameters.)") + $outE.endObject() } diff --git a/src/main/scala/co.blocke.scalajack/json/writing/JW.scala b/src/main/scala/co.blocke.scalajack/json/writing/JsonWriter2.scalax similarity index 57% rename from src/main/scala/co.blocke.scalajack/json/writing/JW.scala rename to src/main/scala/co.blocke.scalajack/json/writing/JsonWriter2.scalax index 5b496560..daec147b 100644 --- a/src/main/scala/co.blocke.scalajack/json/writing/JW.scala +++ b/src/main/scala/co.blocke.scalajack/json/writing/JsonWriter2.scalax @@ -5,58 +5,72 @@ package writing import co.blocke.scala_reflection.{RTypeRef, TypedName} import co.blocke.scala_reflection.reflect.rtypeRefs.* import scala.quoted.* -import scala.collection.mutable.{Map=>MMap} +import scala.collection.mutable.Map as MMap +import scala.util.Failure -object JW: +object JsonWriter2: - private def shouldTestForOkToWrite( r: RTypeRef[_] ): Boolean = - r match + private def shouldTestForOkToWrite(r: RTypeRef[?]): Boolean = + r match case _: OptionRef[?] => true case _: LeftRightRef[?] => true case _: TryRef[?] => true case _ => false + // Tests whether we should write something or not--mainly in the case of Option, or wrapped Option + // Affected types: Option, java.util.Optional, Left/Right, Try/Failure + private def isOkToWrite(a: Any, cfg: JsonConfig) = + a match + case None if !cfg.noneAsNull => false + case o: java.util.Optional[?] if o.isEmpty && !cfg.noneAsNull => false + case Left(None) if !cfg.noneAsNull => false + case Right(None) if !cfg.noneAsNull => false + case Failure(_) if cfg.tryFailureHandling == TryOption.NO_WRITE => false + case _ => true + def refRead[T]( ref: RTypeRef[T] - )(using q: Quotes, tt: Type[T]): Expr[(T, StringBuilder, JsonConfig)=>String] = + )(using q: Quotes, tt: Type[T]): Expr[(T, StringBuilder, JsonConfig) => String] = import quotes.reflect.* - '{ - (a: T, sb: StringBuilder, cfg: JsonConfig) => ${refWrite(ref, '{a}, '{sb}, '{cfg})(using MMap.empty[TypedName,RTypeRef[?]])}.toString + '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => + ${ refWrite(ref, '{ a }, '{ sb }, '{ cfg })(using MMap.empty[TypedName, RTypeRef[?]]) }.toString } private def refWrite[T]( - ref: RTypeRef[T], - aE: Expr[T], - sbE: Expr[StringBuilder], - cfgE: Expr[JsonConfig], - isMapKey: Boolean = false - )(using classesSeen: MMap[TypedName, RTypeRef[?]])(using q: Quotes, tt: Type[T]) : Expr[StringBuilder] = + ref: RTypeRef[T], + aE: Expr[T], + sbE: Expr[StringBuilder], + cfgE: Expr[JsonConfig], + isMapKey: Boolean = false + )(using classesSeen: MMap[TypedName, RTypeRef[?]])(using q: Quotes, tt: Type[T]): Expr[StringBuilder] = import quotes.reflect.* - + ref match - case t: PrimitiveRef[?] if t.family == PrimFamily.Stringish => - '{ if $aE == null then $sbE.append("null") else $sbE.append(s"\"${$aE}\"") } + case t: PrimitiveRef[?] if t.family == PrimFamily.Stringish => + if t.isNullable then '{ if $aE == null then $sbE.append("null") else $sbE.append("\"" + $aE + "\"") } + else '{ $sbE.append("\"" + $aE + "\"") } case t: PrimitiveRef[?] => val isNullable = Expr(t.isNullable) if isMapKey then '{ if $isNullable && $aE == null then $sbE.append("\"null\"") - else $sbE.append(s"\"${$aE.toString}\"") + else $sbE.append($sbE.append("\"" + $aE + "\"")) } - else '{ if $isNullable && $aE == null then $sbE.append("null") else $sbE.append($aE.toString) } + else if t.isNullable then '{ if $aE == null then $sbE.append("null") else $sbE.append("\"" + $aE.toString + "\"") } + else '{ $sbE.append($aE.toString) } case t: SeqRef[?] => if isMapKey then throw new JsonError("Seq instances cannot be map keys") t.elementRef.refType match case '[e] => - val bE = aE.asInstanceOf[Expr[Seq[e]]] // pull type-cast into complie-time for performance gains + val bE = aE.asInstanceOf[Expr[Seq[e]]] // pull type-cast into complie-time for performance gains '{ if $aE == null then $sbE.append("null") else $sbE.append('[') val sbLen = $sbE.length() - + $bE.foreach { one => ${ if shouldTestForOkToWrite(t.elementRef) then // A lot of drama to avoid 1 'if' stmt--it matters for max performance @@ -67,8 +81,8 @@ object JW: } else '{ - ${ refWrite[e](t.elementRef.asInstanceOf[RTypeRef[e]], '{ one }, sbE, cfgE) } - $sbE.append(',') + ${ refWrite[e](t.elementRef.asInstanceOf[RTypeRef[e]], '{ one }, sbE, cfgE) } + $sbE.append(',') } } } @@ -80,36 +94,44 @@ object JW: classesSeen.put(t.typedName, t) val isCase = Expr(t.isCaseClass) '{ + // Experiment (works!) to generate def fn(in:T, sb: JsonOut). The goal is to pre-calc all the ugly stuff so that the + // macro generated is as pure/fast as possible. + // + // def foo(in: T): Unit = + // ${ + // Expr.ofList(t.fields.map { f => + // '{ println("Field: " + ${ Select.unique('in.asTerm, f.name).asExprOf[Any] }) } + // }) + // } if $aE == null then $sbE.append("null") else $sbE.append('{') val sbLen = $sbE.length() ${ - Expr.ofList(t.fields.map{ f => + Expr.ofList(t.fields.map { f => f.fieldRef.refType match case '[e] => val fieldValue = Select.unique(aE.asTerm, f.name).asExprOf[e] val name = Expr(f.name) if shouldTestForOkToWrite(f.fieldRef) then // A lot of drama to avoid 1 'if' stmt--it matters for max performance '{ - if isOkToWrite($fieldValue, $cfgE) then - $sbE.append(s""""${$name}":""") - ${ refWrite[e](f.fieldRef.asInstanceOf[RTypeRef[e]], fieldValue, sbE, cfgE) } - $sbE.append(',') - else $sbE + if isOkToWrite($fieldValue, $cfgE) then + $sbE.append("\"" + $name + ":\"") + ${ refWrite[e](f.fieldRef.asInstanceOf[RTypeRef[e]], fieldValue, sbE, cfgE) } + $sbE.append(',') } else '{ - $sbE.append(s""""${$name}":""") - ${ refWrite[e](f.fieldRef.asInstanceOf[RTypeRef[e]], fieldValue, sbE, cfgE) } - $sbE.append(',') + $sbE.append("\"" + $name + ":\"") + ${ refWrite[e](f.fieldRef.asInstanceOf[RTypeRef[e]], fieldValue, sbE, cfgE) } + $sbE.append(',') } }) } // write out any non-constructor fields (non-case "plain" classes) if ! $isCase && $cfgE.writeNonConstructorFields then ${ - Expr.ofList(t.nonConstructorFields.map{ f => + Expr.ofList(t.nonConstructorFields.map { f => f.fieldRef.refType match case '[e] => val fieldValue = Select.unique(aE.asTerm, f.getterLabel).asExprOf[e] @@ -117,14 +139,14 @@ object JW: if shouldTestForOkToWrite(f.fieldRef) then // A lot of drama to avoid 1 'if' stmt--it matters for max performance '{ if isOkToWrite($fieldValue, $cfgE) then - $sbE.append(s"\"${$name}\":") + $sbE.append(s"\"$$name\":") ${ refWrite[e](f.fieldRef.asInstanceOf[RTypeRef[e]], fieldValue, sbE, cfgE) } $sbE.append(',') else $sbE } - else + else '{ - $sbE.append(s"\"${$name}\":") + $sbE.append(s"\"$$name\":") ${ refWrite[e](f.fieldRef.asInstanceOf[RTypeRef[e]], fieldValue, sbE, cfgE) } $sbE.append(',') } @@ -132,4 +154,4 @@ object JW: } if sbLen == $sbE.length() then $sbE.append('}') else $sbE.setCharAt($sbE.length() - 1, '}') - } \ No newline at end of file + } diff --git a/src/main/scala/co.blocke.scalajack/json/writing/JsonWriterRT.scala b/src/main/scala/co.blocke.scalajack/json/writing/JsonWriterRT.scalax similarity index 100% rename from src/main/scala/co.blocke.scalajack/json/writing/JsonWriterRT.scala rename to src/main/scala/co.blocke.scalajack/json/writing/JsonWriterRT.scalax diff --git a/src/main/scala/co.blocke.scalajack/json/writing/JsonWriter_old.scalax b/src/main/scala/co.blocke.scalajack/json/writing/JsonWriter_old.scalax new file mode 100644 index 00000000..c3fb4344 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/writing/JsonWriter_old.scalax @@ -0,0 +1,492 @@ +package co.blocke.scalajack +package json + +import scala.quoted.* +import co.blocke.scala_reflection.* +import co.blocke.scala_reflection.rtypes.{EnumRType, NonConstructorFieldInfo, ScalaClassRType, TraitRType} +import co.blocke.scala_reflection.reflect.{ReflectOnType, TypeSymbolMapper} +import co.blocke.scala_reflection.reflect.rtypeRefs.* +import scala.jdk.CollectionConverters.* +import scala.util.{Failure, Success, Try} +import scala.quoted.staging.* + +/* + TODO: + [*] - Scala non-case class + [*] - Java class (Do I still want to support this???) + [*] - Enum + [*] - Enumeration + [*] - Java Enum + [*] - Java Collections + [*] - Java Map + [*] - Intersection + [*] - Union + [*] - Either + [*] - Object (How???) + [*] - Trait (How???) + [*] - Sealed trait + [*] - sealed abstract class (handle like sealed trait....) + [*] - SelfRef + [*] - Tuple + [*] - Unknown (throw exception) + [*] - Scala 2 (throw exception) + [*] - TypeSymbol (throw exception) + [*] - Value class + + [*] -- correct all the 'if $aE == null...' + [*] -- type hint label mapping + [*] -- type hint value mapping + [*] -- Discontinue use of inTermsOf "open" (non-sealed) trait support (?!) + [*] -- update runtime-size TraitRType handling to match new compile-time code + + [ ] -- Streaming JSON write support + [ ] -- BigJSON support (eg. multi-gig file) + */ + +object JsonWriter_old: + + final inline def lastPart(n: String) = n.split('.').last.stripSuffix("$") + + // Tests whether we should write something or not--mainly in the case of Option, or wrapped Option + // Affected types: Option, java.util.Optional, Left/Right, Try/Failure + def isOkToWrite(a: Any, cfg: JsonConfig) = + a match + case None if !cfg.noneAsNull => false + case o: java.util.Optional[?] if o.isEmpty && !cfg.noneAsNull => false + case Left(None) if !cfg.noneAsNull => false + case Right(None) if !cfg.noneAsNull => false + case Failure(_) if cfg.tryFailureHandling == TryOption.NO_WRITE => false + case _ => true + + def refWrite[T]( + cfgE: Expr[JsonConfig], + ref: RTypeRef[T], + aE: Expr[T], + sbE: Expr[StringBuilder], + isMapKey: Boolean = false + )(using classesSeen: scala.collection.mutable.Map[TypedName, RTypeRef[?]])(using Quotes, Type[T]): Expr[StringBuilder] = + import quotes.reflect.* + + ref match + case t: PrimitiveRef[?] if t.family == PrimFamily.Stringish => '{ if $aE == null then $sbE.append("null") else $sbE.append("\"" + $aE.toString + "\"") } + case t: PrimitiveRef[?] => + val isNullable = Expr(t.isNullable) + if isMapKey then + '{ + if $isNullable && $aE == null then $sbE.append("\"null\"") + else + $sbE.append('"') + $sbE.append($aE.toString) + $sbE.append('"') + } + else '{ if $isNullable && $aE == null then $sbE.append("null") else $sbE.append($aE.toString) } + + case t: SeqRef[?] => + if isMapKey then throw new JsonError("Seq instances cannot be map keys") + + t.elementRef.refType match + case '[e] => + '{ + val sb = $sbE + if $aE == null then sb.append("null") + else + sb.append('[') + val sbLen = sb.length + + $aE.asInstanceOf[Seq[e]].foldLeft(sb) { (acc, one) => + if isOkToWrite(one, $cfgE) then + ${ refWrite[e](cfgE, t.elementRef.asInstanceOf[RTypeRef[e]], '{ one }, '{ acc }) } + sb.append(',') + else sb + } + + if sbLen == sb.length then sb.append(']') + else sb.setCharAt(sb.length() - 1, ']') + } + case t: ArrayRef[?] => + if isMapKey then throw new JsonError("Arrays cannot be map keys") + + t.elementRef.refType match + case '[e] => + '{ + val sb = $sbE + if $aE == null then sb.append("null") + else + sb.append('[') + val sbLen = sb.length + + $aE.asInstanceOf[Array[e]].foldLeft(sb) { (acc, one) => + if isOkToWrite(one, $cfgE) then + ${ refWrite[e](cfgE, t.elementRef.asInstanceOf[RTypeRef[e]], '{ one }, '{ acc }) } + sb.append(',') + else sb + } + + if sbLen == sb.length then sb.append(']') + else sb.setCharAt(sb.length() - 1, ']') + } + + case t: ScalaClassRef[?] if t.isSealed && t.isAbstractClass => + classesSeen.put(t.typedName, t) + if t.childrenAreObject then + // case object -> just write the simple name of the object + '{ + if $aE == null then $sbE.append("null") + else + $sbE.append('"') + $sbE.append(lastPart($aE.getClass.getName)) + $sbE.append('"') + } + else + // Wow! If this is a sealed trait, all the children already have correctly-typed parameters--no need for expensive inTermsOf() + val rt = t.expr.asInstanceOf[Expr[ScalaClassRType[T]]] + '{ + if $aE == null then $sbE.append("null") + else + val className = $aE.getClass.getName + $rt.sealedChildren + .find(_.name == className) + .map(foundKid => + val augmented = foundKid.asInstanceOf[ScalaClassRType[foundKid.T]].copy(renderTrait = Some($rt.name)).asInstanceOf[RType[foundKid.T]] + JsonWriterRT.refWriteRT[foundKid.T]($cfgE, augmented, $aE.asInstanceOf[foundKid.T], $sbE)(using scala.collection.mutable.Map.empty[TypedName, RType[?]]) + ) + .getOrElse(throw new JsonError(s"Unrecognized child $className of seald trait " + $rt.name)) + } + + case t: ScalaClassRef[?] if t.isValueClass => + val theField = t.fields.head.fieldRef + theField.refType match + case '[e] => + val fieldValue = Select.unique(aE.asTerm, t.fields.head.name).asExprOf[e] + refWrite[e](cfgE, theField.asInstanceOf[RTypeRef[e]], fieldValue, sbE) + + case t: ScalaClassRef[?] => + if t.isAbstractClass then throw new JsonError("Cannot serialize an abstract class") + classesSeen.put(t.typedName, t) + val isCase = Expr(t.isCaseClass) + '{ + val sb = $sbE + if $aE == null then sb.append("null") + else + sb.append('{') + val sbLen = sb.length + ${ + t.fields.foldLeft('{ sb }) { (accE, f) => + f.fieldRef.refType match + case '[e] => + val fieldValue = Select.unique(aE.asTerm, f.name).asExprOf[e] + val name = Expr(f.name) + '{ + val acc = $accE + if isOkToWrite($fieldValue, $cfgE) then + acc.append('"') + acc.append($name) + acc.append('"') + acc.append(':') + ${ refWrite[e](cfgE, f.fieldRef.asInstanceOf[RTypeRef[e]], fieldValue, '{ acc }) } + acc.append(',') + else acc + } + } + } + if ! $isCase && $cfgE.writeNonConstructorFields then + ${ + t.nonConstructorFields.foldLeft('{ sb }) { (accE, f) => + f.fieldRef.refType match + case '[e] => + val fieldValue = Select.unique(aE.asTerm, f.getterLabel).asExprOf[e] + val name = Expr(f.name) + '{ + val acc = $accE + if isOkToWrite($fieldValue, $cfgE) then + acc.append('"') + acc.append($name) + acc.append('"') + acc.append(':') + ${ refWrite[e](cfgE, f.fieldRef.asInstanceOf[RTypeRef[e]], fieldValue, '{ acc }) } + acc.append(',') + else acc + } + } + } + if sbLen == sb.length then sb.append('}') + else sb.setCharAt(sb.length() - 1, '}') + } + + case t: TraitRef[?] => + classesSeen.put(t.typedName, t) + val rt = t.expr.asInstanceOf[Expr[TraitRType[T]]] + if t.childrenAreObject then + // case object -> just write the simple name of the object + '{ + if $aE == null then $sbE.append("null") + else + $sbE.append('"') + $sbE.append(lastPart($aE.getClass.getName)) + $sbE.append('"') + } + else if t.isSealed then + // Wow! If this is a sealed trait, all the children already have correctly-typed parameters--no need for expensive inTermsOf() + '{ + if $aE == null then $sbE.append("null") + else + val className = $aE.getClass.getName + $rt.sealedChildren + .find(_.name == className) + .map(foundKid => + val augmented = foundKid.asInstanceOf[ScalaClassRType[foundKid.T]].copy(renderTrait = Some($rt.name)).asInstanceOf[RType[foundKid.T]] + JsonWriterRT.refWriteRT[foundKid.T]($cfgE, augmented, $aE.asInstanceOf[foundKid.T], $sbE)(using scala.collection.mutable.Map.empty[TypedName, RType[?]]) + ) + .getOrElse(throw new JsonError(s"Unrecognized child $className of seald trait " + $rt.name)) + } + else throw new JsonError("non-sealed traits are not supported") + // '{ + // if $aE == null then $sbE.append("null") + // else + // given Compiler = Compiler.make($aE.getClass.getClassLoader) + // val fn = (q: Quotes) ?=> { + // import q.reflect.* + // val sb = $sbE + // val classRType = RType.inTermsOf[T]($aE.getClass).asInstanceOf[ScalaClassRType[T]].copy(renderTrait = Some($rt.name)).asInstanceOf[RType[T]] + // JsonWriterRT.refWriteRT[classRType.T]($cfgE, classRType, $aE.asInstanceOf[classRType.T], $sbE)(using scala.collection.mutable.Map.empty[TypedName, RType[?]]) + // Expr(1) // do-nothing... '{} requires Expr(something) be returned, so... + // } + // quoted.staging.run(fn) + // $sbE + // } + + case t: OptionRef[?] => + if isMapKey then throw new JsonError("Option valuess cannot be map keys") + t.optionParamType.refType match + case '[e] => + '{ + if $aE == null then $sbE.append("null") + else + $aE match + case None => $sbE.append("null") + case Some(v) => + ${ refWrite[e](cfgE, t.optionParamType.asInstanceOf[RTypeRef[e]], '{ v }.asInstanceOf[Expr[e]], sbE) } + } + + case t: MapRef[?] => + if isMapKey then throw new JsonError("Map values cannot be map keys") + t.elementRef.refType match + case '[k] => + t.elementRef2.refType match + case '[v] => + '{ + val sb = $sbE + if $aE == null then sb.append("null") + else + sb.append('{') + val sbLen = sb.length + $aE.asInstanceOf[Map[?, ?]].foreach { case (key, value) => + if isOkToWrite(value, $cfgE) then + val b = ${ refWrite[k](cfgE, t.elementRef.asInstanceOf[RTypeRef[k]], '{ key }.asInstanceOf[Expr[k]], sbE, true) } + b.append(':') + val b2 = ${ refWrite[v](cfgE, t.elementRef2.asInstanceOf[RTypeRef[v]], '{ value }.asInstanceOf[Expr[v]], sbE) } + b2.append(',') + } + if sbLen == sb.length then sb.append('}') + else sb.setCharAt(sb.length() - 1, '}') + } + + case t: LeftRightRef[?] => + if isMapKey then throw new JsonError("Union, Intersection, or Either-typed values cannot be map keys.") + t.leftRef.refType match + case '[lt] => + t.rightRef.refType match + case '[rt] => + val isEither = Expr(t.lrkind == LRKind.EITHER) + '{ + if $isEither then + $aE match + case Left(v) => + val vv = v.asInstanceOf[lt] + ${ refWrite[lt](cfgE, t.leftRef.asInstanceOf[RTypeRef[lt]], '{ vv }.asInstanceOf[Expr[lt]], sbE) } + case Right(v) => + val vv = v.asInstanceOf[rt] + ${ refWrite[rt](cfgE, t.rightRef.asInstanceOf[RTypeRef[rt]], '{ vv }.asInstanceOf[Expr[rt]], sbE) } + else + // Intersection/Union types....take your best shot! It's all we've got. No definitive info here. + val trial = new StringBuilder() + val lrSb = scala.util.Try { + val vv = $aE.asInstanceOf[rt] + ${ + refWrite[rt](cfgE, t.rightRef.asInstanceOf[RTypeRef[rt]], '{ vv }, '{ trial }) + } + } match + case Success(trialSb) => trialSb + case Failure(_) => + trial.clear + val vv = $aE.asInstanceOf[lt] + ${ + refWrite[lt](cfgE, t.leftRef.asInstanceOf[RTypeRef[lt]], '{ vv }, '{ trial }) + } + $sbE ++= lrSb + } + + case t: TryRef[?] => + if isMapKey then throw new JsonError("Try values (Succeed/Fail) cannot be map keys") + t.tryRef.refType match + case '[e] => + '{ + if $aE == null then $sbE.append("null") + else + $aE match + case Success(v) => + ${ refWrite[e](cfgE, t.tryRef.asInstanceOf[RTypeRef[e]], '{ v }.asInstanceOf[Expr[e]], sbE) } + case Failure(_) if $cfgE.tryFailureHandling == TryOption.AS_NULL => $sbE.append("null") + case Failure(_) if $cfgE.tryFailureHandling == TryOption.AS_NULL => $sbE.append("null") + case Failure(f) if $cfgE.tryFailureHandling == TryOption.THROW_EXCEPTION => + throw new JsonError("A try value was Failure with message: " + f.getMessage()) + case Failure(v) => + $sbE.append("\"Failure(") + $sbE.append(v.getMessage) + $sbE.append(")\"") + } + + case t: TupleRef[?] => + if isMapKey then throw new JsonError("Tuples cannot be map keys") + '{ + val sb = $sbE + if $aE == null then sb.append("null") + else + sb.append('[') + val sbLen = sb.length + ${ + val tupleBuf = t.tupleRefs.zipWithIndex.foldLeft(sbE) { case (accE, (ref, i)) => + ref.refType match + case '[e] => + val fieldValue = Select.unique(aE.asTerm, "_" + (i + 1)).asExprOf[e] + '{ + val acc = $accE + ${ refWrite[e](cfgE, ref.asInstanceOf[RTypeRef[e]], fieldValue, '{ acc }) } + acc.append(',') + } + } + tupleBuf + } + if sbLen == sb.length then sb.append(']') + else sb.setCharAt(sb.length() - 1, ']') + } + + case t: JavaCollectionRef[?] => + if isMapKey then throw new JsonError("Collections cannot be map keys.") + t.elementRef.refType match + case '[e] => + '{ + val sb = $sbE + if $aE == null then sb.append("null") + sb.append('[') + val sbLen = sb.length + $aE.asInstanceOf[java.util.Collection[?]].toArray.foreach { elem => + if isOkToWrite(elem, $cfgE) then + ${ refWrite[e](cfgE, t.elementRef.asInstanceOf[RTypeRef[e]], '{ elem }.asInstanceOf[Expr[e]], sbE) } + sb.append(',') + } + if sbLen == sb.length then sb.append(']') + else sb.setCharAt(sb.length() - 1, ']') + } + + case t: JavaMapRef[?] => + if isMapKey then throw new JsonError("Maps cannot be map keys.") + t.elementRef.refType match + case '[k] => + t.elementRef2.refType match + case '[v] => + '{ + val sb = $sbE + if $aE == null then sb.append("null") + sb.append('{') + val sbLen = sb.length + $aE.asInstanceOf[java.util.Map[?, ?]].asScala.foreach { case (key, value) => + if isOkToWrite(value, $cfgE) then + ${ refWrite[k](cfgE, t.elementRef.asInstanceOf[RTypeRef[k]], '{ key }.asInstanceOf[Expr[k]], sbE, true) } + sb.append(':') + ${ refWrite[v](cfgE, t.elementRef2.asInstanceOf[RTypeRef[v]], '{ value }.asInstanceOf[Expr[v]], sbE) } + sb.append(',') + } + if sbLen == sb.length then sb.append('}') + else sb.setCharAt(sb.length() - 1, '}') + } + + case t: AliasRef[?] => + t.unwrappedType.refType match + case '[e] => + refWrite[e](cfgE, t.unwrappedType.asInstanceOf[RTypeRef[e]], aE.asInstanceOf[Expr[e]], sbE) + + case t: SelfRefRef[?] => + if isMapKey then throw new JsonError("Classes or traits cannot be map keys.") + import quotes.reflect.* + val againE = classesSeen.getOrElse(t.typedName, throw new JsonError("Dangling self-reference: " + t.name)).asInstanceOf[RTypeRef[T]].expr + '{ + val again = $againE.asInstanceOf[RType[T]] + JsonWriterRT.refWriteRT[T]($cfgE, again, $aE.asInstanceOf[T], $sbE)(using scala.collection.mutable.Map.empty[TypedName, RType[?]]) + $sbE + } + + case t: EnumRef[?] => + val enumE = t.expr + val isMapKeyE = Expr(isMapKey) + '{ + if $aE == null then $sbE.append("null") + else + val enumRT = $enumE.asInstanceOf[EnumRType[T]] + val enumAsId = $cfgE.enumsAsIds match + case '*' => true + case aList: List[String] if aList.contains(enumRT.name) => true + case _ => false + if enumAsId then + val enumVal = enumRT.ordinal($aE.toString).getOrElse(throw new JsonError("Value " + $aE.toString + s" is not a valid enum value for ${enumRT.name}")) + if $isMapKeyE then + $sbE.append('"') + $sbE.append(enumVal.toString) + $sbE.append('"') + else $sbE.append(enumVal.toString) + else + $sbE.append('"') + $sbE.append($aE.toString) + $sbE.append('"') + } + + // Just handle Java classes 100% runtime since we need to leverage Java reflection entirely anyway + case t: JavaClassRef[?] => + t.refType match + case '[e] => + '{ + JsonWriterRT.refWriteRT[e]($cfgE, ${ t.expr }.asInstanceOf[RType[e]], $aE.asInstanceOf[e], $sbE)(using scala.collection.mutable.Map.empty[TypedName, RType[?]]) + } + + case t: ObjectRef => + val tname = Expr(t.name) + '{ + $sbE.append("\"" + $tname + "\"") + } + + case t: Scala2Ref[?] => + val tname = Expr(t.name) + '{ + $cfgE.undefinedFieldHandling match + case UndefinedValueOption.AS_NULL => $sbE.append("null") + case UndefinedValueOption.AS_SYMBOL => $sbE.append("\"" + $tname + "\"") + case UndefinedValueOption.THROW_EXCEPTION => throw new JsonError("Unknown/unsupported Scala 2 type " + $tname) + } + + case t: UnknownRef[?] => + val tname = Expr(t.name) + '{ + $cfgE.undefinedFieldHandling match + case UndefinedValueOption.AS_NULL => $sbE.append("null") + case UndefinedValueOption.AS_SYMBOL => $sbE.append("\"" + $tname + "\"") + case UndefinedValueOption.THROW_EXCEPTION => throw new JsonError("Unknown/unsupported type " + $tname) + } + + case t: TypeSymbolRef => + val tname = Expr(t.name) + '{ + $cfgE.undefinedFieldHandling match + case UndefinedValueOption.AS_NULL => $sbE.append("null") + case UndefinedValueOption.AS_SYMBOL => $sbE.append("\"" + $tname + "\"") + case UndefinedValueOption.THROW_EXCEPTION => throw new JsonError("Unknown/unsupported type " + $tname + ". (Class didn't fully define all its type parameters.)") + } diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index 6dce9896..f483f57d 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -18,9 +18,9 @@ object RunMe extends App: import json.* import ScalaJack.* - internal.CodePrinter.code { - sj[Record] - } + // internal.CodePrinter.code { + // sj[Record] + // } // val thing = Foo("Greg", 57) From fc0b259d7d0d7adc66a0870fb65bee0a596e69b5 Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Tue, 21 Nov 2023 01:04:10 -0600 Subject: [PATCH 28/65] another big perf bump --- TODO | 16 - TODO.txt | 66 ++++ benchmark/README.md | 87 +++--- benchmark/build.sbt | 2 +- .../src/main/scala/co.blocke/ScalaJack.scala | 4 +- .../scala/co.blocke.scalajack/ScalaJack.scala | 16 +- .../internal/TreeNode.scala | 17 ++ .../co.blocke.scalajack/json/JsonCodec.scala | 15 + .../json/writing/JsonCodecMaker.scala | 124 ++++++++ .../json/writing/JsonOutput.scala | 288 +++++++++--------- .../{JsonWriter.scala => JsonWriter.scalax} | 0 .../scala/co.blocke.scalajack/run/Play.scala | 40 +-- .../co.blocke.scalajack/run/Record.scala | 3 +- 13 files changed, 457 insertions(+), 221 deletions(-) delete mode 100644 TODO create mode 100644 TODO.txt create mode 100644 src/main/scala/co.blocke.scalajack/internal/TreeNode.scala create mode 100644 src/main/scala/co.blocke.scalajack/json/JsonCodec.scala create mode 100644 src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala rename src/main/scala/co.blocke.scalajack/json/writing/{JsonWriter.scala => JsonWriter.scalax} (100%) diff --git a/TODO b/TODO deleted file mode 100644 index 73dee13c..00000000 --- a/TODO +++ /dev/null @@ -1,16 +0,0 @@ -Path to Performance -------------------- - - - -[ ] - Create def() for each class (and maybe each collection?) found, to output json as seamlessly as possible - - -- Possibly a multi-pass generation? Once to identify all the classes and generate def fn names, and another - to actually generate the code in dependency order - -[*] - Create JsonOutput (like Jsoniter's JsonWriter, but the name conflicts w/mine). Initially continue to use - StringBuilder, but use some of Jsoniter's clever ',' handling - -[ ] - Explore dark magic Jsoniter mechanisms for all that bitwise/byte stuff to drive extreme efficiency - -[*] - In scala-reflection, explode PrimitiveRTypeRef into separate Refs, like they have for primitive RTypes \ No newline at end of file diff --git a/TODO.txt b/TODO.txt new file mode 100644 index 00000000..e2720d3e --- /dev/null +++ b/TODO.txt @@ -0,0 +1,66 @@ +Path to Performance +------------------- + + + +[ ] - Create def() for each class (and maybe each collection?) found, to output json as seamlessly as possible + + -- Possibly a multi-pass generation? Once to identify all the classes and generate def fn names, and another + to actually generate the code in dependency order + +[*] - Create JsonOutput (like Jsoniter's JsonWriter, but the name conflicts w/mine). Initially continue to use + StringBuilder, but use some of Jsoniter's clever ',' handling + +[ ] - Explore dark magic Jsoniter mechanisms for all that bitwise/byte stuff to drive extreme efficiency + +[*] - In scala-reflection, explode PrimitiveRTypeRef into separate Refs, like they have for primitive RTypes + + +LEARNINGS: Jsoniter macros to gen an list: + + ... else if (tpe <:< TypeRepr.of[List[_]]) withEncoderFor(methodKey, m, out) { (out, x) => + val tpe1 = typeArg1(tpe) + tpe1.asType match + case '[t1] => + val tx = x.asExprOf[List[t1]] + '{ + $out.writeArrayStart() + var l = $tx + while (l ne Nil) { + ${genWriteVal('{ l.head }, tpe1 :: types, isStringified, None, out)} + l = l.tail + } + $out.writeArrayEnd() + } + } + +Need to learn what "withEnocderFor()" does--specifically how $out gets passed in. + + +This is the dark magic.... It creates a Symbol and a method (DefDef) for the given function f. +Not sure how to connect/call these together yet, but in isolation, this should allow us to +generate discrete functions that output a "thing". + + def withEncoderFor[T: Type](methodKey: EncoderMethodKey, arg: Expr[T], out: Expr[JsonWriter]) + (f: (Expr[JsonWriter], Expr[T])=> Expr[Unit]): Expr[Unit] = + Apply(Ref(encodeMethodSyms.getOrElse(methodKey, { + val sym = Symbol.newMethod(Symbol.spliceOwner, "e" + encodeMethodSyms.size, + MethodType(List("x", "out"))(_ => List(TypeRepr.of[T], TypeRepr.of[JsonWriter]), _ => TypeRepr.of[Unit])) + encodeMethodSyms.update(methodKey, sym) + encodeMethodDefs += DefDef(sym, params => { + val List(List(x, out)) = params + Some(f(out.asExprOf[JsonWriter], x.asExprOf[T]).asTerm.changeOwner(sym)) + }) + sym + })), List(arg.asTerm, out.asTerm)).asExprOf[Unit] + + +Here's the EncoderMethodKey thingy: + + case class EncoderMethodKey(tpe: TypeRepr, isStringified: Boolean, discriminatorKeyValue: Option[(String, String)]) + + val encodeMethodSyms = new mutable.HashMap[EncoderMethodKey, Symbol] + val encodeMethodDefs = new mutable.ArrayBuffer[DefDef] + +Not sure if we need all this drama yet or not.... Perhaps we can use this as a simple wrapper around the TypedName. Then if + it needs to store more, the shell is already wired in. \ No newline at end of file diff --git a/benchmark/README.md b/benchmark/README.md index 43583add..36cf0915 100644 --- a/benchmark/README.md +++ b/benchmark/README.md @@ -1,12 +1,12 @@ # Performance -JSON serialization benchmarks I found in various repos often measured (IMO) silly things like how fast a parser -could handle a small list of Int. For this benchmark I used a more substantial model + JSON. It's still -not large by any measure, but it does have some nested objects and collections that make it a more +JSON serialization benchmarks I found in various repos often measured (IMO) silly things like how fast +a parser could handle a small list of Int. For this benchmark I used a more substantial model + JSON. +It's still a small model, but it does have some nested objects and collections that make it a more realistic test. -The test is run via jmh, a popular benchmarking tool. The JVM is stock--not tuned to within an inch -of its life, again to be a more realistic use case. +The test is run via jmh, a common and accepted benchmarking tool. The JVM is **stock**--not tuned to +within an inch of its life, again to be a more realistic use case. Run benchmark from the ScalaJack/benchmark directory (not the main ScalaJack project directory): ``` @@ -31,44 +31,63 @@ sbt "jmh:run -i 10 -wi 10 -f 2 -t 1 co.blocke.*" |------------------|-------|-------:|----------------:|-------------:|-------| | Jsoniter | thrpt | 20 | 2843150.452 | ± 21478.503 | ops/s | | Hand-Tooled | thrpt | 20 | 2732571.374 | ± 15129.007 | ops/s | +|**ScalaJack 8 (fast mode)** | thrpt | 20 | **2535936.655** | ± 8940.886 | ops/s | | Circe | thrpt | 20 | 1958244.437 | ± 23965.817 | ops/s | -|**ScalaJack 8 (fast mode)** | thrpt | 20 | **1736631.384** | ± 4484.721 | ops/s | +|**ScalaJack 8 (easy mode)** | thrpt | 20 | **846484.100** | ± 1123.204 | ops/s | | ZIO JSON | thrpt | 20 | 794352.301 | ± 32336.852 | ops/s | -|**ScalaJack 8 (easy mode)** | thrpt | 20 | **705318.598** | ± 19932.389 | ops/s | | Argonaut | thrpt | 20 | 690269.697 | ± 6348.882 | ops/s | | Play JSON | thrpt | 20 | 438650.022 | ± 23800.221 | ops/s | ### Interpretation -Performance for ScalaJack has been... a journey. As I've explored the population of serializers -available for Scala, of which this is a sample of popular choices, I've observed "generations" -of designs. ScalaJack 8 has grown through each successive generation. - -Focusing originally on write performance, my original design was very attuned to the internal -structure of ScalaJack. The code was clean and beautiful--and slow! I was able to get a write -score of only 30-50000, vs Circe, my original benchmark, which was just under 2 million. Not a -good showing. After an extensive overhaul and re-think, performance peaked at the 1.7 million -mark, which I was happy with. That put ScalaJack ahead of everyone else except Circe. Then -something unexpected happened... - -I tried Jsoniter, and was dumbstruck when it outperformed hand-tooled code, which I expected -would be a natural theoretical maximum write performance. How can a full serializer, having -whatever logic, be *faster* than hand-tooled code with zero logic?! This breakout level of -performance for Jsoniter continued on the read tests, being roughly 4x faster than most and -2x the level of ZIO-Json, the previous front-runner. How?! - -I observed the older serializers processed JSON to/from an AST and used conventional JSON -parsing techniques; basically fortified editions of a simple JSON parser. ZIO-Json's -impressive read performance wasn't achieved by any one thing, but rather a collection of well- -applied techniques, including *not* using an intermediate AST. So naturally I incorporated -some of ZIO-Json's approach (and a bit of their code) for JSON reading, stripped, refitted, -and adapted to ScalaJack, and read performance jumped to 633K. Nice! +Performance for ScalaJack has been a journey. ScalaJack is a mature product, and while it +was once (a long time ago) quite fast vs its competition, its performance has lagged +considerably. ScalaJack 8 changes that! + +I was sampling and testing against a collection of popular serializers for Scala util +something quite unexpected happend. When I tested Jsoniter, its performance was through +the roof! Even faster than hand-tooled code. This was a shock. I had to learn how this +worked. + +So full credit where credit is due: ScalaJack 8's reading/writing codec architecture +is heavily derived from Jsoniter. + +[Jsoniter's License](https://github.com/plokhotnyuk/jsoniter-scala/blob/af23cf65a70d48834b8fecb792cc333b23409c6f/LICENSE) + +There are a number of optimizations and design choices I elected not to bring over from +Jsoniter, and of course ScalaJack utilizes our own scala-reflection library to great effect. Jsoniter, it turns out, achieves its neck-breaking speed by going deep--very deep. They -use a lot of low level byte arrays and bitwise operators, much as you'd expect in a C program, -to improve on the standard library functions everyone else uses. It works. +use a lot of low level byte arrays and bitwise operators, much as you'd expect to see in +a C program, to improve on the standard library functions everyone else uses. It works. ScalaJack's focus is first and foremost to be frictionless--no drama to the user. ScalaJack requires zero boilerplate--you can throw any Scala object (or even a Java object) at it with no pre-preparation -and it will serialize it. For its intended use-cases, ScalaJack offers performance equal -to, or exceeding, several widely-used alternative choices. +and it will serialize it. For its intended use-cases, ScalaJack offers excellent performance, equal +to or exceeding a number of widely-used alternative choices. + +### Technical Notes + +Achieving extreme speed for ScalaJack was weeks of learning, trial, error, +and re-writes. I studied Jsoniter, Circe, and ZIO Json, and others to learn optimizations. +The tough news for anyone wanting to duplicate this kind of performance in your own code +is that there isn't one magic trick to achieve maximum performance. It's a basket +of techniques, each achieving marginal gains that add up, and you must decide when enough +is enough. Here's a partial list of learnings incorporated into ScalaJack: + +* Being careful when using .asInstanceOf[]... in fact try to avoid it wherever possible + as it messes up CPU cache harming performance. This means a lot of very careful type + management, and its why you see the RTypeRefs from scala-reflection are now all typed + in the latest version + +* Lots of specific typing. Don't make the computer think--provide detailed types wherever + you can + +* For macro-based software like this--find every opportunity to do hard work at + compile-time + +* Be mindful of what code your macros generate! You can paint by the numbers with quotes and + splices, like the documentaion and blogs suggest, and you'll get something working. When you + examine the code this produces, you may be disappointed. If it looks kludgy it will be slow--rework + your macros until the code is smooth. For fastest performance you'll actually have to generate + custom functions as shown in ScalaJack's code (look at JsonCodecMaker.scala) \ No newline at end of file diff --git a/benchmark/build.sbt b/benchmark/build.sbt index dd177f6b..9b23b7c7 100644 --- a/benchmark/build.sbt +++ b/benchmark/build.sbt @@ -36,7 +36,7 @@ lazy val benchmark = project libraryDependencies ++= Seq( "org.playframework" %% "play-json" % "3.0.1", "io.argonaut" %% "argonaut" % "6.3.9", - "co.blocke" %% "scalajack" % "f01675_unknown", // New-New + "co.blocke" %% "scalajack" % "fa32cf_unknown", "co.blocke" %% "scala-reflection" % "sj_fixes_edbef8", "dev.zio" %% "zio-json" % "0.6.1", "org.typelevel" %% "fabric-core" % "1.12.6", diff --git a/benchmark/src/main/scala/co.blocke/ScalaJack.scala b/benchmark/src/main/scala/co.blocke/ScalaJack.scala index 84faf749..16b049f9 100644 --- a/benchmark/src/main/scala/co.blocke/ScalaJack.scala +++ b/benchmark/src/main/scala/co.blocke/ScalaJack.scala @@ -16,6 +16,6 @@ object ScalaJackZ: trait ScalaJackWritingBenchmark { @Benchmark - // def writeRecordScalaJack = sj[Record].toJson(record) // 677K score - def writeRecordScalaJack = ScalaJack[Record].toJson(record) // 1.7M score <- faster + def writeRecordScalaJack = sj[Record].toJson(record) // 677K score + // def writeRecordScalaJack = ScalaJack[Record].toJson(record) // 1.7M score <- faster } diff --git a/src/main/scala/co.blocke.scalajack/ScalaJack.scala b/src/main/scala/co.blocke.scalajack/ScalaJack.scala index 16c95edd..840decd8 100644 --- a/src/main/scala/co.blocke.scalajack/ScalaJack.scala +++ b/src/main/scala/co.blocke.scalajack/ScalaJack.scala @@ -7,12 +7,21 @@ import scala.quoted.* import quoted.Quotes import json.* -case class ScalaJack[T](jsonDecoder: reading.JsonDecoder[T], jsonEncoder: (T, writing.JsonOutput, JsonConfig) => String): // extends JsonCodec[T] //with YamlCodec with MsgPackCodec +case class ScalaJack[T](jsonDecoder: reading.JsonDecoder[T], jsonEncoder: JsonCodec[T]): // extends JsonCodec[T] //with YamlCodec with MsgPackCodec def fromJson(js: String)(using cfg: JsonConfig = JsonConfig()): Either[JsonParseError, T] = jsonDecoder.decodeJson(js) def toJson(a: T)(using cfg: JsonConfig = JsonConfig()): String = - jsonEncoder(a, writing.JsonOutput(), cfg) + val out = writing.JsonOutput() + jsonEncoder.encodeValue(a, out) + out.result + +// case class ScalaJack[T](jsonDecoder: reading.JsonDecoder[T], jsonEncoder: (T, writing.JsonOutput, JsonConfig) => String): // extends JsonCodec[T] //with YamlCodec with MsgPackCodec +// def fromJson(js: String)(using cfg: JsonConfig = JsonConfig()): Either[JsonParseError, T] = +// jsonDecoder.decodeJson(js) + +// def toJson(a: T)(using cfg: JsonConfig = JsonConfig()): String = +// jsonEncoder(a, writing.JsonOutput(), cfg) // --------------------------------------- @@ -26,7 +35,8 @@ object ScalaJack: import q.reflect.* val classRef = ReflectOnType[T](quotes)(TypeRepr.of[T], true)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) val jsonDecoder = reading.JsonReader.refRead(classRef) - val jsonEncoder = writing.JsonWriter.refRead(classRef) + // val jsonEncoder = writing.JsonWriter.refRead(classRef) + val jsonEncoder = writing.JsonCodecMaker.generateCodecFor(classRef) '{ ScalaJack($jsonDecoder, $jsonEncoder) } diff --git a/src/main/scala/co.blocke.scalajack/internal/TreeNode.scala b/src/main/scala/co.blocke.scalajack/internal/TreeNode.scala new file mode 100644 index 00000000..056b01fe --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/internal/TreeNode.scala @@ -0,0 +1,17 @@ +package co.blocke.scalajack +package internal + + +case class TreeNode[P](payload: P, children: List[TreeNode[P]] = Nil): + def addChild(tn: TreeNode[P]) = this.copy(children = this.children :+ tn) + inline def hasChildren = !children.isEmpty + +object TreeNode: + + def inverted[P]( tn: TreeNode[P] ) = deapthFirst(tn).reverse + def deapthFirst[P]( tn: TreeNode[P], acc: List[TreeNode[P]] = Nil ): List[TreeNode[P]] = + tn.children.foldLeft(if acc == Nil then List(tn) else acc){ case (soFar,child) => + val nextList = soFar :+ child + if !child.hasChildren then nextList + else deapthFirst(child, nextList) + } diff --git a/src/main/scala/co.blocke.scalajack/json/JsonCodec.scala b/src/main/scala/co.blocke.scalajack/json/JsonCodec.scala new file mode 100644 index 00000000..5ce47424 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/JsonCodec.scala @@ -0,0 +1,15 @@ +package co.blocke.scalajack +package json + +import writing.* + +trait JsonCodec[A] { + + // TBD... when we're ready to tackle reading! + // def decodeValue(in: JsonReader, default: A): A = ${ + // if (cfg.encodingOnly) '{ ??? } + // else genReadVal(rootTpe :: Nil, 'default, cfg.isStringified, false, 'in) + // } + + def encodeValue(in: A, out: JsonOutput): Unit +} \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala b/src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala new file mode 100644 index 00000000..c7e8407b --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala @@ -0,0 +1,124 @@ +package co.blocke.scalajack +package json +package writing + +import co.blocke.scala_reflection.{RTypeRef, TypedName} +import co.blocke.scala_reflection.reflect.rtypeRefs.* +import scala.quoted.* +import scala.collection.mutable.{Map => MMap} +import internal.TreeNode + +object JsonCodecMaker: + + def generateCodecFor[T](ref: RTypeRef[T])(using Quotes)(using tt: Type[T]) = + import quotes.reflect.* + + // Cache generated method Symbols + an array of the generated functions (DefDef) + case class MethodKey(ref: RTypeRef[?], isStringified: Boolean) // <-- TODO: Not clear what isStringified does here... + val methodSyms = new scala.collection.mutable.HashMap[MethodKey, Symbol] + val methodDefs = new scala.collection.mutable.ArrayBuffer[DefDef] + + // Fantastic Dark Magic here--lifted from Jasoniter. Props! This thing will create a DefDef, and a Symbol to it. + // The Symbol will let you call the generated function later from other macro-generated code. The goal is to use + // generated functions to create cleaner/faster macro code than what straight quotes/splices would create unaided. + def makeFn[U: Type](methodKey: MethodKey, arg: Expr[U], out: Expr[JsonOutput])(f: (Expr[U], Expr[JsonOutput])=> Expr[Unit]): Expr[Unit] = + // Get a symbol, if one already created for this key... else make one. + Apply(Ref(methodSyms.getOrElse(methodKey, { + val sym = Symbol.newMethod(Symbol.spliceOwner, "w" + methodSyms.size, // 'w' is for Writer! + MethodType(List("in", "out"))(_ => List(TypeRepr.of[U], TypeRepr.of[JsonOutput]), _ => TypeRepr.of[Unit])) + methodSyms.update(methodKey, sym) + methodDefs += DefDef(sym, params => { + val List(List(in, out)) = params + Some(f(in.asExprOf[U], out.asExprOf[JsonOutput]).asTerm.changeOwner(sym)) + }) + sym + })), List(arg.asTerm, out.asTerm)).asExprOf[Unit] + + def genFnBody[T](r: RTypeRef[?], aE: Expr[T], out: Expr[JsonOutput])(using Quotes) = + r.refType match + case '[b] => + r match + case t: SeqRef[?] => + makeFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => + t.elementRef.refType match + case '[e] => + val tin = in.asExprOf[Seq[e]] + '{ + $out.startArray() + $tin.foreach{ i => + ${ genWriteVal('{ i }, t.elementRef.asInstanceOf[RTypeRef[e]], out) } + } + $out.endArray() + } + } + + case t: ScalaClassRef[?] => + makeFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => + val tin = in.asExprOf[b] + val body = { + val eachField = t.fields.map{ f => + f.fieldRef.refType match + case '[z] => + val fname = Expr(f.name) + val fieldValue = Select.unique(tin.asTerm, f.name).asExprOf[z] + '{ + $out.label($fname) + ${ genWriteVal(fieldValue, f.fieldRef.asInstanceOf[RTypeRef[z]], out)} + } + } + if eachField.length == 1 then eachField.head + else Expr.block(eachField.init, eachField.last) + } + '{ + $out.startObject() + $body + $out.endObject() + } + } + + def genWriteVal[T: Type]( + aE: Expr[T], + ref: RTypeRef[T], + // types: List[TypeRepr], + // isStringified: Boolean, // config option to wrap numbers, boolean, etc in "". Not needed for now... we'll see later... + // optWriteDiscriminator: Option[WriteDiscriminator], + out: Expr[JsonOutput])(using Quotes): Expr[Unit] = + val methodKey = MethodKey(ref, false) + methodSyms.get(methodKey).map{ sym => // hit cache first... then match on Ref type + Apply(Ref(sym), List(aE.asTerm, out.asTerm)).asExprOf[Unit] + }.getOrElse( + ref match + case t: BooleanRef => '{ $out.value(${aE.asExprOf[Boolean]}) } + case t: IntRef => '{ $out.value(${aE.asExprOf[Int]}) } + case t: StringRef => '{ $out.value(${aE.asExprOf[String]}) } + case _ => + println("Gen for: "+ref) + genFnBody(ref, aE, out) + ) + + //================================================================ + // You've made it this far! Ok, now we sew everything together. + // We generate a codec class and then kick off a deep traversal of + // generation from the given root ref (refer waaay back at the top of this fn...). + //================================================================ + val codecDef = '{ //FIXME: generate a type class instance using `ClassDef.apply` and `Symbol.newClass` calls after graduating from experimental API: https://www.scala-lang.org/blog/2022/06/21/scala-3.1.3-released.html + new JsonCodec[T] { + // def nullValue: A = ${genNullValue[A](rootTpe :: Nil)} // <- needed? + + // TBD... when we're ready to tackle reading! + // def decodeValue(in: JsonReader, default: A): A = ${ + // if (cfg.encodingOnly) '{ ??? } + // else genReadVal(rootTpe :: Nil, 'default, cfg.isStringified, false, 'in) + // } + + def encodeValue(in: T, out: JsonOutput): Unit = ${ + genWriteVal('in, ref, 'out) + } + } + }.asTerm + val neededDefs = + // others here??? Refer to Jsoniter file JsonCodecMaker.scala + methodDefs + val codec = Block(neededDefs.toList, codecDef).asExprOf[JsonCodec[T]] + println(s"Codec: ${codec.show}") + codec \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json/writing/JsonOutput.scala b/src/main/scala/co.blocke.scalajack/json/writing/JsonOutput.scala index a8deb55c..b125def7 100644 --- a/src/main/scala/co.blocke.scalajack/json/writing/JsonOutput.scala +++ b/src/main/scala/co.blocke.scalajack/json/writing/JsonOutput.scala @@ -3,147 +3,147 @@ package json package writing case class JsonOutput(): - val internal: StringBuilder = new StringBuilder() - - private var comma: Boolean = false - - def result = internal.result - - inline def startObject() = - internal.append('{') - comma = false - - inline def endObject() = - internal.append('}') - this - - inline def startArray() = - internal.append('[') - comma = false - - inline def endArray() = - internal.append(']') - this - - inline def maybeComma() = - if comma then - internal.append(',') - comma = false - - inline def burpNull() = - internal.append("null") - this - - inline def label( s: String ) = - maybeComma() - internal.append("\""+s+"\":") - - inline def label( s: Long ) = - maybeComma() - internal.append("\""+s+"\":") - - //----------------------- Primitive/Simple type support - - // TODO: BigDecimal, BigInt and Java equiv. - - inline def value(v: Boolean) = - internal.append(v) - comma = true - this - - inline def value(v: Byte) = - internal.append(v) - comma = true - this - - inline def value(v: Char) = - internal.append("\""+v+"\"") - comma = true - this - - inline def value(v: Double) = - internal.append(v) - comma = true - this - - inline def value(v: Float) = - internal.append(v) - comma = true - this - - inline def value(v: Int) = - internal.append(v) - comma = true - this - - inline def value(v: Long) = - internal.append(v) - comma = true - this - - inline def value(v: Short) = - internal.append(v) - comma = true - this - - inline def value(v: String) = - if v == null then internal.append("null") - else internal.append("\""+v+"\"") - comma = true - this - - inline def value(v: java.lang.Boolean) = - if v == null then internal.append("null") - else internal.append(v) - comma = true - this - - inline def value(v: java.lang.Byte) = - if v == null then internal.append("null") - else internal.append(v) - comma = true - this - - inline def value(v: java.lang.Character) = - if v == null then internal.append("null") - else internal.append("\""+v+"\"") - comma = true - this - - inline def value(v: java.lang.Double) = - if v == null then internal.append("null") - else internal.append(v) - comma = true - this - - inline def value(v: java.lang.Float) = - if v == null then internal.append("null") - else internal.append(v) - comma = true - this - - inline def value(v: java.lang.Integer) = - if v == null then internal.append("null") - else internal.append(v) - comma = true - this - - inline def value(v: java.lang.Long) = - if v == null then internal.append("null") - else internal.append(v) - comma = true - this - - inline def value(v: java.lang.Short) = - if v == null then internal.append("null") - else internal.append(v) - comma = true - this - - inline def value(v: java.lang.Number) = - if v == null then internal.append("null") - else internal.append(v) - comma = true - this - - // TODO: UUID \ No newline at end of file + val internal: StringBuilder = new StringBuilder() + + private var comma: Boolean = false + + def result = internal.result + + inline def startObject(): Unit = + maybeComma() + internal.append('{') + comma = false + + inline def endObject(): Unit = + internal.append('}') + comma = true + + inline def startArray(): Unit = + maybeComma() + internal.append('[') + comma = false + + inline def endArray(): Unit = + internal.append(']') + comma = true + + inline def maybeComma(): Unit = + if comma then internal.append(',') + comma = false + + inline def burpNull(): Unit = + internal.append("null") + + inline def label(s: String): Unit = + maybeComma() + internal.append("\"" + s + "\":") + + inline def label(s: Long): Unit = + maybeComma() + internal.append("\"" + s + "\":") + + // ----------------------- Primitive/Simple type support + + // TODO: BigDecimal, BigInt and Java equiv. + + inline def value(v: Boolean): Unit = + maybeComma() + internal.append(v) + comma = true + + inline def value(v: Byte): Unit = + maybeComma() + internal.append(v) + comma = true + + inline def value(v: Char): Unit = + maybeComma() + internal.append("\"" + v + "\"") + comma = true + + inline def value(v: Double): Unit = + maybeComma() + internal.append(v) + comma = true + + inline def value(v: Float): Unit = + maybeComma() + internal.append(v) + comma = true + + inline def value(v: Int): Unit = + maybeComma() + internal.append(v) + comma = true + + inline def value(v: Long): Unit = + maybeComma() + internal.append(v) + comma = true + + inline def value(v: Short): Unit = + maybeComma() + internal.append(v) + comma = true + + inline def value(v: String): Unit = + maybeComma() + if v == null then internal.append("null") + else internal.append("\"" + v + "\"") + comma = true + + inline def value(v: java.lang.Boolean): Unit = + maybeComma() + if v == null then internal.append("null") + else internal.append(v) + comma = true + + inline def value(v: java.lang.Byte): Unit = + maybeComma() + if v == null then internal.append("null") + else internal.append(v) + comma = true + + inline def value(v: java.lang.Character): Unit = + maybeComma() + if v == null then internal.append("null") + else internal.append("\"" + v + "\"") + comma = true + + inline def value(v: java.lang.Double): Unit = + maybeComma() + if v == null then internal.append("null") + else internal.append(v) + comma = true + + inline def value(v: java.lang.Float): Unit = + maybeComma() + if v == null then internal.append("null") + else internal.append(v) + comma = true + + inline def value(v: java.lang.Integer): Unit = + maybeComma() + if v == null then internal.append("null") + else internal.append(v) + comma = true + + inline def value(v: java.lang.Long): Unit = + maybeComma() + if v == null then internal.append("null") + else internal.append(v) + comma = true + + inline def value(v: java.lang.Short): Unit = + maybeComma() + if v == null then internal.append("null") + else internal.append(v) + comma = true + + inline def value(v: java.lang.Number): Unit = + maybeComma() + if v == null then internal.append("null") + else internal.append(v) + comma = true + + // TODO: UUID diff --git a/src/main/scala/co.blocke.scalajack/json/writing/JsonWriter.scala b/src/main/scala/co.blocke.scalajack/json/writing/JsonWriter.scalax similarity index 100% rename from src/main/scala/co.blocke.scalajack/json/writing/JsonWriter.scala rename to src/main/scala/co.blocke.scalajack/json/writing/JsonWriter.scalax diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index f483f57d..1d5de457 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -18,35 +18,35 @@ object RunMe extends App: import json.* import ScalaJack.* - // internal.CodePrinter.code { + // co.blocke.scalajack.internal.CodePrinter.code { // sj[Record] // } - // val thing = Foo("Greg", 57) - + val v = Foo("Hey", "Boo") + // println(sj[Foo].toJson(v)) println(sj[Record].toJson(record)) - println("------") - println(sj[Record].fromJson(jsData)) - // println(sj[Foo].fromJson("""{"name":"Greg","age":57}""")) - - /* - val jjs = """{"name":"Greg","age":57}""" - // println(ScalaJack.read[Foo](jjs)) - */ - // val record = sj[Record].fromJson(jsData) match - // case Right(r) => r - // case Left(t: Throwable) => throw t + // println(sj[Record].toJson(record)) - // println(record) // println("------") - // println(sj[Record].toJson(record)) - // val r2 = ScalaJack.read[Record](jsData) - // println(ScalaJack.write(record)) + // println(sj[Record].fromJson(jsData)) + + // import internal.* + + // val root = TreeNode("1A", List(TreeNode("2A", List(TreeNode("3A", Nil))), TreeNode("2B", List(TreeNode("3B", Nil))), TreeNode("2C", List(TreeNode("3C", Nil))))) + + // val m = Map( + // "1A" -> "Report", + // "2A" -> "Person", + // "2B" -> "Seq[Friend]", + // "2C" -> "Seq[Pet]", + // "3A" -> "Address", + // "3B" -> "Friend", + // "3C" -> "Pet" + // ) - // implicit val z: json.sj[Record] = ScalaJack.inspect[Record] - // println(sj[Record].decodeJson(jsData)) + // println(TreeNode.inverted(root).map(p => m(p.payload))) catch { case t: Throwable => diff --git a/src/main/scala/co.blocke.scalajack/run/Record.scala b/src/main/scala/co.blocke.scalajack/run/Record.scala index 9d3c4ee5..fc11e620 100644 --- a/src/main/scala/co.blocke.scalajack/run/Record.scala +++ b/src/main/scala/co.blocke.scalajack/run/Record.scala @@ -37,7 +37,8 @@ case class Record( ) // case class Foo(name: String, maybe: Option[Int], age: Int, expected: String = "nada", gotit: Option[Int] = Some(5)) -case class Foo(name: String, age: Int, expected: String = "nada") +case class Foo(name: String, expected: String = "nada") +// case class Foo(name: String, age: Int, expected: String = "nada") val jsData = """{ From 23e2788b64ade89bdea84243e8b8c6c7ad1aac3f Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Tue, 21 Nov 2023 10:27:48 -0600 Subject: [PATCH 29/65] final write optimization --- .DS_Store | Bin 0 -> 6148 bytes benchmark/README.md | 8 +- benchmark/Writing Performance.png | Bin 0 -> 78787 bytes benchmark/build.sbt | 2 +- doc/.DS_Store | Bin 0 -> 6148 bytes .../scala/co.blocke.scalajack/ScalaJack.scala | 50 +--- .../internal/TreeNode.scala | 19 +- .../co.blocke.scalajack/json/JsonCodec.scala | 18 +- .../json/writing/JsonCodecMaker.scala | 227 ++++++++++-------- .../json/writing/JsonOutput.scala | 4 + 10 files changed, 154 insertions(+), 174 deletions(-) create mode 100644 .DS_Store create mode 100644 benchmark/Writing Performance.png create mode 100644 doc/.DS_Store diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..0e082176d842e2c9234416251cd903c2e8a99a67 GIT binary patch literal 6148 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 ecIAu8EPrS-97dYr<;Q(cIPn<;08L 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 literal 0 HcmV?d00001 diff --git a/benchmark/build.sbt b/benchmark/build.sbt index 9b23b7c7..c5af1411 100644 --- a/benchmark/build.sbt +++ b/benchmark/build.sbt @@ -36,7 +36,7 @@ lazy val benchmark = project libraryDependencies ++= Seq( "org.playframework" %% "play-json" % "3.0.1", "io.argonaut" %% "argonaut" % "6.3.9", - "co.blocke" %% "scalajack" % "fa32cf_unknown", + "co.blocke" %% "scalajack" % "fc0b25_unknown", "co.blocke" %% "scala-reflection" % "sj_fixes_edbef8", "dev.zio" %% "zio-json" % "0.6.1", "org.typelevel" %% "fabric-core" % "1.12.6", diff --git a/doc/.DS_Store b/doc/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0 String): // extends JsonCodec[T] //with YamlCodec with MsgPackCodec -// def fromJson(js: String)(using cfg: JsonConfig = JsonConfig()): Either[JsonParseError, T] = -// jsonDecoder.decodeJson(js) - -// def toJson(a: T)(using cfg: JsonConfig = JsonConfig()): String = -// jsonEncoder(a, writing.JsonOutput(), cfg) - // --------------------------------------- object ScalaJack: @@ -35,7 +28,6 @@ object ScalaJack: import q.reflect.* val classRef = ReflectOnType[T](quotes)(TypeRepr.of[T], true)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) val jsonDecoder = reading.JsonReader.refRead(classRef) - // val jsonEncoder = writing.JsonWriter.refRead(classRef) val jsonEncoder = writing.JsonCodecMaker.generateCodecFor(classRef) '{ ScalaJack($jsonDecoder, $jsonEncoder) } @@ -48,25 +40,6 @@ object ScalaJack: // --------------------------------------------------------------------- - inline def inspect[T]: sj[T] = ${ inspectImpl[T] } - - def inspectImpl[T](using q: Quotes, tt: Type[T]): Expr[sj[T]] = - import q.reflect.* - val classRef = ReflectOnType[T](quotes)(TypeRepr.of[T], true)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) - JsonReader.refRead[T](classRef) - - inline def write[T](a: T)(using cfg: JsonConfig = JsonConfig()): String = ${ writeImpl[T]('a, 'cfg) } - - def writeImpl[T](aE: Expr[T], cfg: Expr[JsonConfig])(using q: Quotes, tt: Type[T]): Expr[String] = - import q.reflect.* - val ref = ReflectOnType(q)(TypeRepr.of[T], true)(using Map.empty[TypedName, Boolean]).asInstanceOf[RTypeRef[T]] - val classesSeen = Map.empty[TypedName, RTypeRef[?]] - - val sbE = '{ new StringBuilder() } - '{ ${ JsonWriter.refWrite[T](cfg, ref, aE, sbE)(using classesSeen) }.result } - - // --------------------------------------------------------------------- - inline def read[T](js: String)(using cfg: JsonConfig = JsonConfig()): Either[ParseError, T] = ${ readImpl[T]('js, 'cfg) } def readImpl[T: Type](js: Expr[String], cfg: Expr[JsonConfig])(using q: Quotes): Expr[Either[ParseError, T]] = @@ -77,22 +50,3 @@ object ScalaJack: $decoder.decodeJson($js) } */ - -/* - - implicit val codec: sj[Record] = ScalaJack.inspect[Record] - - (optional given cfg) - - sj[Record].read(js) - sj[Record].readYml(yml) - sj[Record].readMP(msgPack) - - sj[Record].write(thing) - sj[Record].writeYml(thing) - sj[Record].writeMP(thing) - - - trait sj[A] extends JSModule with YMLModule with MPModule: - ... - */ diff --git a/src/main/scala/co.blocke.scalajack/internal/TreeNode.scala b/src/main/scala/co.blocke.scalajack/internal/TreeNode.scala index 056b01fe..25ecb8d4 100644 --- a/src/main/scala/co.blocke.scalajack/internal/TreeNode.scala +++ b/src/main/scala/co.blocke.scalajack/internal/TreeNode.scala @@ -1,17 +1,16 @@ package co.blocke.scalajack package internal - case class TreeNode[P](payload: P, children: List[TreeNode[P]] = Nil): - def addChild(tn: TreeNode[P]) = this.copy(children = this.children :+ tn) - inline def hasChildren = !children.isEmpty + def addChild(tn: TreeNode[P]) = this.copy(children = this.children :+ tn) + inline def hasChildren = !children.isEmpty object TreeNode: - def inverted[P]( tn: TreeNode[P] ) = deapthFirst(tn).reverse - def deapthFirst[P]( tn: TreeNode[P], acc: List[TreeNode[P]] = Nil ): List[TreeNode[P]] = - tn.children.foldLeft(if acc == Nil then List(tn) else acc){ case (soFar,child) => - val nextList = soFar :+ child - if !child.hasChildren then nextList - else deapthFirst(child, nextList) - } + def inverted[P](tn: TreeNode[P]) = deapthFirst(tn).reverse + def deapthFirst[P](tn: TreeNode[P], acc: List[TreeNode[P]] = Nil): List[TreeNode[P]] = + tn.children.foldLeft(if acc == Nil then List(tn) else acc) { case (soFar, child) => + val nextList = soFar :+ child + if !child.hasChildren then nextList + else deapthFirst(child, nextList) + } diff --git a/src/main/scala/co.blocke.scalajack/json/JsonCodec.scala b/src/main/scala/co.blocke.scalajack/json/JsonCodec.scala index 5ce47424..a8d07bce 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonCodec.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonCodec.scala @@ -4,12 +4,12 @@ package json import writing.* trait JsonCodec[A] { - - // TBD... when we're ready to tackle reading! - // def decodeValue(in: JsonReader, default: A): A = ${ - // if (cfg.encodingOnly) '{ ??? } - // else genReadVal(rootTpe :: Nil, 'default, cfg.isStringified, false, 'in) - // } - - def encodeValue(in: A, out: JsonOutput): Unit -} \ No newline at end of file + + // TBD... when we're ready to tackle reading! + // def decodeValue(in: JsonReader, default: A): A = ${ + // if (cfg.encodingOnly) '{ ??? } + // else genReadVal(rootTpe :: Nil, 'default, cfg.isStringified, false, 'in) + // } + + def encodeValue(in: A, out: JsonOutput): Unit +} diff --git a/src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala b/src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala index c7e8407b..0d8e3d5d 100644 --- a/src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala +++ b/src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala @@ -5,120 +5,137 @@ package writing import co.blocke.scala_reflection.{RTypeRef, TypedName} import co.blocke.scala_reflection.reflect.rtypeRefs.* import scala.quoted.* -import scala.collection.mutable.{Map => MMap} +import scala.collection.mutable.Map as MMap import internal.TreeNode object JsonCodecMaker: - def generateCodecFor[T](ref: RTypeRef[T])(using Quotes)(using tt: Type[T]) = - import quotes.reflect.* + def generateCodecFor[T](ref: RTypeRef[T])(using Quotes)(using tt: Type[T]) = + import quotes.reflect.* - // Cache generated method Symbols + an array of the generated functions (DefDef) - case class MethodKey(ref: RTypeRef[?], isStringified: Boolean) // <-- TODO: Not clear what isStringified does here... - val methodSyms = new scala.collection.mutable.HashMap[MethodKey, Symbol] - val methodDefs = new scala.collection.mutable.ArrayBuffer[DefDef] + // Cache generated method Symbols + an array of the generated functions (DefDef) + case class MethodKey(ref: RTypeRef[?], isStringified: Boolean) // <-- TODO: Not clear what isStringified does here... + val methodSyms = new scala.collection.mutable.HashMap[MethodKey, Symbol] + val methodDefs = new scala.collection.mutable.ArrayBuffer[DefDef] - // Fantastic Dark Magic here--lifted from Jasoniter. Props! This thing will create a DefDef, and a Symbol to it. - // The Symbol will let you call the generated function later from other macro-generated code. The goal is to use - // generated functions to create cleaner/faster macro code than what straight quotes/splices would create unaided. - def makeFn[U: Type](methodKey: MethodKey, arg: Expr[U], out: Expr[JsonOutput])(f: (Expr[U], Expr[JsonOutput])=> Expr[Unit]): Expr[Unit] = - // Get a symbol, if one already created for this key... else make one. - Apply(Ref(methodSyms.getOrElse(methodKey, { - val sym = Symbol.newMethod(Symbol.spliceOwner, "w" + methodSyms.size, // 'w' is for Writer! - MethodType(List("in", "out"))(_ => List(TypeRepr.of[U], TypeRepr.of[JsonOutput]), _ => TypeRepr.of[Unit])) - methodSyms.update(methodKey, sym) - methodDefs += DefDef(sym, params => { - val List(List(in, out)) = params - Some(f(in.asExprOf[U], out.asExprOf[JsonOutput]).asTerm.changeOwner(sym)) - }) - sym - })), List(arg.asTerm, out.asTerm)).asExprOf[Unit] + // Fantastic Dark Magic here--lifted from Jasoniter. Props! This thing will create a DefDef, and a Symbol to it. + // The Symbol will let you call the generated function later from other macro-generated code. The goal is to use + // generated functions to create cleaner/faster macro code than what straight quotes/splices would create unaided. + def makeFn[U: Type](methodKey: MethodKey, arg: Expr[U], out: Expr[JsonOutput])(f: (Expr[U], Expr[JsonOutput]) => Expr[Unit]): Expr[Unit] = + // Get a symbol, if one already created for this key... else make one. + Apply( + Ref( + methodSyms.getOrElse( + methodKey, { + val sym = Symbol.newMethod( + Symbol.spliceOwner, + "w" + methodSyms.size, // 'w' is for Writer! + MethodType(List("in", "out"))(_ => List(TypeRepr.of[U], TypeRepr.of[JsonOutput]), _ => TypeRepr.of[Unit]) + ) + methodSyms.update(methodKey, sym) + methodDefs += DefDef( + sym, + params => { + val List(List(in, out)) = params + Some(f(in.asExprOf[U], out.asExprOf[JsonOutput]).asTerm.changeOwner(sym)) + } + ) + sym + } + ) + ), + List(arg.asTerm, out.asTerm) + ).asExprOf[Unit] - def genFnBody[T](r: RTypeRef[?], aE: Expr[T], out: Expr[JsonOutput])(using Quotes) = - r.refType match - case '[b] => - r match - case t: SeqRef[?] => - makeFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => - t.elementRef.refType match - case '[e] => - val tin = in.asExprOf[Seq[e]] - '{ - $out.startArray() - $tin.foreach{ i => - ${ genWriteVal('{ i }, t.elementRef.asInstanceOf[RTypeRef[e]], out) } - } - $out.endArray() - } - } + def genFnBody[T](r: RTypeRef[?], aE: Expr[T], out: Expr[JsonOutput])(using Quotes) = + r.refType match + case '[b] => + r match + case t: SeqRef[?] => + makeFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => + t.elementRef.refType match + case '[e] => + val tin = in.asExprOf[Seq[e]] + '{ + $out.startArray() + $tin.foreach { i => + ${ genWriteVal('{ i }, t.elementRef.asInstanceOf[RTypeRef[e]], out) } + } + $out.endArray() + } + } - case t: ScalaClassRef[?] => - makeFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => - val tin = in.asExprOf[b] - val body = { - val eachField = t.fields.map{ f => - f.fieldRef.refType match - case '[z] => - val fname = Expr(f.name) - val fieldValue = Select.unique(tin.asTerm, f.name).asExprOf[z] - '{ - $out.label($fname) - ${ genWriteVal(fieldValue, f.fieldRef.asInstanceOf[RTypeRef[z]], out)} - } - } - if eachField.length == 1 then eachField.head - else Expr.block(eachField.init, eachField.last) - } - '{ - $out.startObject() - $body - $out.endObject() - } - } + case t: ScalaClassRef[?] => + makeFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => + val tin = in.asExprOf[b] + val body = { + val eachField = t.fields.map { f => + f.fieldRef.refType match + case '[z] => + val fname = Expr(f.name) + val fieldValue = Select.unique(tin.asTerm, f.name).asExprOf[z] + '{ + $out.label($fname) + ${ genWriteVal(fieldValue, f.fieldRef.asInstanceOf[RTypeRef[z]], out) } + } + } + if eachField.length == 1 then eachField.head + else Expr.block(eachField.init, eachField.last) + } + '{ + $out.startObject() + $body + $out.endObject() + } + } - def genWriteVal[T: Type]( - aE: Expr[T], - ref: RTypeRef[T], - // types: List[TypeRepr], - // isStringified: Boolean, // config option to wrap numbers, boolean, etc in "". Not needed for now... we'll see later... - // optWriteDiscriminator: Option[WriteDiscriminator], - out: Expr[JsonOutput])(using Quotes): Expr[Unit] = - val methodKey = MethodKey(ref, false) - methodSyms.get(methodKey).map{ sym => // hit cache first... then match on Ref type - Apply(Ref(sym), List(aE.asTerm, out.asTerm)).asExprOf[Unit] - }.getOrElse( - ref match - case t: BooleanRef => '{ $out.value(${aE.asExprOf[Boolean]}) } - case t: IntRef => '{ $out.value(${aE.asExprOf[Int]}) } - case t: StringRef => '{ $out.value(${aE.asExprOf[String]}) } - case _ => - println("Gen for: "+ref) - genFnBody(ref, aE, out) - ) + def genWriteVal[T: Type]( + aE: Expr[T], + ref: RTypeRef[T], + // types: List[TypeRepr], + // isStringified: Boolean, // config option to wrap numbers, boolean, etc in "". Not needed for now... we'll see later... + // optWriteDiscriminator: Option[WriteDiscriminator], + out: Expr[JsonOutput] + )(using Quotes): Expr[Unit] = + val methodKey = MethodKey(ref, false) + methodSyms + .get(methodKey) + .map { sym => // hit cache first... then match on Ref type + Apply(Ref(sym), List(aE.asTerm, out.asTerm)).asExprOf[Unit] + } + .getOrElse( + ref match + case t: BooleanRef => '{ $out.value(${ aE.asExprOf[Boolean] }) } + case t: IntRef => '{ $out.value(${ aE.asExprOf[Int] }) } + case t: StringRef => '{ $out.value(${ aE.asExprOf[String] }) } + case _ => + println("Gen for: " + ref) + genFnBody(ref, aE, out) + ) - //================================================================ - // You've made it this far! Ok, now we sew everything together. - // We generate a codec class and then kick off a deep traversal of - // generation from the given root ref (refer waaay back at the top of this fn...). - //================================================================ - val codecDef = '{ //FIXME: generate a type class instance using `ClassDef.apply` and `Symbol.newClass` calls after graduating from experimental API: https://www.scala-lang.org/blog/2022/06/21/scala-3.1.3-released.html - new JsonCodec[T] { - // def nullValue: A = ${genNullValue[A](rootTpe :: Nil)} // <- needed? + // ================================================================ + // You've made it this far! Ok, now we sew everything together. + // We generate a codec class and then kick off a deep traversal of + // generation from the given root ref (refer waaay back at the top of this fn...). + // ================================================================ + val codecDef = '{ // FIXME: generate a type class instance using `ClassDef.apply` and `Symbol.newClass` calls after graduating from experimental API: https://www.scala-lang.org/blog/2022/06/21/scala-3.1.3-released.html + new JsonCodec[T] { + // def nullValue: A = ${genNullValue[A](rootTpe :: Nil)} // <- needed? - // TBD... when we're ready to tackle reading! - // def decodeValue(in: JsonReader, default: A): A = ${ - // if (cfg.encodingOnly) '{ ??? } - // else genReadVal(rootTpe :: Nil, 'default, cfg.isStringified, false, 'in) - // } + // TBD... when we're ready to tackle reading! + // def decodeValue(in: JsonReader, default: A): A = ${ + // if (cfg.encodingOnly) '{ ??? } + // else genReadVal(rootTpe :: Nil, 'default, cfg.isStringified, false, 'in) + // } - def encodeValue(in: T, out: JsonOutput): Unit = ${ - genWriteVal('in, ref, 'out) - } - } - }.asTerm - val neededDefs = - // others here??? Refer to Jsoniter file JsonCodecMaker.scala - methodDefs - val codec = Block(neededDefs.toList, codecDef).asExprOf[JsonCodec[T]] - println(s"Codec: ${codec.show}") - codec \ No newline at end of file + def encodeValue(in: T, out: JsonOutput): Unit = ${ + genWriteVal('in, ref, 'out) + } + } + }.asTerm + val neededDefs = + // others here??? Refer to Jsoniter file JsonCodecMaker.scala + methodDefs + val codec = Block(neededDefs.toList, codecDef).asExprOf[JsonCodec[T]] + println(s"Codec: ${codec.show}") + codec diff --git a/src/main/scala/co.blocke.scalajack/json/writing/JsonOutput.scala b/src/main/scala/co.blocke.scalajack/json/writing/JsonOutput.scala index b125def7..dcf0c510 100644 --- a/src/main/scala/co.blocke.scalajack/json/writing/JsonOutput.scala +++ b/src/main/scala/co.blocke.scalajack/json/writing/JsonOutput.scala @@ -9,6 +9,10 @@ case class JsonOutput(): def result = internal.result + def clear() = + internal.clear() + this + inline def startObject(): Unit = maybeComma() internal.append('{') From c9d18e30590f432337dbdc88a35040dc2653eafb Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Tue, 21 Nov 2023 10:28:46 -0600 Subject: [PATCH 30/65] Update README.md --- benchmark/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/benchmark/README.md b/benchmark/README.md index 42075509..a4c65bf4 100644 --- a/benchmark/README.md +++ b/benchmark/README.md @@ -27,7 +27,7 @@ sbt "jmh:run -i 10 -wi 10 -f 2 -t 1 co.blocke.*" ## Writing Performance: -![image info](./Writing Performance.png) +![image info](Writing Performance.png) | Benchmark | Mode | Count | Score | Error | Units | |------------------|-------|-------:|----------------:|-------------:|-------| @@ -96,4 +96,4 @@ is enough. Here's a partial list of learnings incorporated into ScalaJack: splices, like the documentaion and blogs suggest, and you'll get something working. When you examine the code this produces, you may be disappointed. If it looks kludgy it will be slow--rework your macros until the code is smooth. For fastest performance you'll actually have to generate - custom functions as shown in ScalaJack's code (look at JsonCodecMaker.scala) \ No newline at end of file + custom functions as shown in ScalaJack's code (look at JsonCodecMaker.scala) From 79e3546c9e2a039af9da96f2e83765bc4d694d0a Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Tue, 21 Nov 2023 10:30:20 -0600 Subject: [PATCH 31/65] Update README.md --- benchmark/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmark/README.md b/benchmark/README.md index a4c65bf4..9753fc6e 100644 --- a/benchmark/README.md +++ b/benchmark/README.md @@ -27,7 +27,7 @@ sbt "jmh:run -i 10 -wi 10 -f 2 -t 1 co.blocke.*" ## Writing Performance: -![image info](Writing Performance.png) +![image info](benchmark/Writing Performance.png) | Benchmark | Mode | Count | Score | Error | Units | |------------------|-------|-------:|----------------:|-------------:|-------| From b13979c1ec8c6493d2a8eaaa52ab9af09b442ba1 Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Tue, 21 Nov 2023 10:31:36 -0600 Subject: [PATCH 32/65] readme fidgeting --- benchmark/README.md | 2 +- ...iting Performance.png => WritingPerformance.png} | Bin 2 files changed, 1 insertion(+), 1 deletion(-) rename benchmark/{Writing Performance.png => WritingPerformance.png} (100%) diff --git a/benchmark/README.md b/benchmark/README.md index 9753fc6e..5405fb5f 100644 --- a/benchmark/README.md +++ b/benchmark/README.md @@ -27,7 +27,7 @@ sbt "jmh:run -i 10 -wi 10 -f 2 -t 1 co.blocke.*" ## Writing Performance: -![image info](benchmark/Writing Performance.png) +![image info](./WritingPerformance.png) | Benchmark | Mode | Count | Score | Error | Units | |------------------|-------|-------:|----------------:|-------------:|-------| diff --git a/benchmark/Writing Performance.png b/benchmark/WritingPerformance.png similarity index 100% rename from benchmark/Writing Performance.png rename to benchmark/WritingPerformance.png From bb41444cb2988690cfb9bd94a011d862f4cdb95c Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Tue, 28 Nov 2023 00:33:32 -0600 Subject: [PATCH 33/65] JsonConfig initial working --- .DS_Store | Bin 6148 -> 0 bytes .gitignore | 1 + TODO.txt | 66 +--- .../src/main/scala/co.blocke/ScalaJack.scala | 4 +- .../scala/co.blocke.scalajack/ScalaJack.scala | 22 +- .../internal/TreeNode.scala | 16 - .../co.blocke.scalajack/json/JsonCodec.scalax | 13 - .../co.blocke.scalajack/json/JsonConfig.scala | 160 +++++++- .../co.blocke.scalajack/json/JsonError.scala | 5 +- .../co.blocke.scalajack/json/package.scala | 14 + .../json/reading/JsonReader.scala | 11 + .../json/writing/JsonCodecMaker.scala | 210 ++++++++++- .../json/writing/JsonOutput.scala | 237 +++++++++++- .../json/writing/JsonWriter.scalax | 111 ------ .../json/writing/JsonWriter2.scalax | 157 -------- .../json/writing/JsonWriter_old.scalax | 56 +-- .../scala/co.blocke.scalajack/run/Play.scala | 48 ++- .../co.blocke.scalajack/run/Record.scala | 6 +- .../json/primitives/JavaPrim.scala | 312 +++++++++++++++ .../json/primitives/Model.scala | 22 +- .../json/primitives/ScalaPrim.scala | 63 ++-- .../json/primitives/Simple.scala | 357 ++++++++++++++++++ 22 files changed, 1386 insertions(+), 505 deletions(-) delete mode 100644 .DS_Store delete mode 100644 src/main/scala/co.blocke.scalajack/internal/TreeNode.scala delete mode 100644 src/main/scala/co.blocke.scalajack/json/JsonCodec.scalax delete mode 100644 src/main/scala/co.blocke.scalajack/json/writing/JsonWriter.scalax delete mode 100644 src/main/scala/co.blocke.scalajack/json/writing/JsonWriter2.scalax create mode 100644 src/test/scala/co.blocke.scalajack/json/primitives/JavaPrim.scala create mode 100644 src/test/scala/co.blocke.scalajack/json/primitives/Simple.scala diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 0e082176d842e2c9234416251cd903c2e8a99a67..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK!AiqG5S?wSCKRCu1&<3}3u=P}@e*qN0V8@)sYz2bG|fuW+CwSitUu(J_&v_- zZpG4iRIJRv?3>-0+0DL$-3$PT);K-_r~!b3N?3BSSs~;nU6PU=sUQkH$0H0Ngdy~! z$x<{A{6z-n-MJ7!4>IV&m-m-HMX8F=`xZT=Nj^F}d>5s1Wn**8*>bARUGS(T!6+P! z^Hw;#q28rZX*9RP=ql+=I<=iMm5stA>kV~668A9V_BzR8HEF4F7Uw!QFb>Cc+)iz8 zI`zC}U3!h^+Ep;O?Or)AsX9A42_v9mburV&0V zg{^Qu2g58?*#mlM;xfdbpgT{RuSLl$%m6dM46H8$_B3;<>$?Sha%O-T_#p=9e2}Pw zzQw|zK02_`B>*D*MruKuY6;4b7JZ9_LG+*qlZt3kg)K3JNyl+%<9v&SL6Z)`79YZ{ zENq1$^y)aj)Zrj}gWNI$%)lZ8Wz((E{eS*_{lA#RJ!XIz_*V>wN;_z`a7*@XUD_Po vwG#Cnm4xC7gP$pA=&KlG=_=ks)q>-a3`E~zVGu`9_(wp~zzs9-s| - val tpe1 = typeArg1(tpe) - tpe1.asType match - case '[t1] => - val tx = x.asExprOf[List[t1]] - '{ - $out.writeArrayStart() - var l = $tx - while (l ne Nil) { - ${genWriteVal('{ l.head }, tpe1 :: types, isStringified, None, out)} - l = l.tail - } - $out.writeArrayEnd() - } - } - -Need to learn what "withEnocderFor()" does--specifically how $out gets passed in. - - -This is the dark magic.... It creates a Symbol and a method (DefDef) for the given function f. -Not sure how to connect/call these together yet, but in isolation, this should allow us to -generate discrete functions that output a "thing". - - def withEncoderFor[T: Type](methodKey: EncoderMethodKey, arg: Expr[T], out: Expr[JsonWriter]) - (f: (Expr[JsonWriter], Expr[T])=> Expr[Unit]): Expr[Unit] = - Apply(Ref(encodeMethodSyms.getOrElse(methodKey, { - val sym = Symbol.newMethod(Symbol.spliceOwner, "e" + encodeMethodSyms.size, - MethodType(List("x", "out"))(_ => List(TypeRepr.of[T], TypeRepr.of[JsonWriter]), _ => TypeRepr.of[Unit])) - encodeMethodSyms.update(methodKey, sym) - encodeMethodDefs += DefDef(sym, params => { - val List(List(x, out)) = params - Some(f(out.asExprOf[JsonWriter], x.asExprOf[T]).asTerm.changeOwner(sym)) - }) - sym - })), List(arg.asTerm, out.asTerm)).asExprOf[Unit] - - -Here's the EncoderMethodKey thingy: - - case class EncoderMethodKey(tpe: TypeRepr, isStringified: Boolean, discriminatorKeyValue: Option[(String, String)]) - - val encodeMethodSyms = new mutable.HashMap[EncoderMethodKey, Symbol] - val encodeMethodDefs = new mutable.ArrayBuffer[DefDef] - -Not sure if we need all this drama yet or not.... Perhaps we can use this as a simple wrapper around the TypedName. Then if - it needs to store more, the shell is already wired in. \ No newline at end of file +[ ] - Study Jsoniter and how it propagates its write config between compile-time and run-time (usage too) + Ideally we'd love to be able to see cfg at compile-time and geneate accordingly.... Right now, + ScalaJack's config is purely runtime, which is less convenient for us... \ No newline at end of file diff --git a/benchmark/src/main/scala/co.blocke/ScalaJack.scala b/benchmark/src/main/scala/co.blocke/ScalaJack.scala index 16b049f9..84faf749 100644 --- a/benchmark/src/main/scala/co.blocke/ScalaJack.scala +++ b/benchmark/src/main/scala/co.blocke/ScalaJack.scala @@ -16,6 +16,6 @@ object ScalaJackZ: trait ScalaJackWritingBenchmark { @Benchmark - def writeRecordScalaJack = sj[Record].toJson(record) // 677K score - // def writeRecordScalaJack = ScalaJack[Record].toJson(record) // 1.7M score <- faster + // def writeRecordScalaJack = sj[Record].toJson(record) // 677K score + def writeRecordScalaJack = ScalaJack[Record].toJson(record) // 1.7M score <- faster } diff --git a/src/main/scala/co.blocke.scalajack/ScalaJack.scala b/src/main/scala/co.blocke.scalajack/ScalaJack.scala index b2e34006..cebe8c02 100644 --- a/src/main/scala/co.blocke.scalajack/ScalaJack.scala +++ b/src/main/scala/co.blocke.scalajack/ScalaJack.scala @@ -8,11 +8,11 @@ import quoted.Quotes import json.* case class ScalaJack[T](jsonDecoder: reading.JsonDecoder[T], jsonEncoder: JsonCodec[T]): // extends JsonCodec[T] //with YamlCodec with MsgPackCodec - def fromJson(js: String)(using cfg: JsonConfig = JsonConfig()): Either[JsonParseError, T] = + def fromJson(js: String): Either[JsonParseError, T] = jsonDecoder.decodeJson(js) val out = writing.JsonOutput() // let's clear & re-use JsonOutput--avoid re-allocating all the internal buffer space - def toJson(a: T)(using cfg: JsonConfig = JsonConfig()): String = + def toJson(a: T): String = jsonEncoder.encodeValue(a, out.clear()) out.result @@ -22,14 +22,24 @@ object ScalaJack: def apply[A](implicit a: ScalaJack[A]): ScalaJack[A] = a + // ----- Use default JsonConfig inline def sj[T]: ScalaJack[T] = ${ sjImpl[T] } - - def sjImpl[T](using q: Quotes, tt: Type[T]): Expr[ScalaJack[T]] = - import q.reflect.* + def sjImpl[T: Type](using Quotes): Expr[ScalaJack[T]] = + import quotes.reflect.* val classRef = ReflectOnType[T](quotes)(TypeRepr.of[T], true)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) val jsonDecoder = reading.JsonReader.refRead(classRef) - val jsonEncoder = writing.JsonCodecMaker.generateCodecFor(classRef) + val jsonEncoder = writing.JsonCodecMaker.generateCodecFor(classRef, JsonConfig) + + '{ ScalaJack($jsonDecoder, $jsonEncoder) } + // ----- Use given JsonConfig + inline def sj[T](inline cfg: JsonConfig): ScalaJack[T] = ${ sjImplWithConfig[T]('cfg) } + def sjImplWithConfig[T: Type](cfgE: Expr[JsonConfig])(using Quotes): Expr[ScalaJack[T]] = + import quotes.reflect.* + val cfg = summon[FromExpr[JsonConfig]].unapply(cfgE) + val classRef = ReflectOnType[T](quotes)(TypeRepr.of[T], true)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) + val jsonDecoder = reading.JsonReader.refRead(classRef) + val jsonEncoder = writing.JsonCodecMaker.generateCodecFor(classRef, cfg.getOrElse(JsonConfig)) '{ ScalaJack($jsonDecoder, $jsonEncoder) } // refRead[T](classRef) diff --git a/src/main/scala/co.blocke.scalajack/internal/TreeNode.scala b/src/main/scala/co.blocke.scalajack/internal/TreeNode.scala deleted file mode 100644 index 25ecb8d4..00000000 --- a/src/main/scala/co.blocke.scalajack/internal/TreeNode.scala +++ /dev/null @@ -1,16 +0,0 @@ -package co.blocke.scalajack -package internal - -case class TreeNode[P](payload: P, children: List[TreeNode[P]] = Nil): - def addChild(tn: TreeNode[P]) = this.copy(children = this.children :+ tn) - inline def hasChildren = !children.isEmpty - -object TreeNode: - - def inverted[P](tn: TreeNode[P]) = deapthFirst(tn).reverse - def deapthFirst[P](tn: TreeNode[P], acc: List[TreeNode[P]] = Nil): List[TreeNode[P]] = - tn.children.foldLeft(if acc == Nil then List(tn) else acc) { case (soFar, child) => - val nextList = soFar :+ child - if !child.hasChildren then nextList - else deapthFirst(child, nextList) - } diff --git a/src/main/scala/co.blocke.scalajack/json/JsonCodec.scalax b/src/main/scala/co.blocke.scalajack/json/JsonCodec.scalax deleted file mode 100644 index cb15290b..00000000 --- a/src/main/scala/co.blocke.scalajack/json/JsonCodec.scalax +++ /dev/null @@ -1,13 +0,0 @@ -package co.blocke.scalajack -package json - -class JsonCodec[T]: - parent: ScalaJack[T] => - - inline def fromJson( js: String )(using cfg: JsonConfig = JsonConfig()): Either[JsonParseError, T] = - parent.jsonDecoder.decodeJson(js) - - inline def toJson( a: T )(using cfg: JsonConfig = JsonConfig()): String = - parent.jsonEncoder.encode(a, new StringBuilder(), cfg) - - \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala b/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala index 5f494d0e..c77e6b48 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala @@ -1,24 +1,158 @@ package co.blocke.scalajack package json -case class JsonConfig( - noneAsNull: Boolean = false, - forbidNullsInInput: Boolean = false, - tryFailureHandling: TryOption = TryOption.NO_WRITE, - undefinedFieldHandling: UndefinedValueOption = UndefinedValueOption.THROW_EXCEPTION, - permissivePrimitives: Boolean = false, - writeNonConstructorFields: Boolean = false, +import co.blocke.scala_reflection.TypedName +import co.blocke.scala_reflection.reflect.* +import co.blocke.scala_reflection.reflect.rtypeRefs.* +import scala.quoted.* + +class JsonConfig private[scalajack] ( + val noneAsNull: Boolean, + // forbidNullsInInput: Boolean = false, + // tryFailureHandling: TryOption = TryOption.NO_WRITE, + // undefinedFieldHandling: UndefinedValueOption = UndefinedValueOption.THROW_EXCEPTION, + // permissivePrimitives: Boolean = false, + // writeNonConstructorFields: Boolean = false, // -------------------------- - typeHintLabel: String = "_hint", - typeHintLabelByTrait: Map[String, String] = Map.empty[String, String], // Trait name -> type hint label - typeHintDefaultTransformer: String => String = (v: String) => v, // in case you want something different than class name (simple name re-mapping) - typeHintTransformer: Map[String, Any => String] = Map.empty[String, Any => String], // if you want class-specific control (instance value => String) + val typeHintLabel: String, + val typeHintPolicy: TypeHintPolicy = TypeHintPolicy.SIMPLE_CLASSNAME // -------------------------- - enumsAsIds: Char | List[String] = Nil // Char is '*' for all enums as ids, or a list of fully-qualified class names -) + // enumsAsIds: Option[List[String]] = None // None=no enums as ids, Some(Nil)=all enums as ids, Some(List(...))=specified classes enums as ids +): + def withNoneAsNull(nan: Boolean): JsonConfig = copy(noneAsNull = nan) + def withTypeHintLabel(label: String): JsonConfig = copy(typeHintLabel = label) + def withTypeHintPolicy(hintPolicy: TypeHintPolicy): JsonConfig = copy(typeHintPolicy = hintPolicy) + + private[this] def copy( + noneAsNull: Boolean = noneAsNull, + typeHintLabel: String = typeHintLabel, + typeHintPolicy: TypeHintPolicy = typeHintPolicy + ): JsonConfig = new JsonConfig( + noneAsNull, + typeHintLabel, + typeHintPolicy + ) enum TryOption: case AS_NULL, NO_WRITE, ERR_MSG_STRING, THROW_EXCEPTION enum UndefinedValueOption: case AS_NULL, AS_SYMBOL, THROW_EXCEPTION + +enum TypeHintPolicy: + case SIMPLE_CLASSNAME, SCRAMBLE_CLASSNAME, USE_ANNOTATION + +object JsonConfig + extends JsonConfig( + noneAsNull = false, + typeHintLabel = "_hint", + typeHintPolicy = TypeHintPolicy.SIMPLE_CLASSNAME + ): + import scala.quoted.FromExpr.* + + private[scalajack] given FromExpr[JsonConfig] with { + + def extract[X: FromExpr](name: String, x: Expr[X])(using Quotes): X = + import quotes.reflect.* + summon[FromExpr[X]].unapply(x).getOrElse(throw JsonConfigError(s"Can't parse $name: ${x.show}, tree: ${x.asTerm}")) + + def unapply(x: Expr[JsonConfig])(using Quotes): Option[JsonConfig] = + import quotes.reflect.* + + x match + case '{ + JsonConfig( + $noneAsNullE, + // $forbitNullsInInputE, + // $tryFailureHandlerE, + // $undefinedFieldHandlingE, + // $permissivePrimitivesE, + // $writeNonConstructorFieldsE, + $typeHintLabelE, + $typeHintPolicyE + // $enumsAsIdsE + ) + } => + try + Some( + JsonConfig( + extract("noneAsNull", noneAsNullE), + // extract("forbitNullsInInput", forbitNullsInInputE), + // extract("tryFailureHandler", tryFailureHandlerE), + // extract("undefinedFieldHandling", undefinedFieldHandlingE), + // extract("permissivePrimitives", permissivePrimitivesE), + // extract("writeNonConstructorFields", writeNonConstructorFieldsE), + // extract2[String]("typeHintLabel", x) + extract("typeHintLabel", typeHintLabelE), + extract("typeHintPolicy", typeHintPolicyE) + // extract("enumsAsIds", enumsAsIdsE) + ) + ) + catch { + case x => + println("ERROR: " + x.getMessage) + None + } + case '{ JsonConfig } => Some(JsonConfig) + case '{ ($x: JsonConfig).withNoneAsNull($v) } => Some(x.valueOrAbort.withNoneAsNull(v.valueOrAbort)) + case '{ ($x: JsonConfig).withTypeHintLabel($v) } => Some(x.valueOrAbort.withTypeHintLabel(v.valueOrAbort)) + case '{ ($x: JsonConfig).withTypeHintPolicy($v) } => Some(x.valueOrAbort.withTypeHintPolicy(v.valueOrAbort)) + case z => + println("Z: " + z.show) + None + } + + private[scalajack] given FromExpr[TryOption] with { + def unapply(x: Expr[TryOption])(using Quotes): Option[TryOption] = + import quotes.reflect.* + x match + case '{ TryOption.AS_NULL } => Some(TryOption.AS_NULL) + case '{ TryOption.NO_WRITE } => Some(TryOption.NO_WRITE) + case '{ TryOption.ERR_MSG_STRING } => Some(TryOption.ERR_MSG_STRING) + case '{ TryOption.THROW_EXCEPTION } => Some(TryOption.THROW_EXCEPTION) + } + + private[scalajack] given FromExpr[UndefinedValueOption] with { + def unapply(x: Expr[UndefinedValueOption])(using Quotes): Option[UndefinedValueOption] = + import quotes.reflect.* + x match + case '{ UndefinedValueOption.AS_NULL } => Some(UndefinedValueOption.AS_NULL) + case '{ UndefinedValueOption.AS_SYMBOL } => Some(UndefinedValueOption.AS_SYMBOL) + case '{ UndefinedValueOption.THROW_EXCEPTION } => Some(UndefinedValueOption.THROW_EXCEPTION) + } + + private[scalajack] given FromExpr[TypeHintPolicy] with { + def unapply(x: Expr[TypeHintPolicy])(using Quotes): Option[TypeHintPolicy] = + import quotes.reflect.* + x match + case '{ TypeHintPolicy.SIMPLE_CLASSNAME } => Some(TypeHintPolicy.SIMPLE_CLASSNAME) + case '{ TypeHintPolicy.SCRAMBLE_CLASSNAME } => Some(TypeHintPolicy.SCRAMBLE_CLASSNAME) + case '{ TypeHintPolicy.USE_ANNOTATION } => Some(TypeHintPolicy.USE_ANNOTATION) + } + + /* + Here's how we use Quotes to get default values from a class...def + + // Constructor argument list, preloaded with optional 'None' values and any default values specified + val preloaded = Expr + .ofList(r.fields.map { f => + val scalaF = f.asInstanceOf[ScalaFieldInfoRef] + if scalaF.defaultValueAccessorName.isDefined then + r.refType match + case '[t] => + val tpe = TypeRepr.of[t].widen + val sym = tpe.typeSymbol + val companionBody = sym.companionClass.tree.asInstanceOf[ClassDef].body + val companion = Ref(sym.companionModule) + companionBody + .collect { + case defaultMethod @ DefDef(name, _, _, _) if name.startsWith("$lessinit$greater$default$" + (f.index + 1)) => + companion.select(defaultMethod.symbol).appliedToTypes(tpe.typeArgs).asExpr + } + .headOption + .getOrElse(Expr(null.asInstanceOf[Boolean])) + else if scalaF.fieldRef.isInstanceOf[OptionRef[_]] then Expr(None) + else Expr(null.asInstanceOf[Int]) + }) + + */ diff --git a/src/main/scala/co.blocke.scalajack/json/JsonError.scala b/src/main/scala/co.blocke.scalajack/json/JsonError.scala index b5f127cf..fb85e76c 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonError.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonError.scala @@ -1,7 +1,10 @@ package co.blocke.scalajack package json -class JsonError(msg: String) extends Throwable +class JsonIllegalKeyType(msg: String) extends Throwable(msg) +class JsonNullKeyValue(msg: String) extends Throwable(msg) +class JsonUnsupportedType(msg: String) extends Throwable(msg) +class JsonConfigError(msg: String) extends Throwable(msg) class ParseError(val msg: String) extends Throwable(msg): val show: String = "" diff --git a/src/main/scala/co.blocke.scalajack/json/package.scala b/src/main/scala/co.blocke.scalajack/json/package.scala index 8bf78f50..692942f9 100644 --- a/src/main/scala/co.blocke.scalajack/json/package.scala +++ b/src/main/scala/co.blocke.scalajack/json/package.scala @@ -7,3 +7,17 @@ val BUFFER_EXCEEDED: Char = 7 // Old "BELL" ASCII value, used as a marker when w val END_OF_STRING: Char = 3 inline def lastPart(n: String) = n.split('.').last.stripSuffix("$") + +val random = new scala.util.Random() +def scramble(hash: Int): String = + val last5 = f"$hash%05d".takeRight(5) + val digits = (1 to 5).map(_ => random.nextInt(10)) + if digits(0) % 2 == 0 then s"${last5(0)}${digits(0)}${last5(1)}${digits(1)}${last5(2)}-${digits(2)}${last5(3)}${digits(3)}-${last5(4)}${digits(4)}A" + else s"${digits(0)}${last5(0)}${digits(1)}${last5(1)}${digits(2)}-${last5(2)}${digits(3)}${last5(3)}-${digits(4)}${last5(4)}B" + +def descrambleTest(in: String, hash: Int): Boolean = + val last5 = f"$hash%05d".takeRight(5) + in.last match + case 'A' if in.length == 13 => "" + in(0) + in(2) + in(4) + in(7) + in(10) == last5 + case 'B' if in.length == 13 => "" + in(1) + in(3) + in(6) + in(8) + in(11) == last5 + case _ => false diff --git a/src/main/scala/co.blocke.scalajack/json/reading/JsonReader.scala b/src/main/scala/co.blocke.scalajack/json/reading/JsonReader.scala index d06707fa..a8a12267 100644 --- a/src/main/scala/co.blocke.scalajack/json/reading/JsonReader.scala +++ b/src/main/scala/co.blocke.scalajack/json/reading/JsonReader.scala @@ -18,10 +18,21 @@ import scala.collection.Factory */ object JsonReader: + // Temporary no-op reader... def refRead[T]( ref: RTypeRef[T] )(using q: Quotes, tt: Type[T]): Expr[JsonDecoder[T]] = import quotes.reflect.* + '{ + new JsonDecoder[T] { + def unsafeDecode(in: JsonSource): T = null.asInstanceOf[T] + } + } + + def refRead2[T]( + ref: RTypeRef[T] + )(using q: Quotes, tt: Type[T]): Expr[JsonDecoder[T]] = + import quotes.reflect.* ref match case r: PrimitiveRef => diff --git a/src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala b/src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala index 0d8e3d5d..1cef76f3 100644 --- a/src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala +++ b/src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala @@ -5,12 +5,10 @@ package writing import co.blocke.scala_reflection.{RTypeRef, TypedName} import co.blocke.scala_reflection.reflect.rtypeRefs.* import scala.quoted.* -import scala.collection.mutable.Map as MMap -import internal.TreeNode object JsonCodecMaker: - def generateCodecFor[T](ref: RTypeRef[T])(using Quotes)(using tt: Type[T]) = + def generateCodecFor[T](ref: RTypeRef[T], cfg: JsonConfig)(using Quotes)(using tt: Type[T]) = import quotes.reflect.* // Cache generated method Symbols + an array of the generated functions (DefDef) @@ -47,15 +45,43 @@ object JsonCodecMaker: List(arg.asTerm, out.asTerm) ).asExprOf[Unit] - def genFnBody[T](r: RTypeRef[?], aE: Expr[T], out: Expr[JsonOutput])(using Quotes) = + def genFnBody[T](r: RTypeRef[?], aE: Expr[T], out: Expr[JsonOutput], emitDiscriminator: Boolean = false)(using Quotes): Expr[Unit] = r.refType match case '[b] => r match + case t: ArrayRef[?] => + makeFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => + t.elementRef.refType match + case '[e] => + val tin = in.asExprOf[Array[e]] + '{ + $out.startArray() + $tin.foreach { i => + ${ genWriteVal('{ i }, t.elementRef.asInstanceOf[RTypeRef[e]], out) } + } + $out.endArray() + } + } + case t: SeqRef[?] => makeFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => t.elementRef.refType match case '[e] => - val tin = in.asExprOf[Seq[e]] + val tin = if t.isMutable then in.asExprOf[scala.collection.mutable.Seq[e]] else in.asExprOf[Seq[e]] + '{ + $out.startArray() + $tin.foreach { i => + ${ genWriteVal('{ i }, t.elementRef.asInstanceOf[RTypeRef[e]], out) } + } + $out.endArray() + } + } + + case t: SetRef[?] => + makeFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => + t.elementRef.refType match + case '[e] => + val tin = if t.isMutable then in.asExprOf[scala.collection.mutable.Set[e]] else in.asExprOf[Set[e]] '{ $out.startArray() $tin.foreach { i => @@ -79,7 +105,19 @@ object JsonCodecMaker: ${ genWriteVal(fieldValue, f.fieldRef.asInstanceOf[RTypeRef[z]], out) } } } - if eachField.length == 1 then eachField.head + if emitDiscriminator then + val cname = cfg.typeHintPolicy match + case TypeHintPolicy.SIMPLE_CLASSNAME => Expr(lastPart(t.name)) + case TypeHintPolicy.SCRAMBLE_CLASSNAME => + val hash = Expr(lastPart(t.name).hashCode) + '{ scramble($hash) } + case TypeHintPolicy.USE_ANNOTATION => ??? + val withDisc = '{ + $out.label(${ Expr(cfg.typeHintLabel) }) + $out.value($cname) + } +: eachField + Expr.block(withDisc.init, withDisc.last) + else if eachField.length == 1 then eachField.head else Expr.block(eachField.init, eachField.last) } '{ @@ -89,13 +127,65 @@ object JsonCodecMaker: } } + case t: MapRef[?] => + t.elementRef.refType match + case '[k] => + makeFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => + t.elementRef2.refType match + case '[v] => + val tin = if t.isMutable then in.asExprOf[scala.collection.mutable.Map[k, v]] else in.asExprOf[Map[k, v]] + '{ + $out.startObject() + $tin.foreach { case (k, v) => + $out.maybeComma() + ${ genWriteVal('{ k }, t.elementRef.asInstanceOf[RTypeRef[k]], out, true) } + $out.colon() + ${ genWriteVal('{ v }, t.elementRef2.asInstanceOf[RTypeRef[v]], out) } + } + $out.endObject() + } + } + + case t: TraitRef[?] => + // classesSeen.put(t.typedName, t) + // val rt = t.expr.asInstanceOf[Expr[TraitRType[T]]] + if !t.isSealed then throw new JsonUnsupportedType("Non-sealed traits are not supported") + if t.childrenAreObject then + // case object -> just write the simple name of the object + val tin = aE.asExprOf[b] + '{ + $out.value($tin.getClass.getName.split('.').last.stripSuffix("$")) + } + else + // So... sealed trait children could be any of those defined for the trait. We need to + // generate functions for each child then a master function that examines $aE and based on + // its value, call the appropriate function to render. + // val beforeKeys = methodSyms.keySet + // t.sealedChildren.foreach { child => + // child.refType match + // case '[c] => + // genFnBody[c](child, aE.asExprOf[c], out) + // } + // Now generate and return the calling function based on runtime type + // Refer to Jsoniter: JsonCodecMaker.scala around line 920 for example how to do this, incl a wildcard, which + // we don't need here. + val cases = t.sealedChildren.map { child => + child.refType match + case '[c] => + val subtype = TypeIdent(TypeRepr.of[c].typeSymbol) + val sym = Symbol.newBind(Symbol.spliceOwner, "t", Flags.EmptyFlags, subtype.tpe) + CaseDef(Bind(sym, Typed(Ref(sym), subtype)), None, genFnBody[c](child, Ref(sym).asExprOf[c], out, true).asTerm) + } :+ CaseDef(Literal(NullConstant()), None, '{ $out.burpNull() }.asTerm) + val matchExpr = Match(aE.asTerm, cases).asExprOf[Unit] + matchExpr + def genWriteVal[T: Type]( aE: Expr[T], ref: RTypeRef[T], - // types: List[TypeRepr], - // isStringified: Boolean, // config option to wrap numbers, boolean, etc in "". Not needed for now... we'll see later... // optWriteDiscriminator: Option[WriteDiscriminator], - out: Expr[JsonOutput] + out: Expr[JsonOutput], + // cfgE: Expr[JsonConfig], + isStringified: Boolean = false // e.g. Map key values. Doesn't apply to stringish values, which are always quotes-wrapped )(using Quotes): Expr[Unit] = val methodKey = MethodKey(ref, false) methodSyms @@ -105,12 +195,96 @@ object JsonCodecMaker: } .getOrElse( ref match - case t: BooleanRef => '{ $out.value(${ aE.asExprOf[Boolean] }) } - case t: IntRef => '{ $out.value(${ aE.asExprOf[Int] }) } - case t: StringRef => '{ $out.value(${ aE.asExprOf[String] }) } - case _ => - println("Gen for: " + ref) - genFnBody(ref, aE, out) + // First cover all primitive and simple types... + case t: BigDecimalRef => + if isStringified then '{ $out.valueStringified(${ aE.asExprOf[scala.math.BigDecimal] }) } + else '{ $out.value(${ aE.asExprOf[scala.math.BigDecimal] }) } + case t: BigIntRef => + if isStringified then '{ $out.valueStringified(${ aE.asExprOf[scala.math.BigInt] }) } + else '{ $out.value(${ aE.asExprOf[scala.math.BigInt] }) } + case t: BooleanRef => + if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Boolean] }) } + else '{ $out.value(${ aE.asExprOf[Boolean] }) } + case t: ByteRef => + if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Byte] }) } + else '{ $out.value(${ aE.asExprOf[Byte] }) } + case t: CharRef => '{ $out.value(${ aE.asExprOf[Char] }) } + case t: DoubleRef => + if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Double] }) } + else '{ $out.value(${ aE.asExprOf[Double] }) } + case t: FloatRef => + if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Float] }) } + else '{ $out.value(${ aE.asExprOf[Float] }) } + case t: IntRef => + if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Int] }) } + else '{ $out.value(${ aE.asExprOf[Int] }) } + case t: LongRef => + if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Long] }) } + else '{ $out.value(${ aE.asExprOf[Long] }) } + case t: ShortRef => + if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Short] }) } + else '{ $out.value(${ aE.asExprOf[Short] }) } + case t: StringRef => '{ $out.value(${ aE.asExprOf[String] }) } + + case t: JBigDecimalRef => + if isStringified then '{ $out.valueStringified(${ aE.asExprOf[java.math.BigDecimal] }) } + else '{ $out.value(${ aE.asExprOf[java.math.BigDecimal] }) } + case t: JBigIntegerRef => + if isStringified then '{ $out.value(${ aE.asExprOf[java.math.BigInteger] }) } + else '{ $out.value(${ aE.asExprOf[java.math.BigInteger] }) } + case t: JBooleanRef => + if isStringified then '{ $out.value(${ aE.asExprOf[java.lang.Boolean] }) } + else '{ $out.value(${ aE.asExprOf[java.lang.Boolean] }) } + case t: JByteRef => + if isStringified then '{ $out.value(${ aE.asExprOf[java.lang.Byte] }) } + else '{ $out.value(${ aE.asExprOf[java.lang.Byte] }) } + case t: JCharacterRef => + if isStringified then '{ $out.value(${ aE.asExprOf[java.lang.Character] }) } + else '{ $out.value(${ aE.asExprOf[java.lang.Character] }) } + case t: JDoubleRef => + if isStringified then '{ $out.value(${ aE.asExprOf[java.lang.Double] }) } + else '{ $out.value(${ aE.asExprOf[java.lang.Double] }) } + case t: JFloatRef => + if isStringified then '{ $out.value(${ aE.asExprOf[java.lang.Float] }) } + else '{ $out.value(${ aE.asExprOf[java.lang.Float] }) } + case t: JIntegerRef => + if isStringified then '{ $out.value(${ aE.asExprOf[java.lang.Integer] }) } + else '{ $out.value(${ aE.asExprOf[java.lang.Integer] }) } + case t: JLongRef => + if isStringified then '{ $out.value(${ aE.asExprOf[java.lang.Long] }) } + else '{ $out.value(${ aE.asExprOf[java.lang.Long] }) } + case t: JShortRef => + if isStringified then '{ $out.value(${ aE.asExprOf[java.lang.Short] }) } + else '{ $out.value(${ aE.asExprOf[java.lang.Short] }) } + case t: JNumberRef => + if isStringified then '{ $out.value(${ aE.asExprOf[java.lang.Number] }) } + else '{ $out.value(${ aE.asExprOf[java.lang.Number] }) } + + case t: DurationRef => '{ $out.value(${ aE.asExprOf[java.time.Duration] }) } + case t: InstantRef => '{ $out.value(${ aE.asExprOf[java.time.Instant] }) } + case t: LocalDateRef => '{ $out.value(${ aE.asExprOf[java.time.LocalDate] }) } + case t: LocalDateTimeRef => '{ $out.value(${ aE.asExprOf[java.time.LocalDateTime] }) } + case t: LocalTimeRef => '{ $out.value(${ aE.asExprOf[java.time.LocalTime] }) } + case t: MonthDayRef => '{ $out.value(${ aE.asExprOf[java.time.MonthDay] }) } + case t: OffsetDateTimeRef => '{ $out.value(${ aE.asExprOf[java.time.OffsetDateTime] }) } + case t: OffsetTimeRef => '{ $out.value(${ aE.asExprOf[java.time.OffsetTime] }) } + case t: PeriodRef => '{ $out.value(${ aE.asExprOf[java.time.Period] }) } + case t: YearRef => '{ $out.value(${ aE.asExprOf[java.time.Year] }) } + case t: YearMonthRef => '{ $out.value(${ aE.asExprOf[java.time.YearMonth] }) } + case t: ZonedDateTimeRef => '{ $out.value(${ aE.asExprOf[java.time.ZonedDateTime] }) } + case t: ZoneIdRef => '{ $out.value(${ aE.asExprOf[java.time.ZoneId] }) } + case t: ZoneOffsetRef => '{ $out.value(${ aE.asExprOf[java.time.ZoneOffset] }) } + + case t: UUIDRef => '{ $out.value(${ aE.asExprOf[java.util.UUID] }) } + + case t: AliasRef[?] => + t.unwrappedType.refType match + case '[e] => + genWriteVal[e](aE.asInstanceOf[Expr[e]], t.unwrappedType.asInstanceOf[RTypeRef[e]], out) + + // Everything else... + case _ if isStringified => throw new JsonIllegalKeyType("Non-primitive/non-simple types cannot be map keys") + case _ => genFnBody(ref, aE, out) ) // ================================================================ @@ -128,14 +302,12 @@ object JsonCodecMaker: // else genReadVal(rootTpe :: Nil, 'default, cfg.isStringified, false, 'in) // } - def encodeValue(in: T, out: JsonOutput): Unit = ${ - genWriteVal('in, ref, 'out) - } + def encodeValue(in: T, out: JsonOutput): Unit = ${ genWriteVal('in, ref, 'out) } } }.asTerm val neededDefs = // others here??? Refer to Jsoniter file JsonCodecMaker.scala methodDefs val codec = Block(neededDefs.toList, codecDef).asExprOf[JsonCodec[T]] - println(s"Codec: ${codec.show}") + // println(s"Codec: ${codec.show}") codec diff --git a/src/main/scala/co.blocke.scalajack/json/writing/JsonOutput.scala b/src/main/scala/co.blocke.scalajack/json/writing/JsonOutput.scala index dcf0c510..7e05cf37 100644 --- a/src/main/scala/co.blocke.scalajack/json/writing/JsonOutput.scala +++ b/src/main/scala/co.blocke.scalajack/json/writing/JsonOutput.scala @@ -2,6 +2,8 @@ package co.blocke.scalajack package json package writing +import java.time.format.DateTimeFormatter.* + case class JsonOutput(): val internal: StringBuilder = new StringBuilder() @@ -31,33 +33,93 @@ case class JsonOutput(): internal.append(']') comma = true + inline def colon(): Unit = + internal.append(':') + comma = false + inline def maybeComma(): Unit = if comma then internal.append(',') comma = false inline def burpNull(): Unit = + maybeComma() internal.append("null") + comma = true inline def label(s: String): Unit = maybeComma() internal.append("\"" + s + "\":") - inline def label(s: Long): Unit = + // ----------------------- Primitive/Simple type support + + inline def value(v: scala.math.BigDecimal): Unit = maybeComma() - internal.append("\"" + s + "\":") + if v == null then internal.append("null") + else internal.append(v) + comma = true - // ----------------------- Primitive/Simple type support + // Note: data types that are not naturally quotes-wrapped have "Stringified" variants + // (saparate vs a param for speed), for use in Map/Json object keys. + inline def valueStringified(v: scala.math.BigDecimal): Unit = + maybeComma() + if v == null then throw new JsonNullKeyValue("Key values may not be null") + else internal.append("\"" + v + "\"") + comma = true - // TODO: BigDecimal, BigInt and Java equiv. + inline def value(v: scala.math.BigInt): Unit = + maybeComma() + if v == null then internal.append("null") + else internal.append(v) + comma = true + + inline def valueStringified(v: scala.math.BigInt): Unit = + maybeComma() + if v == null then throw new JsonNullKeyValue("Key values may not be null") + else internal.append("\"" + v + "\"") + comma = true + + inline def value(v: java.math.BigDecimal): Unit = + maybeComma() + if v == null then internal.append("null") + else internal.append(v) + comma = true + + inline def valueStringified(v: java.math.BigDecimal): Unit = + maybeComma() + if v == null then throw new JsonNullKeyValue("Key values may not be null") + else internal.append("\"" + v + "\"") + comma = true + + inline def value(v: java.math.BigInteger): Unit = + maybeComma() + if v == null then internal.append("null") + else internal.append(v) + comma = true + + inline def valueStringified(v: java.math.BigInteger): Unit = + maybeComma() + if v == null then throw new JsonNullKeyValue("Key values may not be null") + else internal.append("\"" + v + "\"") + comma = true inline def value(v: Boolean): Unit = maybeComma() internal.append(v) comma = true + inline def valueStringified(v: Boolean): Unit = + maybeComma() + internal.append("\"" + v + "\"") + comma = true + inline def value(v: Byte): Unit = maybeComma() - internal.append(v) + internal.append(v.toInt) + comma = true + + inline def valueStringified(v: Byte): Unit = + maybeComma() + internal.append("\"" + v + "\"") comma = true inline def value(v: Char): Unit = @@ -70,26 +132,51 @@ case class JsonOutput(): internal.append(v) comma = true + inline def valueStringified(v: Double): Unit = + maybeComma() + internal.append("\"" + v + "\"") + comma = true + inline def value(v: Float): Unit = maybeComma() internal.append(v) comma = true + inline def valueStringified(v: Float): Unit = + maybeComma() + internal.append("\"" + v + "\"") + comma = true + inline def value(v: Int): Unit = maybeComma() internal.append(v) comma = true + inline def valueSringified(v: Int): Unit = + maybeComma() + internal.append("\"" + v + "\"") + comma = true + inline def value(v: Long): Unit = maybeComma() internal.append(v) comma = true + inline def valueStringified(v: Long): Unit = + maybeComma() + internal.append("\"" + v + "\"") + comma = true + inline def value(v: Short): Unit = maybeComma() internal.append(v) comma = true + inline def valueStringified(v: Short): Unit = + maybeComma() + internal.append("\"" + v + "\"") + comma = true + inline def value(v: String): Unit = maybeComma() if v == null then internal.append("null") @@ -102,10 +189,22 @@ case class JsonOutput(): else internal.append(v) comma = true + inline def valueStringified(v: java.lang.Boolean): Unit = + maybeComma() + if v == null then internal.append("null") + else internal.append("\"" + v + "\"") + comma = true + inline def value(v: java.lang.Byte): Unit = maybeComma() if v == null then internal.append("null") - else internal.append(v) + else internal.append(v.toInt) + comma = true + + inline def valueStringified(v: java.lang.Byte): Unit = + maybeComma() + if v == null then internal.append("null") + else internal.append("\"" + v.toInt + "\"") comma = true inline def value(v: java.lang.Character): Unit = @@ -120,34 +219,158 @@ case class JsonOutput(): else internal.append(v) comma = true + inline def valueStringified(v: java.lang.Double): Unit = + maybeComma() + if v == null then internal.append("null") + else internal.append("\"" + v + "\"") + comma = true + inline def value(v: java.lang.Float): Unit = maybeComma() if v == null then internal.append("null") else internal.append(v) comma = true + inline def valueStringified(v: java.lang.Float): Unit = + maybeComma() + if v == null then internal.append("null") + else internal.append("\"" + v + "\"") + comma = true + inline def value(v: java.lang.Integer): Unit = maybeComma() if v == null then internal.append("null") else internal.append(v) comma = true + inline def valueStringified(v: java.lang.Integer): Unit = + maybeComma() + if v == null then internal.append("null") + else internal.append("\"" + v + "\"") + comma = true + inline def value(v: java.lang.Long): Unit = maybeComma() if v == null then internal.append("null") else internal.append(v) comma = true + inline def valueStringified(v: java.lang.Long): Unit = + maybeComma() + if v == null then internal.append("null") + else internal.append("\"" + v + "\"") + comma = true + inline def value(v: java.lang.Short): Unit = maybeComma() if v == null then internal.append("null") else internal.append(v) comma = true + inline def valueStringified(v: java.lang.Short): Unit = + maybeComma() + if v == null then internal.append("null") + else internal.append("\"" + v + "\"") + comma = true + inline def value(v: java.lang.Number): Unit = maybeComma() if v == null then internal.append("null") else internal.append(v) comma = true - // TODO: UUID + inline def valueStringified(v: java.lang.Number): Unit = + maybeComma() + if v == null then internal.append("null") + else internal.append("\"" + v + "\"") + comma = true + + inline def value(v: java.time.Duration): Unit = + maybeComma() + if v == null then internal.append("null") + else internal.append("\"" + v + "\"") + comma = true + + inline def value(v: java.time.Instant): Unit = + maybeComma() + if v == null then internal.append("null") + else internal.append("\"" + v + "\"") + comma = true + + inline def value(v: java.time.LocalDate): Unit = + maybeComma() + if v == null then internal.append("null") + else internal.append("\"" + v.format(ISO_LOCAL_DATE) + "\"") + comma = true + + inline def value(v: java.time.LocalDateTime): Unit = + maybeComma() + if v == null then internal.append("null") + else internal.append("\"" + v.format(ISO_LOCAL_DATE_TIME) + "\"") + comma = true + + inline def value(v: java.time.LocalTime): Unit = + maybeComma() + if v == null then internal.append("null") + else internal.append("\"" + v.format(ISO_LOCAL_TIME) + "\"") + comma = true + + inline def value(v: java.time.MonthDay): Unit = + maybeComma() + if v == null then internal.append("null") + else internal.append("\"" + v + "\"") + comma = true + + inline def value(v: java.time.OffsetDateTime): Unit = + maybeComma() + if v == null then internal.append("null") + else internal.append("\"" + v.format(ISO_OFFSET_DATE_TIME) + "\"") + comma = true + + inline def value(v: java.time.OffsetTime): Unit = + maybeComma() + if v == null then internal.append("null") + else internal.append("\"" + v.format(ISO_OFFSET_TIME) + "\"") + comma = true + + inline def value(v: java.time.Period): Unit = + maybeComma() + if v == null then internal.append("null") + else internal.append("\"" + v + "\"") + comma = true + + inline def value(v: java.time.Year): Unit = + maybeComma() + if v == null then internal.append("null") + else internal.append("\"" + v + "\"") + comma = true + + inline def value(v: java.time.YearMonth): Unit = + maybeComma() + if v == null then internal.append("null") + else internal.append("\"" + v + "\"") + comma = true + + inline def value(v: java.time.ZonedDateTime): Unit = + maybeComma() + if v == null then internal.append("null") + else internal.append("\"" + v.format(ISO_ZONED_DATE_TIME) + "\"") + comma = true + + inline def value(v: java.time.ZoneId): Unit = + maybeComma() + if v == null then internal.append("null") + else internal.append("\"" + v + "\"") + comma = true + + inline def value(v: java.time.ZoneOffset): Unit = + maybeComma() + if v == null then internal.append("null") + else internal.append("\"" + v + "\"") + comma = true + + inline def value(v: java.util.UUID): Unit = + maybeComma() + if v == null then internal.append("null") + else internal.append("\"" + v + "\"") + comma = true diff --git a/src/main/scala/co.blocke.scalajack/json/writing/JsonWriter.scalax b/src/main/scala/co.blocke.scalajack/json/writing/JsonWriter.scalax deleted file mode 100644 index ff2ae12c..00000000 --- a/src/main/scala/co.blocke.scalajack/json/writing/JsonWriter.scalax +++ /dev/null @@ -1,111 +0,0 @@ -package co.blocke.scalajack -package json -package writing - -import co.blocke.scala_reflection.{RTypeRef, TypedName} -import co.blocke.scala_reflection.reflect.rtypeRefs.* -import co.blocke.scala_reflection.rtypes.* -import scala.quoted.* -import scala.collection.mutable.Map as MMap -import scala.util.Failure - -object JsonWriter: - - private def shouldTestForOkToWrite(r: RTypeRef[?]): Boolean = - r match - case _: OptionRef[?] => true - case _: LeftRightRef[?] => true - case _: TryRef[?] => true - case _ => false - - // Tests whether we should write something or not--mainly in the case of Option, or wrapped Option - // Affected types: Option, java.util.Optional, Left/Right, Try/Failure - private def isOkToWrite(a: Any, cfg: JsonConfig) = - a match - case None if !cfg.noneAsNull => false - case o: java.util.Optional[?] if o.isEmpty && !cfg.noneAsNull => false - case Left(None) if !cfg.noneAsNull => false - case Right(None) if !cfg.noneAsNull => false - case Failure(_) if cfg.tryFailureHandling == TryOption.NO_WRITE => false - case _ => true - - def refRead[T]( - ref: RTypeRef[T] - )(using q: Quotes, tt: Type[T]): Expr[(T, JsonOutput, JsonConfig) => String] = - import quotes.reflect.* - '{ (a: T, out: JsonOutput, cfg: JsonConfig) => - ${ refWrite(ref, '{ a }, '{ out }, '{ cfg })(using MMap.empty[TypedName, RTypeRef[?]]) }.result - } - - private def refWrite[T]( - ref: RTypeRef[T], - aE: Expr[T], - outE: Expr[JsonOutput], - cfgE: Expr[JsonConfig], - isMapKey: Boolean = false - )(using classesSeen: MMap[TypedName, RTypeRef[?]])(using q: Quotes, tt: Type[T]): Expr[JsonOutput] = - import quotes.reflect.* - - ref match - case t: BooleanRef => '{ $outE.value($aE) } - case t: IntRef => '{ $outE.value($aE) } - case t: StringRef => '{ $outE.value($aE) } - - case t: SeqRef[?] => - if isMapKey then throw new JsonError("Seq instances cannot be map keys") - t.elementRef.refType match - case '[e] => - val bE = aE.asInstanceOf[Expr[Seq[e]]] // pull type-cast into complie-time for performance gains - '{ - if $aE == null then $outE.burpNull() - else - $outE.startArray() - $bE.foreach { one => - ${ - if shouldTestForOkToWrite(t.elementRef) then // A lot of drama to avoid 1 'if' stmt--it matters for max performance - '{ - if isOkToWrite(one, $cfgE) then ${ refWrite[e](t.elementRef.asInstanceOf[RTypeRef[e]], '{ one }, outE, cfgE) } - } - else refWrite[e](t.elementRef.asInstanceOf[RTypeRef[e]], '{ one }, outE, cfgE) - } - } - $outE.endArray() - } - - case t: ScalaClassRef[?] => - classesSeen.put(t.typedName, t) - val isCase = Expr(t.isCaseClass) - '{ - // Experiment (works!) to generate def fn(in:T, sb: JsonOut). The goal is to pre-calc all the ugly stuff so that the - // macro generated is as pure/fast as possible. - // - // def foo(in: T): Unit = - // ${ - // Expr.ofList(t.fields.map { f => - // '{ println("Field: " + ${ Select.unique('in.asTerm, f.name).asExprOf[Any] }) } - // }) - // } - if $aE == null then $outE.burpNull() - else - $outE.startObject() - ${ - Expr.ofList(t.fields.map { f => - f.fieldRef.refType match - case '[e] => - val fieldValue = Select.unique(aE.asTerm, f.name).asExprOf[e] - val name = Expr(f.name) - if shouldTestForOkToWrite(f.fieldRef) then // A lot of drama to avoid 1 'if' stmt--it matters for max performance - '{ - if isOkToWrite($fieldValue, $cfgE) then - $outE.label($name) - ${ refWrite[e](f.fieldRef.asInstanceOf[RTypeRef[e]], fieldValue, outE, cfgE) } - } - else - '{ - $outE.label($name) - ${ refWrite[e](f.fieldRef.asInstanceOf[RTypeRef[e]], fieldValue, outE, cfgE) } - } - }) - } - $outE.endObject() - } diff --git a/src/main/scala/co.blocke.scalajack/json/writing/JsonWriter2.scalax b/src/main/scala/co.blocke.scalajack/json/writing/JsonWriter2.scalax deleted file mode 100644 index daec147b..00000000 --- a/src/main/scala/co.blocke.scalajack/json/writing/JsonWriter2.scalax +++ /dev/null @@ -1,157 +0,0 @@ -package co.blocke.scalajack -package json -package writing - -import co.blocke.scala_reflection.{RTypeRef, TypedName} -import co.blocke.scala_reflection.reflect.rtypeRefs.* -import scala.quoted.* -import scala.collection.mutable.Map as MMap -import scala.util.Failure - -object JsonWriter2: - - private def shouldTestForOkToWrite(r: RTypeRef[?]): Boolean = - r match - case _: OptionRef[?] => true - case _: LeftRightRef[?] => true - case _: TryRef[?] => true - case _ => false - - // Tests whether we should write something or not--mainly in the case of Option, or wrapped Option - // Affected types: Option, java.util.Optional, Left/Right, Try/Failure - private def isOkToWrite(a: Any, cfg: JsonConfig) = - a match - case None if !cfg.noneAsNull => false - case o: java.util.Optional[?] if o.isEmpty && !cfg.noneAsNull => false - case Left(None) if !cfg.noneAsNull => false - case Right(None) if !cfg.noneAsNull => false - case Failure(_) if cfg.tryFailureHandling == TryOption.NO_WRITE => false - case _ => true - - def refRead[T]( - ref: RTypeRef[T] - )(using q: Quotes, tt: Type[T]): Expr[(T, StringBuilder, JsonConfig) => String] = - import quotes.reflect.* - '{ (a: T, sb: StringBuilder, cfg: JsonConfig) => - ${ refWrite(ref, '{ a }, '{ sb }, '{ cfg })(using MMap.empty[TypedName, RTypeRef[?]]) }.toString - } - - private def refWrite[T]( - ref: RTypeRef[T], - aE: Expr[T], - sbE: Expr[StringBuilder], - cfgE: Expr[JsonConfig], - isMapKey: Boolean = false - )(using classesSeen: MMap[TypedName, RTypeRef[?]])(using q: Quotes, tt: Type[T]): Expr[StringBuilder] = - import quotes.reflect.* - - ref match - case t: PrimitiveRef[?] if t.family == PrimFamily.Stringish => - if t.isNullable then '{ if $aE == null then $sbE.append("null") else $sbE.append("\"" + $aE + "\"") } - else '{ $sbE.append("\"" + $aE + "\"") } - - case t: PrimitiveRef[?] => - val isNullable = Expr(t.isNullable) - if isMapKey then - '{ - if $isNullable && $aE == null then $sbE.append("\"null\"") - else $sbE.append($sbE.append("\"" + $aE + "\"")) - } - else if t.isNullable then '{ if $aE == null then $sbE.append("null") else $sbE.append("\"" + $aE.toString + "\"") } - else '{ $sbE.append($aE.toString) } - - case t: SeqRef[?] => - if isMapKey then throw new JsonError("Seq instances cannot be map keys") - t.elementRef.refType match - case '[e] => - val bE = aE.asInstanceOf[Expr[Seq[e]]] // pull type-cast into complie-time for performance gains - '{ - if $aE == null then $sbE.append("null") - else - $sbE.append('[') - val sbLen = $sbE.length() - - $bE.foreach { one => - ${ - if shouldTestForOkToWrite(t.elementRef) then // A lot of drama to avoid 1 'if' stmt--it matters for max performance - '{ - if isOkToWrite(one, $cfgE) then - ${ refWrite[e](t.elementRef.asInstanceOf[RTypeRef[e]], '{ one }, sbE, cfgE) } - $sbE.append(',') - } - else - '{ - ${ refWrite[e](t.elementRef.asInstanceOf[RTypeRef[e]], '{ one }, sbE, cfgE) } - $sbE.append(',') - } - } - } - if sbLen == $sbE.length() then $sbE.append(']') - else $sbE.setCharAt($sbE.length() - 1, ']') - } - - case t: ScalaClassRef[?] => - classesSeen.put(t.typedName, t) - val isCase = Expr(t.isCaseClass) - '{ - // Experiment (works!) to generate def fn(in:T, sb: JsonOut). The goal is to pre-calc all the ugly stuff so that the - // macro generated is as pure/fast as possible. - // - // def foo(in: T): Unit = - // ${ - // Expr.ofList(t.fields.map { f => - // '{ println("Field: " + ${ Select.unique('in.asTerm, f.name).asExprOf[Any] }) } - // }) - // } - if $aE == null then $sbE.append("null") - else - $sbE.append('{') - val sbLen = $sbE.length() - ${ - Expr.ofList(t.fields.map { f => - f.fieldRef.refType match - case '[e] => - val fieldValue = Select.unique(aE.asTerm, f.name).asExprOf[e] - val name = Expr(f.name) - if shouldTestForOkToWrite(f.fieldRef) then // A lot of drama to avoid 1 'if' stmt--it matters for max performance - '{ - if isOkToWrite($fieldValue, $cfgE) then - $sbE.append("\"" + $name + ":\"") - ${ refWrite[e](f.fieldRef.asInstanceOf[RTypeRef[e]], fieldValue, sbE, cfgE) } - $sbE.append(',') - } - else - '{ - $sbE.append("\"" + $name + ":\"") - ${ refWrite[e](f.fieldRef.asInstanceOf[RTypeRef[e]], fieldValue, sbE, cfgE) } - $sbE.append(',') - } - }) - } - // write out any non-constructor fields (non-case "plain" classes) - if ! $isCase && $cfgE.writeNonConstructorFields then - ${ - Expr.ofList(t.nonConstructorFields.map { f => - f.fieldRef.refType match - case '[e] => - val fieldValue = Select.unique(aE.asTerm, f.getterLabel).asExprOf[e] - val name = Expr(f.name) - if shouldTestForOkToWrite(f.fieldRef) then // A lot of drama to avoid 1 'if' stmt--it matters for max performance - '{ - if isOkToWrite($fieldValue, $cfgE) then - $sbE.append(s"\"$$name\":") - ${ refWrite[e](f.fieldRef.asInstanceOf[RTypeRef[e]], fieldValue, sbE, cfgE) } - $sbE.append(',') - else $sbE - } - else - '{ - $sbE.append(s"\"$$name\":") - ${ refWrite[e](f.fieldRef.asInstanceOf[RTypeRef[e]], fieldValue, sbE, cfgE) } - $sbE.append(',') - } - }) - } - if sbLen == $sbE.length() then $sbE.append('}') - else $sbE.setCharAt($sbE.length() - 1, '}') - } diff --git a/src/main/scala/co.blocke.scalajack/json/writing/JsonWriter_old.scalax b/src/main/scala/co.blocke.scalajack/json/writing/JsonWriter_old.scalax index c3fb4344..a2aae7fb 100644 --- a/src/main/scala/co.blocke.scalajack/json/writing/JsonWriter_old.scalax +++ b/src/main/scala/co.blocke.scalajack/json/writing/JsonWriter_old.scalax @@ -12,32 +12,36 @@ import scala.quoted.staging.* /* TODO: - [*] - Scala non-case class - [*] - Java class (Do I still want to support this???) - [*] - Enum - [*] - Enumeration - [*] - Java Enum - [*] - Java Collections - [*] - Java Map - [*] - Intersection - [*] - Union - [*] - Either - [*] - Object (How???) - [*] - Trait (How???) - [*] - Sealed trait - [*] - sealed abstract class (handle like sealed trait....) - [*] - SelfRef - [*] - Tuple - [*] - Unknown (throw exception) - [*] - Scala 2 (throw exception) - [*] - TypeSymbol (throw exception) - [*] - Value class - - [*] -- correct all the 'if $aE == null...' - [*] -- type hint label mapping - [*] -- type hint value mapping - [*] -- Discontinue use of inTermsOf "open" (non-sealed) trait support (?!) - [*] -- update runtime-size TraitRType handling to match new compile-time code + [*] - Primitive types + [*] - Simple Types + [*] - Seq/Set/Array + [*] - Map + [*] - Scala case class + [ ] - Scala non-case class + [ ] - Java class + [ ] - Enum + [ ] - Enumeration + [ ] - Java Enum + [ ] - Java Collections + [ ] - Java Map + [ ] - Intersection + [ ] - Union + [ ] - Either + [*] - Alias type + [ ] - Object (How???) + [X] - Trait (How???) + [ ] - Sealed Trait + [ ] - Sealed Abstract Class (handle like sealed trait....) + [ ] - SelfRef + [ ] - Tuple + [ ] - Unknown (throw exception) + [ ] - Scala 2 (throw exception) + [ ] - TypeSymbol (throw exception) + [ ] - Value class + + [ ] -- type hint label mapping + [ ] -- type hint value mapping + [ ] -- update runtime-size TraitRType handling to match new compile-time code [ ] -- Streaming JSON write support [ ] -- BigJSON support (eg. multi-gig file) diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index 1d5de457..37431f9f 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -4,52 +4,50 @@ package run import co.blocke.scala_reflection.* import scala.jdk.CollectionConverters.* import scala.reflect.ClassTag +import json.* object RunMe extends App: - given json.JsonConfig = json - .JsonConfig() - .copy(noneAsNull = true) - .copy(writeNonConstructorFields = true) - // .copy(enumsAsIds = '*') + // import scala.util.Random + // val random = new Random() + + // def scramble(hash: Int): String = + // val last5 = f"$hash%05d".takeRight(5) + // val digits = (1 to 5).map(_ => random.nextInt(10)) + // if digits(0) % 2 == 0 then s"${last5(0)}${digits(0)}${last5(1)}${digits(1)}${last5(2)}-${digits(2)}${last5(3)}${digits(3)}-${last5(4)}${digits(4)}A" + // else s"${digits(0)}${last5(0)}${digits(1)}${last5(1)}${digits(2)}-${last5(2)}${digits(3)}${last5(3)}-${digits(4)}${last5(4)}B" try import json.* import ScalaJack.* + implicit val blah: ScalaJack[Foo] = sj[Foo](JsonConfig.withTypeHintPolicy(TypeHintPolicy.SCRAMBLE_CLASSNAME)) + // co.blocke.scalajack.internal.CodePrinter.code { // sj[Record] // } - val v = Foo("Hey", "Boo") - // println(sj[Foo].toJson(v)) - println(sj[Record].toJson(record)) + val v = Foo("Hey", Fish("Bloop", false)) + // val v = Foo("Hey", "Boo") + + println(ScalaJack[Foo].toJson(v)) + // println(sj[Foo](JsonConfig.withTypeHintLabel("bogus")).toJson(v)) // println(sj[Record].toJson(record)) // println("------") // println(sj[Record].fromJson(jsData)) - - // import internal.* - - // val root = TreeNode("1A", List(TreeNode("2A", List(TreeNode("3A", Nil))), TreeNode("2B", List(TreeNode("3B", Nil))), TreeNode("2C", List(TreeNode("3C", Nil))))) - - // val m = Map( - // "1A" -> "Report", - // "2A" -> "Person", - // "2B" -> "Seq[Friend]", - // "2C" -> "Seq[Pet]", - // "3A" -> "Address", - // "3B" -> "Friend", - // "3C" -> "Pet" - // ) - - // println(TreeNode.inverted(root).map(p => m(p.payload))) - catch { case t: Throwable => println(s"BOOM ($t): " + t.getMessage) t.printStackTrace } + + // val s1 = scramble(15) + // val s2 = scramble(394857) + // println(s1) + // println(s2) + // println(descrambleTest(s1, 15)) + // println(descrambleTest(s2, 394857)) diff --git a/src/main/scala/co.blocke.scalajack/run/Record.scala b/src/main/scala/co.blocke.scalajack/run/Record.scala index fc11e620..963fb7d1 100644 --- a/src/main/scala/co.blocke.scalajack/run/Record.scala +++ b/src/main/scala/co.blocke.scalajack/run/Record.scala @@ -37,9 +37,13 @@ case class Record( ) // case class Foo(name: String, maybe: Option[Int], age: Int, expected: String = "nada", gotit: Option[Int] = Some(5)) -case class Foo(name: String, expected: String = "nada") +case class Foo(name: String, a: Animal, expected: String = "nada") // case class Foo(name: String, age: Int, expected: String = "nada") +sealed trait Animal +case class Dog(name: String, numLegs: Int) extends Animal +case class Fish(name: String, isFreshwater: Boolean) extends Animal + val jsData = """{ "person": { diff --git a/src/test/scala/co.blocke.scalajack/json/primitives/JavaPrim.scala b/src/test/scala/co.blocke.scalajack/json/primitives/JavaPrim.scala new file mode 100644 index 00000000..05a3b009 --- /dev/null +++ b/src/test/scala/co.blocke.scalajack/json/primitives/JavaPrim.scala @@ -0,0 +1,312 @@ +package co.blocke.scalajack +package json +package primitives + +import ScalaJack.* +import co.blocke.scala_reflection.* +import org.scalatest.funspec.AnyFunSpec +import org.scalatest.matchers.should.Matchers.* +import org.scalatest.* +import TestUtil.* + +import java.lang.{ + Boolean => JBoolean, + Byte => JByte, + Double => JDouble, + Float => JFloat, + Integer => JInt, + Long => JLong, + Short => JShort +} +import java.math.{ BigDecimal => JBigDecimal, BigInteger => JBigInteger } + +class JavaPrim() extends AnyFunSpec with JsonMatchers: + + describe(colorString("---------------------------\n: Java Primitive Tests :\n---------------------------", Console.YELLOW)) { + describe(colorString("+++ Positive Tests +++")) { + it("BigDecimal must work") { + val inst = SampleJBigDecimal( + JBigDecimal.ZERO, + JBigDecimal.ONE, + JBigDecimal.TEN, + new JBigDecimal( + "0.1499999999999999944488848768742172978818416595458984375" + ), + null + ) + val js = sj[SampleJBigDecimal].toJson(inst) + js should matchJson("""{"bd1":0,"bd2":1,"bd3":10,"bd4":0.1499999999999999944488848768742172978818416595458984375,"bd5":null}""") + // inst shouldEqual ScalaJack.read[SampleJBigDecimal](js) + } + + it("BigInteger must work") { + val inst = SampleJBigInteger( + JBigInteger.ZERO, + JBigInteger.ONE, + JBigInteger.TEN, + new JBigInteger("-90182736451928374653345"), + new JBigInteger("90182736451928374653345"), + new JBigInteger("0"), + null + ) + val js = sj[SampleJBigInteger].toJson(inst) + js should matchJson("""{"bi1":0,"bi2":1,"bi3":10,"bi4":-90182736451928374653345,"bi5":90182736451928374653345,"bi6":0,"bi7":null}""") + // inst shouldEqual ScalaJack.read[SampleJBigInteger](js) + } + + it("Boolean must work") { + val inst = SampleJBoolean(JBoolean.TRUE, JBoolean.FALSE, true, false, null) + val js = sj[SampleJBoolean].toJson(inst) + js should matchJson("""{"bool1":true,"bool2":false,"bool3":true,"bool4":false,"bool5":null}""") + // inst shouldEqual ScalaJack.read[SampleJBoolean](js) + } + + it("Byte must work") { + val inst = SampleJByte( + JByte.MAX_VALUE, + JByte.MIN_VALUE, + 0.asInstanceOf[Byte], + 64.asInstanceOf[Byte], + null + ) + val js = sj[SampleJByte].toJson(inst) + js should matchJson("""{"b1":127,"b2":-128,"b3":0,"b4":64,"b5":null}""") + // inst shouldEqual ScalaJack.read[SampleJByte](js) + } + + it("Character must work") { + val inst = SampleJChar('Z', '\u20A0', null) + val js = sj[SampleJChar].toJson(inst) + js should matchJson("""{"c1":"Z","c2":"\""" + """u20a0","c3":null}""") + // inst shouldEqual ScalaJack.read[SampleJChar](js) + } + + it("Double must work") { + val inst = SampleJDouble( + JDouble.MAX_VALUE, + JDouble.MIN_VALUE, + 0.0, + -123.4567, + null + ) + val js = sj[SampleJDouble].toJson(inst) + js should matchJson("""{"d1":1.7976931348623157E308,"d2":4.9E-324,"d3":0.0,"d4":-123.4567,"d5":null}""") + // inst shouldEqual ScalaJack.read[SampleJDouble](js) + } + + it("Float must work") { + val inst = SampleJFloat( + JFloat.MAX_VALUE, + JFloat.MIN_VALUE, + 0.0F, + -123.4567F, + null + ) + val js = sj[SampleJFloat].toJson(inst) + js should matchJson("""{"f1":3.4028235E38,"f2":1.4E-45,"f3":0.0,"f4":-123.4567,"f5":null}""") + // inst shouldEqual ScalaJack.read[SampleJFloat](js) + } + + it("Integer must work") { + val inst = SampleJInt(JInt.MAX_VALUE, JInt.MIN_VALUE, 0, 123, null) + val js = sj[SampleJInt].toJson(inst) + js should matchJson("""{"i1":2147483647,"i2":-2147483648,"i3":0,"i4":123,"i5":null}""") + // inst shouldEqual ScalaJack.read[SampleJInt](js) + } + + it("Long must work") { + val inst = SampleJLong(JLong.MAX_VALUE, JLong.MIN_VALUE, 0L, 123L, null) + val js = sj[SampleJLong].toJson(inst) + js should matchJson("""{"l1":9223372036854775807,"l2":-9223372036854775808,"l3":0,"l4":123,"l5":null}""") + // inst shouldEqual ScalaJack.read[SampleJLong](js) + } + + it("Number must work") { + val inst = SampleJNumber( + JByte.valueOf("-128"), + JByte.valueOf("127"), + JShort.valueOf("-32768"), + JShort.valueOf("32767"), + JInt.valueOf("-2147483648"), + JInt.valueOf("2147483647"), + JLong.valueOf("-9223372036854775808"), + JLong.valueOf("9223372036854755807"), + null, //new JBigInteger("9923372036854755810"), + JByte.valueOf("0"), + JFloat.valueOf("3.4e-038"), + JFloat.valueOf("3.4e+038"), + JDouble.valueOf("1.7e-308"), + JDouble.valueOf("1.7e+308"), + null, //new JBigDecimal("1.8e+308"), + JFloat.valueOf("0.0"), + null + ) + val js = sj[SampleJNumber].toJson(inst) + js should matchJson("""{"n1":-128,"n2":127,"n3":-32768,"n4":32767,"n5":-2147483648,"n6":2147483647,"n7":-9223372036854775808,"n8":9223372036854755807,"n9":null,"n10":0,"n11":3.4E-38,"n12":3.4E38,"n13":1.7E-308,"n14":1.7E308,"n15":null,"n16":0.0,"n17":null}""") + // inst shouldEqual ScalaJack.read[SampleJNumber](js) + } + + it("Short must work") { + val inst = SampleJShort( + JShort.MAX_VALUE, + JShort.MIN_VALUE, + 0.asInstanceOf[Short], + 123.asInstanceOf[Short], + null + ) + val js = sj[SampleJShort].toJson(inst) + js should matchJson("""{"s1":32767,"s2":-32768,"s3":0,"s4":123,"s5":null}""") + // inst shouldEqual ScalaJack.read[SampleJShort](js) + } + } + } + +/* + + //-------------------------------------------------------- + + + test("BigDecimal must break") { + describe("--- Negative Tests ---") + val js = + """{"bd1":0,"bd2":1,"bd3":10,"bd4":"0.1499999999999999944488848768742172978818416595458984375","bd5":null}""".asInstanceOf[JSON] + val msg = + """Expected a Number here + |{"bd1":0,"bd2":1,"bd3":10,"bd4":"0.149999999999999994448884876874217297881841... + |--------------------------------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ + sj.read[SampleJBigDecimal](js) + } + } + + test("BigInt must break") { + val js = + """{"bi1":"0","bi2":1,"bi3":10,"bi4":-90182736451928374653345,"bi5":90182736451928374653345,"bi6":0,"bi7":null}""".asInstanceOf[JSON] + val msg = + """Expected a Number here + |{"bi1":"0","bi2":1,"bi3":10,"bi4":-90182736451928374653345,"bi5":901827364519... + |-------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ + sj.read[SampleJBigInteger](js) + } + } + + test("Boolean must break") { + val js = """{"bool1":true,"bool2":false,"bool3":true,"bool4":"false","bool5":null}""".asInstanceOf[JSON] + val msg = + """Expected a Boolean here + |{"bool1":true,"bool2":false,"bool3":true,"bool4":"false","bool5":null} + |-------------------------------------------------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ + sj.read[SampleJBoolean](js) + } + } + + test("Byte must break") { + val js = """{"b1":127,"b2":-128,"b3":false,"b4":64,"b5":null}""".asInstanceOf[JSON] + val msg = """Expected a Number here + |{"b1":127,"b2":-128,"b3":false,"b4":64,"b5":null} + |-------------------------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ + sj.read[SampleJByte](js) + } + } + + test("Char must break") { + val js = """{"c1":"Z","c2":3,"c3":null}""".asInstanceOf[JSON] + val msg = """Expected a String here + |{"c1":"Z","c2":3,"c3":null} + |---------------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ + sj.read[SampleJChar](js) + } + val js2 = """{"c1":"Z","c2":"","c3":null}""".asInstanceOf[JSON] + val msg2 = """Tried to read a Character but empty string found + |{"c1":"Z","c2":"","c3":null} + |----------------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ + sj.read[SampleJChar](js2) + } + } + + test("Double must break") { + val js = + """{"d1":1.7976931348623157E308,"d2":4.9E-324,"d3":"0.0","d4":-123.4567,"d5":null}""".asInstanceOf[JSON] + val msg = + """Expected a Number here + |{"d1":1.7976931348623157E308,"d2":4.9E-324,"d3":"0.0","d4":-123.4567,"d5":null} + |------------------------------------------------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ + sj.read[SampleJDouble](js) + } + } + + test("Float must break") { + val js = + """{"f1":3.4028235E38,"f2":"1.4E-45","f3":0.0,"f4":-123.4567,"f5":null}""".asInstanceOf[JSON] + val msg = + """Expected a Number here + |{"f1":3.4028235E38,"f2":"1.4E-45","f3":0.0,"f4":-123.4567,"f5":null} + |------------------------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ + sj.read[SampleJFloat](js) + } + } + + test("Int must break") { + val js = """{"i1":2147483647,"i2":-2147483648,"i3":false,"i4":123,"i5":null}""".asInstanceOf[JSON] + val msg = + """Expected a Number here + |{"i1":2147483647,"i2":-2147483648,"i3":false,"i4":123,"i5":null} + |---------------------------------------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ + sj.read[SampleJInt](js) + } + val js2 = """{"i1":2147483647,"i2":-2147483648,"i3":0.3,"i4":123,"i5":null}""".asInstanceOf[JSON] + interceptMessage[java.lang.NumberFormatException]("For input string: \"0.3\""){ + sj.read[SampleJInt](js2) + } + } + + test("Long must break") { + val js = + """{"l1":9223372036854775807,"l2":-9223372036854775808,"l3":"0","l4":123,"l5":null}""".asInstanceOf[JSON] + val msg = + """Expected a Number here + |...23372036854775807,"l2":-9223372036854775808,"l3":"0","l4":123,"l5":null} + |----------------------------------------------------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ + sj.read[SampleJLong](js) + } + val js2 = + """{"l1":9223372036854775807,"l2":-9223372036854775808,"l3":0.3,"l4":123,"l5":null}""".asInstanceOf[JSON] + interceptMessage[java.lang.NumberFormatException]("For input string: \"0.3\""){ + sj.read[SampleJLong](js2) + } + } + + test("Number must break") { + val js = """{"n1":-128,"n2":127,"n3":"-32768","n4":32767,"n5":-2147483648,"n6":2147483647,"n7":-9223372036854775808,"n8":9223372036854755807,"n9":9923372036854755810,"n10":0,"n11":3.4E-38,"n12":3.4E38,"n13":1.7E-308,"n14":1.7E308,"n15":1.8E+308,"n16":0.0,"n17":null}""".asInstanceOf[JSON] + val msg = + """Expected a Number here + |{"n1":-128,"n2":127,"n3":"-32768","n4":32767,"n5":-2147483648,"n6":2147483647... + |-------------------------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ + sj.read[SampleJNumber](js) + } + } + + test("Short must break") { + val js = """{"s1":false,"s2":-32768,"s3":0,"s4":123,"s5":null}""".asInstanceOf[JSON] + val msg = """Expected a Number here + |{"s1":false,"s2":-32768,"s3":0,"s4":123,"s5":null} + |------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ + sj.read[SampleJShort](js) + } + val js2 = """{"s1":2.3,"s2":-32768,"s3":0,"s4":123,"s5":null}""".asInstanceOf[JSON] + interceptMessage[java.lang.NumberFormatException]("For input string: \"2.3\""){ + sj.read[SampleJShort](js2) + } + } +*/ \ No newline at end of file diff --git a/src/test/scala/co.blocke.scalajack/json/primitives/Model.scala b/src/test/scala/co.blocke.scalajack/json/primitives/Model.scala index ae207055..ee156366 100644 --- a/src/test/scala/co.blocke.scalajack/json/primitives/Model.scala +++ b/src/test/scala/co.blocke.scalajack/json/primitives/Model.scala @@ -7,15 +7,6 @@ import java.math.{BigDecimal as JBigDecimal, BigInteger as JBigInteger} import java.time.* import scala.math.* -// === Scala -case class SampleBigDecimal(bd1: BigDecimal, bd2: BigDecimal, bd3: BigDecimal, bd4: BigDecimal, bd5: BigDecimal, bd6: BigDecimal) -case class SampleBigInt(bi1: BigInt, bi2: BigInt, bi3: BigInt, bi4: BigInt) -case class SampleBinary(b1: Array[Byte], b2: Array[Byte]) -case class SampleBoolean(bool1: Boolean, bool2: Boolean) -case class SampleByte(b1: Byte, b2: Byte, b3: Byte, b4: Byte) -case class SampleChar(c1: Char, c2: Char, c3: Char) -case class SampleDouble(d1: Double, d2: Double, d3: Double, d4: Double) - object Size extends Enumeration { val Small, Medium, Large = Value } @@ -44,6 +35,13 @@ case class Plane(numberOfEngines: Int) extends Vehicle case class Ride(wheels: Vehicle) case class Favorite(flavor: Flavor) +// === Scala +case class SampleBigDecimal(bd1: BigDecimal, bd2: BigDecimal, bd3: BigDecimal, bd4: BigDecimal, bd5: BigDecimal, bd6: BigDecimal) +case class SampleBigInt(bi1: BigInt, bi2: BigInt, bi3: BigInt, bi4: BigInt) +case class SampleBoolean(bool1: Boolean, bool2: Boolean) +case class SampleByte(b1: Byte, b2: Byte, b3: Byte, b4: Byte) +case class SampleChar(c1: Char, c2: Char, c3: Char) +case class SampleDouble(d1: Double, d2: Double, d3: Double, d4: Double) case class SampleFloat(f1: Float, f2: Float, f3: Float, f4: Float) case class SampleInt(i1: Int, i2: Int, i3: Int, i4: Int) case class SampleLong(l1: Long, l2: Long, l3: Long, l4: Long) @@ -70,10 +68,16 @@ case class SampleInstant(i1: Instant, i2: Instant, i3: Instant, i4: Instant, i5: case class SampleLocalDateTime(d1: LocalDateTime, d2: LocalDateTime, d3: LocalDateTime, d4: LocalDateTime) case class SampleLocalDate(d1: LocalDate, d2: LocalDate, d3: LocalDate, d4: LocalDate) case class SampleLocalTime(d1: LocalTime, d2: LocalTime, d3: LocalTime, d4: LocalTime, d5: LocalTime, d6: LocalTime) +case class SampleMonthDay(m1: MonthDay, m2: MonthDay) case class SampleOffsetDateTime(o1: OffsetDateTime, o2: OffsetDateTime, o3: OffsetDateTime, o4: OffsetDateTime) case class SampleOffsetTime(o1: OffsetTime, o2: OffsetTime, o3: OffsetTime, o4: OffsetTime) case class SamplePeriod(p1: Period, p2: Period, p3: Period) +case class SampleYear(y1: Year, y2: Year, y3: Year, y4: Year) +case class SampleYearMonth(y1: YearMonth, y2: YearMonth) case class SampleZonedDateTime(o1: ZonedDateTime, o2: ZonedDateTime) +case class SampleZoneId(z1: ZoneId, z2: ZoneId) +case class SampleZoneOffset(z1: ZoneOffset, z2: ZoneOffset) +// TODO: Missing Year, MonthYear, ZoneId, ZoneOffset, others? // === Any primitives case class AnyShell(a: Any) diff --git a/src/test/scala/co.blocke.scalajack/json/primitives/ScalaPrim.scala b/src/test/scala/co.blocke.scalajack/json/primitives/ScalaPrim.scala index b0eb163b..e3cdc112 100644 --- a/src/test/scala/co.blocke.scalajack/json/primitives/ScalaPrim.scala +++ b/src/test/scala/co.blocke.scalajack/json/primitives/ScalaPrim.scala @@ -2,12 +2,11 @@ package co.blocke.scalajack package json package primitives +import ScalaJack.* import co.blocke.scala_reflection.* import scala.math.BigDecimal import java.util.UUID import TestUtil.* -// import munit.* -// import munit.internal.console import org.scalatest.funspec.AnyFunSpec import org.scalatest.matchers.should.Matchers.* import org.scalatest.* @@ -28,9 +27,9 @@ class ScalaPrim() extends AnyFunSpec with JsonMatchers: null ) - val js = ScalaJack.write(inst) + val js = sj[SampleBigDecimal].toJson(inst) js should matchJson("""{"bd1":123,"bd2":1.23,"bd3":0,"bd4":123.456,"bd5":0.1499999999999999944488848768742172978818416595458984375,"bd6":null}""") - inst shouldEqual ScalaJack.read[SampleBigDecimal](js) + // inst shouldEqual ScalaJack.read[SampleBigDecimal](js) } it("BigInt must work") { @@ -40,88 +39,77 @@ class ScalaPrim() extends AnyFunSpec with JsonMatchers: BigInt(0), null ) - val js = ScalaJack.write(inst) + val js = sj[SampleBigInt].toJson(inst) js should matchJson("""{"bi1":-90182736451928374653345,"bi2":90182736451928374653345,"bi3":0,"bi4":null}""") - inst shouldEqual ScalaJack.read[SampleBigInt](js) - } - - it("Binary must work") { - val inst = SampleBinary( - null, - hexStringToByteArray("e04fd020ea3a6910a2d808002b30309d") - ) - val js = ScalaJack.write(inst) - val inst2 = ScalaJack.read[SampleBinary](js) - js should matchJson("""{"b1":null,"b2":[-32,79,-48,32,-22,58,105,16,-94,-40,8,0,43,48,48,-99]}""") - inst2.b1 shouldEqual null - inst.b2.toList shouldEqual inst2.b2.toList + // inst shouldEqual ScalaJack.read[SampleBigInt](js) } it("Boolean must work (not nullable)") { val inst = SampleBoolean(bool1 = true, bool2 = false) - val js = ScalaJack.write(inst) + val js = sj[SampleBoolean].toJson(inst) js should matchJson("""{"bool1":true,"bool2":false}""") - inst shouldEqual ScalaJack.read[SampleBoolean](js) + // inst shouldEqual ScalaJack.read[SampleBoolean](js) } it("Byte must work (not nullable)") { val inst = SampleByte(Byte.MaxValue, Byte.MinValue, 0, 64) - val js = ScalaJack.write(inst) + val js = sj[SampleByte].toJson(inst) js should matchJson("""{"b1":127,"b2":-128,"b3":0,"b4":64}""") - inst shouldEqual ScalaJack.read[SampleByte](js) + // inst shouldEqual ScalaJack.read[SampleByte](js) } it("Char must work (not nullable)") { val inst = SampleChar(Char.MaxValue, 'Z', '\u20A0') - val js = ScalaJack.write(inst) + val js = sj[SampleChar].toJson(inst) js should matchJson("""{"c1":"\""" + """uffff","c2":"Z","c3":"\""" + """u20a0"}""") - inst shouldEqual ScalaJack.read[SampleChar](js) + // inst shouldEqual ScalaJack.read[SampleChar](js) } it("Double must work (not nullable)") { val inst = SampleDouble(Double.MaxValue, Double.MinValue, 0.0, -123.4567) - val js = ScalaJack.write(inst) + val js = sj[SampleDouble].toJson(inst) js should matchJson("""{"d1":1.7976931348623157E308,"d2":-1.7976931348623157E308,"d3":0.0,"d4":-123.4567}""") - inst shouldEqual ScalaJack.read[SampleDouble](js) + // inst shouldEqual ScalaJack.read[SampleDouble](js) } - it("Float must work") { + it("Float must work (not nullable)") { val inst = SampleFloat(Float.MaxValue, Float.MinValue, 0.0f, -123.4567f) - val js = ScalaJack.write(inst) + val js = sj[SampleFloat].toJson(inst) js should matchJson("""{"f1":3.4028235E38,"f2":-3.4028235E38,"f3":0.0,"f4":-123.4567}""") - inst shouldEqual ScalaJack.read[SampleFloat](js) + // inst shouldEqual ScalaJack.read[SampleFloat](js) } it("Int must work (not nullable)") { val inst = SampleInt(Int.MaxValue, Int.MinValue, 0, 123) - val js = ScalaJack.write(inst) + val js = sj[SampleInt].toJson(inst) js should matchJson("""{"i1":2147483647,"i2":-2147483648,"i3":0,"i4":123}""") - inst shouldEqual ScalaJack.read[SampleInt](js) + // inst shouldEqual ScalaJack.read[SampleInt](js) } it("Long must work (not nullable)") { val inst = SampleLong(Long.MaxValue, Long.MinValue, 0L, 123L) - val js = ScalaJack.write(inst) + val js = sj[SampleLong].toJson(inst) js should matchJson("""{"l1":9223372036854775807,"l2":-9223372036854775808,"l3":0,"l4":123}""") - inst shouldEqual ScalaJack.read[SampleLong](js) + // inst shouldEqual ScalaJack.read[SampleLong](js) } it("Short must work (not nullable)") { val inst = SampleShort(Short.MaxValue, Short.MinValue, 0, 123) - val js = ScalaJack.write(inst) + val js = sj[SampleShort].toJson(inst) js should matchJson("""{"s1":32767,"s2":-32768,"s3":0,"s4":123}""") - inst shouldEqual ScalaJack.read[SampleShort](js) + // inst shouldEqual ScalaJack.read[SampleShort](js) } it("String must work") { val inst = SampleString("something\b\n\f\r\t☆", "", null) - val js = ScalaJack.write(inst) + val js = sj[SampleString].toJson(inst) // The weird '+' here is to break up the unicode so it won't be interpreted and wreck the test. js should matchJson("""{"s1":"something\b\n\f\r\t\""" + """u2606","s2":"","s3":null}""") - inst shouldEqual ScalaJack.read[SampleString](js) + // inst shouldEqual ScalaJack.read[SampleString](js) } + /* it("UUID must work") { val inst = SampleUUID( null, @@ -298,5 +286,6 @@ class ScalaPrim() extends AnyFunSpec with JsonMatchers: val thrown = the[JsonParseError] thrownBy ScalaJack.read[SampleUUID](js) thrown.getMessage should equal(msg) } + */ } } diff --git a/src/test/scala/co.blocke.scalajack/json/primitives/Simple.scala b/src/test/scala/co.blocke.scalajack/json/primitives/Simple.scala new file mode 100644 index 00000000..a46e9939 --- /dev/null +++ b/src/test/scala/co.blocke.scalajack/json/primitives/Simple.scala @@ -0,0 +1,357 @@ +package co.blocke.scalajack +package json +package primitives + +import ScalaJack.* +import co.blocke.scala_reflection.* +import org.scalatest.funspec.AnyFunSpec +import org.scalatest.matchers.should.Matchers.* +import org.scalatest.* +import TestUtil.* +import java.time._ +import java.util.UUID + +class Simple() extends AnyFunSpec with JsonMatchers: + + describe(colorString("-----------------------\n: Simple Type Tests :\n-----------------------", Console.YELLOW)) { + describe(colorString("+++ Positive Tests +++")) { + it("Duration must work") { + val inst = SampleDuration(Duration.ZERO, Duration.parse("P2DT3H4M"), null) + val js = sj[SampleDuration].toJson(inst) + js should matchJson("""{"d1":"PT0S","d2":"PT51H4M","d3":null}""") + // inst shouldEqual ScalaJack.read[SampleDuration](js) + } + + it("Instant must work") { + val inst = SampleInstant( + Instant.EPOCH, + Instant.MAX, + Instant.MIN, + Instant.parse("2007-12-03T10:15:30.00Z"), + null + ) + val js = sj[SampleInstant].toJson(inst) + js should matchJson("""{"i1":"1970-01-01T00:00:00Z","i2":"+1000000000-12-31T23:59:59.999999999Z","i3":"-1000000000-01-01T00:00:00Z","i4":"2007-12-03T10:15:30Z","i5":null}""") + // inst shouldEqual ScalaJack.read[SampleInstant](js) + } + + it("LocalDate must work") { + val inst = SampleLocalDate( + LocalDate.MAX, + LocalDate.MIN, + LocalDate.parse("2007-12-03"), + null + ) + val js = sj[SampleLocalDate].toJson(inst) + js should matchJson("""{"d1":"+999999999-12-31","d2":"-999999999-01-01","d3":"2007-12-03","d4":null}""") + // inst shouldEqual ScalaJack.read[SampleLocalDate](js) + } + + it("LocalDateTime must work") { + val inst = SampleLocalDateTime( + LocalDateTime.MAX, + LocalDateTime.MIN, + LocalDateTime.parse("2007-12-03T10:15:30"), + null + ) + val js = sj[SampleLocalDateTime].toJson(inst) + js should matchJson("""{"d1":"+999999999-12-31T23:59:59.999999999","d2":"-999999999-01-01T00:00:00","d3":"2007-12-03T10:15:30","d4":null}""") + // inst shouldEqual ScalaJack.read[SampleLocalDateTime](js) + } + + it("LocalTime must work") { + val inst = SampleLocalTime( + LocalTime.MAX, + LocalTime.MIN, + LocalTime.MIDNIGHT, + LocalTime.NOON, + LocalTime.parse("10:15:30"), + null + ) + val js = sj[SampleLocalTime].toJson(inst) + js should matchJson("""{"d1":"23:59:59.999999999","d2":"00:00:00","d3":"00:00:00","d4":"12:00:00","d5":"10:15:30","d6":null}""") + // inst shouldEqual ScalaJack.read[SampleLocalTime](js) + } + + it("MonthDay must work") { + val inst = SampleMonthDay( + MonthDay.of(7,1), + null + ) + val js = sj[SampleMonthDay].toJson(inst) + js should matchJson("""{"m1":"--07-01","m2":null}""") + // inst shouldEqual ScalaJack.read[SampleMonthDay](js) + } + + it("OffsetDateTime must work") { + val inst = SampleOffsetDateTime( + OffsetDateTime.MAX, + OffsetDateTime.MIN, + OffsetDateTime.parse("2007-12-03T10:15:30+01:00"), + null + ) + val js = sj[SampleOffsetDateTime].toJson(inst) + js should matchJson("""{"o1":"+999999999-12-31T23:59:59.999999999-18:00","o2":"-999999999-01-01T00:00:00+18:00","o3":"2007-12-03T10:15:30+01:00","o4":null}""") + // inst shouldEqual ScalaJack.read[SampleOffsetDateTime](js) + } + + it("OffsetTime must work") { + val inst = SampleOffsetTime( + OffsetTime.MAX, + OffsetTime.MIN, + OffsetTime.parse("10:15:30+01:00"), + null + ) + val js = sj[SampleOffsetTime].toJson(inst) + js should matchJson("""{"o1":"23:59:59.999999999-18:00","o2":"00:00:00+18:00","o3":"10:15:30+01:00","o4":null}""") + // inst shouldEqual ScalaJack.read[SampleOffsetTime](js) + } + + it("Period must work") { + val inst = SamplePeriod(Period.ZERO, Period.parse("P1Y2M3D"), null) + val js = sj[SamplePeriod].toJson(inst) + js should matchJson("""{"p1":"P0D","p2":"P1Y2M3D","p3":null}""") + // inst shouldEqual ScalaJack.read[SamplePeriod](js) + } + + it("Year must work") { + val inst = SampleYear(Year.of(Year.MAX_VALUE), Year.of(Year.MIN_VALUE), Year.parse("2020"), null) + val js = sj[SampleYear].toJson(inst) + js should matchJson("""{"y1":"999999999","y2":"-999999999","y3":"2020","y4":null}""") + // inst shouldEqual ScalaJack.read[SampleYear](js) + } + + it("YearMonth must work") { + val inst = SampleYearMonth(YearMonth.of(2020,7), null) + val js = sj[SampleYearMonth].toJson(inst) + js should matchJson("""{"y1":"2020-07","y2":null}""") + // inst shouldEqual ScalaJack.read[SampleYearMonth](js) + } + + it("ZonedDateTime must work") { + val inst = SampleZonedDateTime( + ZonedDateTime.parse("2007-12-03T10:15:30+01:00[Europe/Paris]"), + null + ) + val js = sj[SampleZonedDateTime].toJson(inst) + js should matchJson("""{"o1":"2007-12-03T10:15:30+01:00[Europe/Paris]","o2":null}""") + // inst shouldEqual ScalaJack.read[SampleZonedDateTime](js) + } + + it("ZonedId must work") { + val inst = SampleZoneId( + ZoneId.of("America/Puerto_Rico"), + null + ) + val js = sj[SampleZoneId].toJson(inst) + js should matchJson("""{"z1":"America/Puerto_Rico","z2":null}""") + // inst shouldEqual ScalaJack.read[SampleZoneId](js) + } + + it("ZoneOffset must work") { + val ldt = LocalDateTime.parse("2007-12-03T10:15:30") + val zone = ZoneId.of("Europe/Berlin") + val zoneOffSet = zone.getRules().getOffset(ldt) + val inst = SampleZoneOffset( + null, + zoneOffSet + ) + val js = sj[SampleZoneOffset].toJson(inst) + js should matchJson("""{"z1":null,"z2":"+01:00"}""") + // inst shouldEqual ScalaJack.read[SampleZoneOffset](js) + } + + it("UUID must work") { + val inst = SampleUUID( + null, + UUID.fromString("580afe0d-81c0-458f-9e09-4486c7af0fe9") + ) + val js = sj[SampleUUID].toJson(inst) + js should matchJson("""{"u1":null,"u2":"580afe0d-81c0-458f-9e09-4486c7af0fe9"}""") + // inst shouldEqual ScalaJack.read[SampleUUID](js) + } + } + } + +/* + + + test("Duration must break") { + describe("--- Negative Tests ---") + + val js = """{"d1":"PT0S","d2":21,"d3":null}""".asInstanceOf[JSON] + val msg = """Expected a String here + |{"d1":"PT0S","d2":21,"d3":null} + |------------------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ + sj.read[SampleDuration](js) + } + val js2 = """{"d1":"PT0S","d2":"bogus","d3":null}""".asInstanceOf[JSON] + val msg2 = """Failed to parse Duration from input 'bogus' + |{"d1":"PT0S","d2":"bogus","d3":null} + |------------------------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ + sj.read[SampleDuration](js2) + } + } + + test("Instant must break") { + val js = + """{"i1":"1970-01-01T00:00:00Z","i2":false,"i3":"-1000000000-01-01T00:00:00Z","i4":"2007-12-03T10:15:30Z","i5":null}""".asInstanceOf[JSON] + val msg = + """Expected a String here + |{"i1":"1970-01-01T00:00:00Z","i2":false,"i3":"-1000000000-01-01T00:00:00Z","i... + |----------------------------------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ + sj.read[SampleInstant](js) + } + val js2 = + """{"i1":"1970-01-01T00:00:00Z","i2":"bogus","i3":"-1000000000-01-01T00:00:00Z","i4":"2007-12-03T10:15:30Z","i5":null}""".asInstanceOf[JSON] + val msg2 = + """Failed to parse Instant from input 'bogus' + |{"i1":"1970-01-01T00:00:00Z","i2":"bogus","i3":"-1000000000-01-01T00:00:00Z",... + |----------------------------------------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ + sj.read[SampleInstant](js2) + } + } + + test("LocalDateTime must break") { + val js = + """{"d1":-1,"d2":"-999999999-01-01T00:00:00","d3":"2007-12-03T10:15:30","d4":null}""".asInstanceOf[JSON] + val msg = + """Expected a String here + |{"d1":-1,"d2":"-999999999-01-01T00:00:00","d3":"2007-12-03T10:15:30","d4":null} + |------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ + sj.read[SampleLocalDateTime](js) + } + val js2 = + """{"d1":"bogus","d2":"-999999999-01-01T00:00:00","d3":"2007-12-03T10:15:30","d1":null}""".asInstanceOf[JSON] + val msg2 = + """Failed to parse LocalDateTime from input 'bogus' + |{"d1":"bogus","d2":"-999999999-01-01T00:00:00","d3":"2007-12-03T10:15:30","d1... + |------------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ + sj.read[SampleLocalDateTime](js2) + } + } + + test("LocalDate must break") { + val js = + """{"d1":-1,"d2":"-999999999-01-01","d3":"2007-12-03","d4":null}""".asInstanceOf[JSON] + val msg = """Expected a String here + |{"d1":-1,"d2":"-999999999-01-01","d3":"2007-12-03","d4":null} + |------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ + sj.read[SampleLocalDate](js) + } + val js2 = + """{"d1":"bogus","d2":"-999999999-01-01","d3":"2007-12-03","d4":null}""".asInstanceOf[JSON] + val msg2 = + """Failed to parse LocalDate from input 'bogus' + |{"d1":"bogus","d2":"-999999999-01-01","d3":"2007-12-03","d4":null} + |------------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ + sj.read[SampleLocalDate](js2) + } + } + + test("LocalTime must break") { + val js = + """{"d1":"23:59:59.999999999","d2":"00:00:00","d3":"00:00:00","d4":"12:00:00","d5":false,"d6":null}""".asInstanceOf[JSON] + val msg = + """Expected a String here + |...:"00:00:00","d3":"00:00:00","d4":"12:00:00","d5":false,"d6":null} + |----------------------------------------------------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ + sj.read[SampleLocalTime](js) + } + val js2 = + """{"d1":"23:59:59.999999999","d2":"00:00:00","d3":"00:00:00","d4":"12:00:00","d5":"Bogus","d6":null}""".asInstanceOf[JSON] + val msg2 = + """Failed to parse LocalTime from input 'Bogus' + |...0:00","d3":"00:00:00","d4":"12:00:00","d5":"Bogus","d6":null} + |----------------------------------------------------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ + sj.read[SampleLocalTime](js2) + } + } + + test("OffsetDateTime must break") { + val js = + """{"o1":"+999999999-12-31T23:59:59.999999999-18:00","o2":2,"o3":"2007-12-03T10:15:30+01:00","o4":null}""".asInstanceOf[JSON] + val msg = + """Expected a String here + |..."+999999999-12-31T23:59:59.999999999-18:00","o2":2,"o3":"2007-12-03T10:15:30... + |----------------------------------------------------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ + sj.read[SampleOffsetDateTime](js) + } + val js2 = + """{"o1":"+999999999-12-31T23:59:59.999999999-18:00","o2":"-999999999-01T00:00:00+18:00","o3":"2007-12-03T10:15:30+01:00","o4":null}""".asInstanceOf[JSON] + val msg2 = + """Failed to parse OffsetDateTime from input '-999999999-01T00:00:00+18:00' + |...9999999-18:00","o2":"-999999999-01T00:00:00+18:00","o3":"2007-12-03T10:15:30... + |----------------------------------------------------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ + sj.read[SampleOffsetDateTime](js2) + } + } + + test("OffsetTime must break") { + val js = + """{"o1":"23:59:59.999999999-18:00","o2":false,"o3":"10:15:30+01:00","o4":null}""".asInstanceOf[JSON] + val msg = + """Expected a String here + |{"o1":"23:59:59.999999999-18:00","o2":false,"o3":"10:15:30+01:00","o4":null} + |--------------------------------------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ + sj.read[SampleOffsetTime](js) + } + val js2 = + """{"o1":"23:59:59.999999999-18:00","o2":"00:00:00:00+18:00","o3":"10:15:30+01:00","o4":null}""".asInstanceOf[JSON] + val msg2 = + """Failed to parse OffsetTime from input '00:00:00:00+18:00' + |...23:59:59.999999999-18:00","o2":"00:00:00:00+18:00","o3":"10:15:30+01:00","o4... + |----------------------------------------------------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ + sj.read[SampleOffsetTime](js2) + } + } + + test("Period must break") { + val js = """{"p1":"P0D","p2":5,"p3":null}""".asInstanceOf[JSON] + val msg = """Expected a String here + |{"p1":"P0D","p2":5,"p3":null} + |-----------------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ + sj.read[SamplePeriod](js) + } + val js2 = """{"p1":"P0D","p2":"bogus","p3":null}""".asInstanceOf[JSON] + val msg2 = """Failed to parse Period from input 'bogus' + |{"p1":"P0D","p2":"bogus","p3":null} + |-----------------------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ + sj.read[SamplePeriod](js2) + } + } + + test("ZonedDateTime must break") { + val js = """{"o1":true,"o2":null}""".asInstanceOf[JSON] + val msg = """Expected a String here + |{"o1":true,"o2":null} + |------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ + sj.read[SampleZonedDateTime](js) + } + val js2 = """{"o1":"2007-12-03T10:15:30+01:00 Earth","o2":null}""".asInstanceOf[JSON] + val msg2 = + """Failed to parse ZonedDateTime from input '2007-12-03T10:15:30+01:00 Earth' + |{"o1":"2007-12-03T10:15:30+01:00 Earth","o2":null} + |--------------------------------------^""".stripMargin + interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ + sj.read[SampleZonedDateTime](js2) + } + } +*/ \ No newline at end of file From 25e7d4b4a9686a3ee179663679b6f2f5b7e1208a Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Wed, 6 Dec 2023 01:05:35 -0600 Subject: [PATCH 34/65] about a third thru write tests --- TODO.txt | 46 +- build.sbt | 2 +- .../java/co/blocke/scalajack/TypeHint.java | 5 +- .../co.blocke.scalajack/json/JsonConfig.scala | 95 ++- .../co.blocke.scalajack/json/JsonError.scala | 1 + .../json/writing/JsonCodecMaker.scala | 586 ++++++++++++++++-- .../json/writing/JsonOutput.scala | 12 + .../json/writing/JsonWriterRT.scalax | 361 ----------- .../json/writing/JsonWriter_old.scalax | 64 +- .../scala/co.blocke.scalajack/run/Play.scala | 9 +- .../co.blocke.scalajack/run/Record.scala | 16 +- .../json/collections/JavaCollSpec.scala | 120 ++++ .../json/collections/JavaMapSpec.scala | 129 ++++ .../json/collections/MapSpec.scala | 95 +++ .../json/collections/Model.scala | 22 + .../json/collections/SeqSetArraySpec.scala | 171 +++++ .../json/collections/TupleSpec.scala | 34 + .../co.blocke.scalajack/json/misc/Model.scala | 29 + .../json/misc/OptionLRTrySpec.scala | 188 ++++++ .../{JavaPrim.scala => JavaPrimSpec.scala} | 142 ++--- .../{ScalaPrim.scala => ScalaPrimSpec.scala} | 2 +- .../{Simple.scala => SimpleSpec.scala} | 112 ++-- 22 files changed, 1618 insertions(+), 623 deletions(-) delete mode 100644 src/main/scala/co.blocke.scalajack/json/writing/JsonWriterRT.scalax create mode 100644 src/test/scala/co.blocke.scalajack/json/collections/JavaCollSpec.scala create mode 100644 src/test/scala/co.blocke.scalajack/json/collections/JavaMapSpec.scala create mode 100644 src/test/scala/co.blocke.scalajack/json/collections/MapSpec.scala create mode 100644 src/test/scala/co.blocke.scalajack/json/collections/Model.scala create mode 100644 src/test/scala/co.blocke.scalajack/json/collections/SeqSetArraySpec.scala create mode 100644 src/test/scala/co.blocke.scalajack/json/collections/TupleSpec.scala create mode 100644 src/test/scala/co.blocke.scalajack/json/misc/Model.scala create mode 100644 src/test/scala/co.blocke.scalajack/json/misc/OptionLRTrySpec.scala rename src/test/scala/co.blocke.scalajack/json/primitives/{JavaPrim.scala => JavaPrimSpec.scala} (79%) rename src/test/scala/co.blocke.scalajack/json/primitives/{ScalaPrim.scala => ScalaPrimSpec.scala} (99%) rename src/test/scala/co.blocke.scalajack/json/primitives/{Simple.scala => SimpleSpec.scala} (89%) diff --git a/TODO.txt b/TODO.txt index 4021cf08..3fc2d442 100644 --- a/TODO.txt +++ b/TODO.txt @@ -2,7 +2,47 @@ Path to Performance ------------------- [ ] - Fix string value in JsonOutput to handle escaping " and anything else--maybe use apache library + (Make it a config option (for speed) to handle escaped string values or not--in the case where you know your strings are pure) -[ ] - Study Jsoniter and how it propagates its write config between compile-time and run-time (usage too) - Ideally we'd love to be able to see cfg at compile-time and geneate accordingly.... Right now, - ScalaJack's config is purely runtime, which is less convenient for us... \ No newline at end of file +[ ] - Add enum as a map key to MapSpec + +[ ] - Implement special Tuple handling for Either and write Seq/Tuple Either tests + + Fature Support: + Code Test + [*] [W] - Primitive types + [*] [W] - Simple Types + + [*] [W] - Seq/Set/Array + [*] [W] - Map + [*] [W] - Java Collections + [*] [W] - Java Map + [*] [W] - Tuple + + [*] [ ] - Scala case class + [*] [ ] - Scala non-case class + [*] [ ] - Java class + [*] [ ] - Sealed Trait + [*] [ ] - Sealed Abstract Class (handle like sealed trait....) + + [*] [W] - Option/Optional + [*] [ ] - Either + [*] [W] - Intersection + [*] [W] - Union + [*] [ ] - Enum + [*] [ ] - Enumeration + [*] [ ] - Java Enum + [*] [ ] - Alias type + [*] [ ] - Object + [*] [ ] - SelfRef + [*] [W] - TryRef + [*] [X] - Unknown (throw exception) + [*] [X] - Scala 2 (throw exception) + [*] [X] - TypeSymbol (throw exception) + [*] [W] - Value class (tested in MapSpec) + + [*] [ ] - type hint label mapping + [*] [ ] - type hint value mapping + + [ ] -- Streaming JSON write support + [ ] -- BigJSON support (eg. multi-gig file) \ No newline at end of file diff --git a/build.sbt b/build.sbt index 21a9dc1a..4353302f 100644 --- a/build.sbt +++ b/build.sbt @@ -35,7 +35,7 @@ lazy val root = project Test / parallelExecution := false, scalafmtOnCompile := !isCI, libraryDependencies ++= Seq( - "co.blocke" %% "scala-reflection" % "sj_fixes_3a7daa", + "co.blocke" %% "scala-reflection" % "sj_fixes_f43af7", "org.apache.commons" % "commons-text" % "1.10.0", "io.github.kitlangton" %% "neotype" % "0.0.9", "org.scalatest" %% "scalatest" % "3.2.17" % Test, diff --git a/src/main/java/co/blocke/scalajack/TypeHint.java b/src/main/java/co/blocke/scalajack/TypeHint.java index 9d958b9c..954f5997 100644 --- a/src/main/java/co/blocke/scalajack/TypeHint.java +++ b/src/main/java/co/blocke/scalajack/TypeHint.java @@ -4,5 +4,6 @@ @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) -public @interface TypeHint{} - +public @interface TypeHint{ + String hintValue(); +} diff --git a/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala b/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala index c77e6b48..c93fbf2b 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala @@ -8,34 +8,50 @@ import scala.quoted.* class JsonConfig private[scalajack] ( val noneAsNull: Boolean, - // forbidNullsInInput: Boolean = false, - // tryFailureHandling: TryOption = TryOption.NO_WRITE, - // undefinedFieldHandling: UndefinedValueOption = UndefinedValueOption.THROW_EXCEPTION, - // permissivePrimitives: Boolean = false, - // writeNonConstructorFields: Boolean = false, + // val forbidNullsInInput: Boolean = false, + val tryFailureHandling: TryPolicy, + val eitherLeftHandling: EitherLeftPolicy, + // val undefinedFieldHandling: UndefinedValueOption = UndefinedValueOption.THROW_EXCEPTION, + // val permissivePrimitives: Boolean = false, + val writeNonConstructorFields: Boolean, // -------------------------- val typeHintLabel: String, - val typeHintPolicy: TypeHintPolicy = TypeHintPolicy.SIMPLE_CLASSNAME + val typeHintPolicy: TypeHintPolicy, // -------------------------- - // enumsAsIds: Option[List[String]] = None // None=no enums as ids, Some(Nil)=all enums as ids, Some(List(...))=specified classes enums as ids + val enumsAsIds: Option[List[String]] // None=no enums as ids, Some(Nil)=all enums as ids, Some(List(...))=specified classes enums as ids ): def withNoneAsNull(nan: Boolean): JsonConfig = copy(noneAsNull = nan) + def withTryFailureHandling(tryPolicy: TryPolicy): JsonConfig = copy(tryFailureHandling = tryPolicy) + def withEitherLeftHandling(eitherPolicy: EitherLeftPolicy): JsonConfig = copy(eitherLeftHandling = eitherPolicy) + def withWriteNonConstructorFields(nonConstFlds: Boolean): JsonConfig = copy(writeNonConstructorFields = nonConstFlds) def withTypeHintLabel(label: String): JsonConfig = copy(typeHintLabel = label) def withTypeHintPolicy(hintPolicy: TypeHintPolicy): JsonConfig = copy(typeHintPolicy = hintPolicy) + def withEnumsAsIds(asIds: Option[List[String]]): JsonConfig = copy(enumsAsIds = asIds) private[this] def copy( noneAsNull: Boolean = noneAsNull, + tryFailureHandling: TryPolicy = tryFailureHandling, + eitherLeftHandling: EitherLeftPolicy = eitherLeftHandling, + writeNonConstructorFields: Boolean = writeNonConstructorFields, typeHintLabel: String = typeHintLabel, - typeHintPolicy: TypeHintPolicy = typeHintPolicy + typeHintPolicy: TypeHintPolicy = typeHintPolicy, + enumsAsIds: Option[List[String]] = enumsAsIds ): JsonConfig = new JsonConfig( noneAsNull, + tryFailureHandling, + eitherLeftHandling, + writeNonConstructorFields, typeHintLabel, - typeHintPolicy + typeHintPolicy, + enumsAsIds ) -enum TryOption: +enum TryPolicy: case AS_NULL, NO_WRITE, ERR_MSG_STRING, THROW_EXCEPTION +enum EitherLeftPolicy: + case AS_VALUE, AS_NULL, NO_WRITE, ERR_MSG_STRING, THROW_EXCEPTION + enum UndefinedValueOption: case AS_NULL, AS_SYMBOL, THROW_EXCEPTION @@ -45,8 +61,12 @@ enum TypeHintPolicy: object JsonConfig extends JsonConfig( noneAsNull = false, + tryFailureHandling = TryPolicy.NO_WRITE, + eitherLeftHandling = EitherLeftPolicy.NO_WRITE, + writeNonConstructorFields = true, typeHintLabel = "_hint", - typeHintPolicy = TypeHintPolicy.SIMPLE_CLASSNAME + typeHintPolicy = TypeHintPolicy.SIMPLE_CLASSNAME, + enumsAsIds = None ): import scala.quoted.FromExpr.* @@ -64,13 +84,14 @@ object JsonConfig JsonConfig( $noneAsNullE, // $forbitNullsInInputE, - // $tryFailureHandlerE, + $tryFailureHandlerE, + $eitherLeftHandlerE, // $undefinedFieldHandlingE, // $permissivePrimitivesE, - // $writeNonConstructorFieldsE, + $writeNonConstructorFieldsE, $typeHintLabelE, - $typeHintPolicyE - // $enumsAsIdsE + $typeHintPolicyE, + $enumsAsIdsE ) } => try @@ -78,14 +99,15 @@ object JsonConfig JsonConfig( extract("noneAsNull", noneAsNullE), // extract("forbitNullsInInput", forbitNullsInInputE), - // extract("tryFailureHandler", tryFailureHandlerE), + extract("tryFailureHandler", tryFailureHandlerE), + extract("eitherLeftHandler", eitherLeftHandlerE), // extract("undefinedFieldHandling", undefinedFieldHandlingE), // extract("permissivePrimitives", permissivePrimitivesE), - // extract("writeNonConstructorFields", writeNonConstructorFieldsE), + extract("writeNonConstructorFields", writeNonConstructorFieldsE), // extract2[String]("typeHintLabel", x) extract("typeHintLabel", typeHintLabelE), - extract("typeHintPolicy", typeHintPolicyE) - // extract("enumsAsIds", enumsAsIdsE) + extract("typeHintPolicy", typeHintPolicyE), + extract("enumsAsIds", enumsAsIdsE) ) ) catch { @@ -93,23 +115,38 @@ object JsonConfig println("ERROR: " + x.getMessage) None } - case '{ JsonConfig } => Some(JsonConfig) - case '{ ($x: JsonConfig).withNoneAsNull($v) } => Some(x.valueOrAbort.withNoneAsNull(v.valueOrAbort)) - case '{ ($x: JsonConfig).withTypeHintLabel($v) } => Some(x.valueOrAbort.withTypeHintLabel(v.valueOrAbort)) - case '{ ($x: JsonConfig).withTypeHintPolicy($v) } => Some(x.valueOrAbort.withTypeHintPolicy(v.valueOrAbort)) + case '{ JsonConfig } => Some(JsonConfig) + case '{ ($x: JsonConfig).withNoneAsNull($v) } => Some(x.valueOrAbort.withNoneAsNull(v.valueOrAbort)) + case '{ ($x: JsonConfig).withTryFailureHandling($v) } => Some(x.valueOrAbort.withTryFailureHandling(v.valueOrAbort)) + case '{ ($x: JsonConfig).withEitherLeftHandling($v) } => Some(x.valueOrAbort.withEitherLeftHandling(v.valueOrAbort)) + case '{ ($x: JsonConfig).withWriteNonConstructorFields($v) } => Some(x.valueOrAbort.withWriteNonConstructorFields(v.valueOrAbort)) + case '{ ($x: JsonConfig).withTypeHintLabel($v) } => Some(x.valueOrAbort.withTypeHintLabel(v.valueOrAbort)) + case '{ ($x: JsonConfig).withTypeHintPolicy($v) } => Some(x.valueOrAbort.withTypeHintPolicy(v.valueOrAbort)) + case '{ ($x: JsonConfig).withEnumsAsIds($v) } => Some(x.valueOrAbort.withEnumsAsIds(v.valueOrAbort)) case z => println("Z: " + z.show) None } - private[scalajack] given FromExpr[TryOption] with { - def unapply(x: Expr[TryOption])(using Quotes): Option[TryOption] = + private[scalajack] given FromExpr[TryPolicy] with { + def unapply(x: Expr[TryPolicy])(using Quotes): Option[TryPolicy] = + import quotes.reflect.* + x match + case '{ TryPolicy.AS_NULL } => Some(TryPolicy.AS_NULL) + case '{ TryPolicy.NO_WRITE } => Some(TryPolicy.NO_WRITE) + case '{ TryPolicy.ERR_MSG_STRING } => Some(TryPolicy.ERR_MSG_STRING) + case '{ TryPolicy.THROW_EXCEPTION } => Some(TryPolicy.THROW_EXCEPTION) + } + + private[scalajack] given FromExpr[EitherLeftPolicy] with { + def unapply(x: Expr[EitherLeftPolicy])(using Quotes): Option[EitherLeftPolicy] = import quotes.reflect.* x match - case '{ TryOption.AS_NULL } => Some(TryOption.AS_NULL) - case '{ TryOption.NO_WRITE } => Some(TryOption.NO_WRITE) - case '{ TryOption.ERR_MSG_STRING } => Some(TryOption.ERR_MSG_STRING) - case '{ TryOption.THROW_EXCEPTION } => Some(TryOption.THROW_EXCEPTION) + case '{ EitherLeftPolicy.AS_VALUE } => Some(EitherLeftPolicy.AS_VALUE) + case '{ EitherLeftPolicy.AS_NULL } => Some(EitherLeftPolicy.AS_NULL) + case '{ EitherLeftPolicy.NO_WRITE } => Some(EitherLeftPolicy.NO_WRITE) + case '{ EitherLeftPolicy.ERR_MSG_STRING } => Some(EitherLeftPolicy.ERR_MSG_STRING) + case '{ EitherLeftPolicy.THROW_EXCEPTION } => Some(EitherLeftPolicy.THROW_EXCEPTION) } private[scalajack] given FromExpr[UndefinedValueOption] with { diff --git a/src/main/scala/co.blocke.scalajack/json/JsonError.scala b/src/main/scala/co.blocke.scalajack/json/JsonError.scala index fb85e76c..d10e0a59 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonError.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonError.scala @@ -5,6 +5,7 @@ class JsonIllegalKeyType(msg: String) extends Throwable(msg) class JsonNullKeyValue(msg: String) extends Throwable(msg) class JsonUnsupportedType(msg: String) extends Throwable(msg) class JsonConfigError(msg: String) extends Throwable(msg) +class JsonEitherLeftError(msg: String) extends Throwable(msg) class ParseError(val msg: String) extends Throwable(msg): val show: String = "" diff --git a/src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala b/src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala index 1cef76f3..4880a6a4 100644 --- a/src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala +++ b/src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala @@ -3,13 +3,17 @@ package json package writing import co.blocke.scala_reflection.{RTypeRef, TypedName} +import co.blocke.scala_reflection.reflect.ReflectOnType import co.blocke.scala_reflection.reflect.rtypeRefs.* +import co.blocke.scala_reflection.rtypes.{EnumRType, JavaClassRType, NonConstructorFieldInfo} +import scala.jdk.CollectionConverters.* import scala.quoted.* +import dotty.tools.dotc.ast.Trees.EmptyTree object JsonCodecMaker: - def generateCodecFor[T](ref: RTypeRef[T], cfg: JsonConfig)(using Quotes)(using tt: Type[T]) = - import quotes.reflect.* + def generateCodecFor[T](ref: RTypeRef[T], cfg: JsonConfig)(using q: Quotes)(using tt: Type[T]) = + import q.reflect.* // Cache generated method Symbols + an array of the generated functions (DefDef) case class MethodKey(ref: RTypeRef[?], isStringified: Boolean) // <-- TODO: Not clear what isStringified does here... @@ -45,6 +49,156 @@ object JsonCodecMaker: List(arg.asTerm, out.asTerm) ).asExprOf[Unit] + // --------------------------------------------------------------------------------------------- + + def maybeWrite[T](label: String, aE: Expr[T], ref: RTypeRef[T], out: Expr[JsonOutput], cfg: JsonConfig): Expr[Unit] = + val labelE = Expr(label) + _maybeWrite[T]( + '{ $out.label($labelE) }, + aE, + ref, + out, + cfg + ) + + def maybeWriteMap[K, V](keyE: Expr[K], valueE: Expr[V], keyRef: RTypeRef[K], valueRef: RTypeRef[V], out: Expr[JsonOutput], cfg: JsonConfig): Expr[Unit] = + keyRef.refType match + case '[k] => + _maybeWrite[V]( + '{ + $out.maybeComma() + ${ genWriteVal(keyE.asExprOf[k], keyRef.asInstanceOf[RTypeRef[k]], out, true) } + $out.colon() + }, + valueE, + valueRef, + out, + cfg + ) + + // Tests whether we should write something or not--mainly in the case of Option, or wrapped Option + // Affected types: Option, java.util.Optional, Left/Right, Try/Failure + // Returns Expr[Unit] containing either the original phrase (if ok to write) or the phrase + // prepended with the type-appropriate runtime check. This may seem like drama, but the idea + // is to avoid slowing runtime down with extra "if" checks unless they're absolutely needed. + def _maybeWrite[T](prefix: Expr[Unit], aE: Expr[T], ref: RTypeRef[T], out: Expr[JsonOutput], cfg: JsonConfig): Expr[Unit] = + ref match + case t: ScalaOptionRef[?] if !cfg.noneAsNull => + t.optionParamType.refType match + case '[e] => + val tin = aE.asExprOf[Option[e]] + '{ + $tin match + case None => () + case Some(v) => ${ _maybeWrite[e](prefix, '{ v }.asExprOf[e], t.optionParamType.asInstanceOf[RTypeRef[e]], out, cfg) } + } + case t: JavaOptionalRef[?] if !cfg.noneAsNull => + t.optionParamType.refType match + case '[e] => + val tin = aE.asExprOf[java.util.Optional[e]] + '{ + if ! $tin.isEmpty then ${ _maybeWrite[e](prefix, '{ $tin.get }.asExprOf[e], t.optionParamType.asInstanceOf[RTypeRef[e]], out, cfg) } + } + case t: TryRef[?] => + t.tryRef.refType match + case '[e] => + val tin = aE.asExprOf[scala.util.Try[e]] + '{ + $tin match + case scala.util.Failure(v) => + ${ + cfg.tryFailureHandling match + case TryPolicy.AS_NULL => + '{ + $prefix + $out.burpNull() + } + case TryPolicy.NO_WRITE => '{ () } + case TryPolicy.ERR_MSG_STRING => + '{ + $prefix + $out.value("Try Failure with msg: " + v.getMessage()) + } + case TryPolicy.THROW_EXCEPTION => '{ throw v } + } + case _ => ${ _maybeWrite[e](prefix, '{ $tin.get }.asExprOf[e], t.tryRef.asInstanceOf[RTypeRef[e]], out, cfg) } + } + case t: LeftRightRef[?] if t.lrkind == LRKind.EITHER => + t.refType match + case '[u] => + val tin = aE.asExprOf[u] + t.rightRef.refType match + case '[rt] => + cfg.eitherLeftHandling match + case EitherLeftPolicy.NO_WRITE => + '{ + if $tin == null then $out.burpNull() + $tin match + case Left(_) => () + case Right(v) => ${ _maybeWrite[rt](prefix, '{ v.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out, cfg) } + } + case EitherLeftPolicy.AS_NULL => + '{ + if $tin == null then $out.burpNull() + $tin match + case Left(_) => $out.burpNull() + case Right(v) => ${ _maybeWrite[rt](prefix, '{ v.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out, cfg) } + } + case EitherLeftPolicy.ERR_MSG_STRING => + '{ + if $tin == null then $out.burpNull() + $tin match + case Left(err) => + $prefix + $out.value("Left Error: " + err.toString) + case Right(v) => ${ _maybeWrite[rt](prefix, '{ v.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out, cfg) } + } + case EitherLeftPolicy.THROW_EXCEPTION => + '{ + if $tin == null then $out.burpNull() + $tin match + case Left(err) => throw new JsonEitherLeftError("Left Error: " + err.toString) + case Right(v) => ${ _maybeWrite[rt](prefix, '{ v.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out, cfg) } + } + case EitherLeftPolicy.AS_VALUE => + t.leftRef.refType match + case '[lt] => + '{ + if $tin == null then $out.burpNull() + $tin match + case Left(v) => ${ _maybeWrite[lt](prefix, '{ v.asInstanceOf[lt] }, t.leftRef.asInstanceOf[RTypeRef[lt]], out, cfg) } + case Right(v) => ${ _maybeWrite[rt](prefix, '{ v.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out, cfg) } + } + case t: LeftRightRef[?] if !cfg.noneAsNull && t.lrkind != LRKind.EITHER && (t.leftRef.isInstanceOf[OptionRef[_]] || t.rightRef.isInstanceOf[OptionRef[_]]) => + t.refType match + case '[e] => + t.rightRef.refType match + case '[rt] => + t.leftRef.refType match + case '[lt] => + val tin = aE.asExprOf[e] + '{ + if $tin == None then () + else + $out.mark() + scala.util.Try { + ${ _maybeWrite[rt](prefix, '{ $tin.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out, cfg) } + } match + case scala.util.Success(_) => () + case scala.util.Failure(_) => + $out.revert() + ${ _maybeWrite[lt](prefix, '{ $tin.asInstanceOf[lt] }, t.leftRef.asInstanceOf[RTypeRef[lt]], out, cfg) } + } + case _ => + ref.refType match + case '[u] => + '{ + $prefix + ${ genWriteVal[u](aE.asExprOf[u], ref.asInstanceOf[RTypeRef[u]], out) } + } + + // --------------------------------------------------------------------------------------------- + def genFnBody[T](r: RTypeRef[?], aE: Expr[T], out: Expr[JsonOutput], emitDiscriminator: Boolean = false)(using Quotes): Expr[Unit] = r.refType match case '[b] => @@ -53,13 +207,15 @@ object JsonCodecMaker: makeFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => t.elementRef.refType match case '[e] => - val tin = in.asExprOf[Array[e]] + val tin = in.asInstanceOf[Expr[Array[e]]] '{ - $out.startArray() - $tin.foreach { i => - ${ genWriteVal('{ i }, t.elementRef.asInstanceOf[RTypeRef[e]], out) } - } - $out.endArray() + if $tin == null then $out.burpNull() + else + $out.startArray() + $tin.foreach { i => + ${ genWriteVal('{ i }, t.elementRef.asInstanceOf[RTypeRef[e]], out) } + } + $out.endArray() } } @@ -69,11 +225,13 @@ object JsonCodecMaker: case '[e] => val tin = if t.isMutable then in.asExprOf[scala.collection.mutable.Seq[e]] else in.asExprOf[Seq[e]] '{ - $out.startArray() - $tin.foreach { i => - ${ genWriteVal('{ i }, t.elementRef.asInstanceOf[RTypeRef[e]], out) } - } - $out.endArray() + if $tin == null then $out.burpNull() + else + $out.startArray() + $tin.foreach { i => + ${ genWriteVal('{ i }, t.elementRef.asInstanceOf[RTypeRef[e]], out) } + } + $out.endArray() } } @@ -83,35 +241,61 @@ object JsonCodecMaker: case '[e] => val tin = if t.isMutable then in.asExprOf[scala.collection.mutable.Set[e]] else in.asExprOf[Set[e]] '{ - $out.startArray() - $tin.foreach { i => - ${ genWriteVal('{ i }, t.elementRef.asInstanceOf[RTypeRef[e]], out) } - } - $out.endArray() + if $tin == null then $out.burpNull() + else + $out.startArray() + $tin.foreach { i => + ${ genWriteVal('{ i }.asExprOf[e], t.elementRef.asInstanceOf[RTypeRef[e]], out) } + } + $out.endArray() } } + case t: ScalaClassRef[?] if t.isSealed && t.isAbstractClass => // basically just like sealed trait... + makeFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => + if t.childrenAreObject then + // case object -> just write the simple name of the object + '{ + $out.value($in.getClass.getName.split('.').last.stripSuffix("$")) + } + else + val cases = t.sealedChildren.map { child => + child.refType match + case '[c] => + val subtype = TypeIdent(TypeRepr.of[c].typeSymbol) + val sym = Symbol.newBind(Symbol.spliceOwner, "t", Flags.EmptyFlags, subtype.tpe) + CaseDef(Bind(sym, Typed(Ref(sym), subtype)), None, genFnBody[c](child, Ref(sym).asExprOf[c], out, true).asTerm) + } :+ CaseDef(Literal(NullConstant()), None, '{ $out.burpNull() }.asTerm) + val matchExpr = Match(aE.asTerm, cases).asExprOf[Unit] + '{ + if $in == null then $out.burpNull() + else $matchExpr + } + } + + // We don't use makeFn here because a value class is basically just a "box" around a simple type + case t: ScalaClassRef[?] if t.isValueClass => + val theField = t.fields.head.fieldRef + theField.refType match + case '[e] => + val fieldValue = Select.unique(aE.asTerm, t.fields.head.name).asExprOf[e] + genWriteVal(fieldValue, theField.asInstanceOf[RTypeRef[e]], out) + case t: ScalaClassRef[?] => makeFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => - val tin = in.asExprOf[b] val body = { val eachField = t.fields.map { f => f.fieldRef.refType match case '[z] => - val fname = Expr(f.name) - val fieldValue = Select.unique(tin.asTerm, f.name).asExprOf[z] - '{ - $out.label($fname) - ${ genWriteVal(fieldValue, f.fieldRef.asInstanceOf[RTypeRef[z]], out) } - } + val fieldValue = Select.unique(in.asTerm, f.name).asExprOf[z] + maybeWrite[z](f.name, fieldValue, f.fieldRef.asInstanceOf[RTypeRef[z]], out, cfg) } if emitDiscriminator then val cname = cfg.typeHintPolicy match - case TypeHintPolicy.SIMPLE_CLASSNAME => Expr(lastPart(t.name)) - case TypeHintPolicy.SCRAMBLE_CLASSNAME => - val hash = Expr(lastPart(t.name).hashCode) - '{ scramble($hash) } - case TypeHintPolicy.USE_ANNOTATION => ??? + case TypeHintPolicy.SIMPLE_CLASSNAME => Expr(lastPart(t.name)) + case TypeHintPolicy.SCRAMBLE_CLASSNAME => '{ scramble(${ Expr(lastPart(t.name).hashCode) }) } + case TypeHintPolicy.USE_ANNOTATION => + Expr(t.annotations.get("co.blocke.scalajack.TypeHint").flatMap(_.get("hintValue")).getOrElse(lastPart(t.name))) val withDisc = '{ $out.label(${ Expr(cfg.typeHintLabel) }) $out.value($cname) @@ -120,35 +304,125 @@ object JsonCodecMaker: else if eachField.length == 1 then eachField.head else Expr.block(eachField.init, eachField.last) } - '{ - $out.startObject() - $body - $out.endObject() - } + + if !t.isCaseClass && cfg.writeNonConstructorFields then + val eachField = t.nonConstructorFields.map { f => + f.fieldRef.refType match + case '[e] => + val fieldValue = Select.unique(in.asTerm, f.getterLabel).asExprOf[e] + maybeWrite[e](f.name, fieldValue, f.fieldRef.asInstanceOf[RTypeRef[e]], out, cfg) + } + val subBody = eachField.length match + case 0 => '{} + case 1 => eachField.head + case _ => Expr.block(eachField.init, eachField.last) + '{ + if $in == null then $out.burpNull() + else + $out.startObject() + $body + $subBody + $out.endObject() + } + else + '{ + if $in == null then $out.burpNull() + else + $out.startObject() + $body + $out.endObject() + } } case t: MapRef[?] => - t.elementRef.refType match - case '[k] => - makeFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => + makeFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => + t.elementRef.refType match + case '[k] => t.elementRef2.refType match case '[v] => val tin = if t.isMutable then in.asExprOf[scala.collection.mutable.Map[k, v]] else in.asExprOf[Map[k, v]] '{ - $out.startObject() - $tin.foreach { case (k, v) => - $out.maybeComma() - ${ genWriteVal('{ k }, t.elementRef.asInstanceOf[RTypeRef[k]], out, true) } - $out.colon() - ${ genWriteVal('{ v }, t.elementRef2.asInstanceOf[RTypeRef[v]], out) } + if $tin == null then $out.burpNull() + else + $out.startObject() + $tin.foreach { case (key, value) => + ${ + maybeWriteMap[k, v]('{ key }, '{ value }.asExprOf[v], t.elementRef.asInstanceOf[RTypeRef[k]], t.elementRef2.asInstanceOf[RTypeRef[v]], out, cfg) + } + } + $out.endObject() + } + } + + case t: JavaCollectionRef[?] => + makeFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => + t.elementRef.refType match + case '[e] => + val tin = in.asExprOf[java.util.Collection[_]] + '{ + if $tin == null then $out.burpNull() + else + $out.startArray() + $tin.toArray.foreach { elem => + ${ genWriteVal('{ elem.asInstanceOf[e] }, t.elementRef.asInstanceOf[RTypeRef[e]], out) } + } + $out.endArray() + } + } + + case t: JavaMapRef[?] => + makeFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => + t.elementRef.refType match + case '[k] => + t.elementRef2.refType match + case '[v] => + val tin = in.asExprOf[java.util.Map[k, v]] + '{ + if $tin == null then $out.burpNull() + else + $out.startObject() + $tin.asScala.foreach { case (key, value) => + ${ + maybeWriteMap[k, v]('{ key }, '{ value }.asExprOf[v], t.elementRef.asInstanceOf[RTypeRef[k]], t.elementRef2.asInstanceOf[RTypeRef[v]], out, cfg) + } + } + $out.endObject() + } + } + + case t: JavaClassRef[?] => + makeFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => + t.refType match + case '[p] => + val rtype = t.expr.asExprOf[JavaClassRType[p]] + val tin = aE.asExprOf[b] + var fieldRefs = t.fields.asInstanceOf[List[NonConstructorFieldInfoRef]] + val fieldNames = t.fields.map(_.name) + var i = -1 + '{ + if $tin == null then $out.burpNull() + else + $out.startObject() + val rt = $rtype + rt.fields.foreach { f => + val field = f.asInstanceOf[NonConstructorFieldInfo] + val m = $tin.getClass.getMethod(field.getterLabel) + m.setAccessible(true) + val fieldValue = m.invoke($tin) + ${ + val ref = fieldRefs.head + fieldRefs = fieldRefs.tail + ref.fieldRef.refType match + case '[e] => + i += 1 + maybeWrite[e](fieldNames(i), '{ fieldValue }.asExprOf[e], ref.fieldRef.asInstanceOf[RTypeRef[e]], out, cfg) } - $out.endObject() } - } + $out.endObject() + } + } case t: TraitRef[?] => - // classesSeen.put(t.typedName, t) - // val rt = t.expr.asInstanceOf[Expr[TraitRType[T]]] if !t.isSealed then throw new JsonUnsupportedType("Non-sealed traits are not supported") if t.childrenAreObject then // case object -> just write the simple name of the object @@ -158,17 +432,9 @@ object JsonCodecMaker: } else // So... sealed trait children could be any of those defined for the trait. We need to - // generate functions for each child then a master function that examines $aE and based on - // its value, call the appropriate function to render. - // val beforeKeys = methodSyms.keySet - // t.sealedChildren.foreach { child => - // child.refType match - // case '[c] => - // genFnBody[c](child, aE.asExprOf[c], out) - // } - // Now generate and return the calling function based on runtime type - // Refer to Jsoniter: JsonCodecMaker.scala around line 920 for example how to do this, incl a wildcard, which - // we don't need here. + // generate a match/case statement that in turn generates render functions for each + // child of the sealed trait. + // Refer to Jsoniter: JsonCodecMaker.scala around line 920 for example how to do this, incl a wildcard. val cases = t.sealedChildren.map { child => child.refType match case '[c] => @@ -177,7 +443,190 @@ object JsonCodecMaker: CaseDef(Bind(sym, Typed(Ref(sym), subtype)), None, genFnBody[c](child, Ref(sym).asExprOf[c], out, true).asTerm) } :+ CaseDef(Literal(NullConstant()), None, '{ $out.burpNull() }.asTerm) val matchExpr = Match(aE.asTerm, cases).asExprOf[Unit] - matchExpr + // Generating a function for a single match might be overkill, but... there may be a lot of cases, and/or + // this sealed trait may be used a lot of times, so... its a trade-off + makeFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => + matchExpr + } + + // No makeFn here--Option is just a wrapper to the real thingy + case t: OptionRef[?] => + t.optionParamType.refType match + case '[e] => + val tin = aE.asExprOf[b] + '{ + if $tin == null then $out.burpNull() + else + $tin match + case None => + ${ + if cfg.noneAsNull then '{ $out.burpNull() } + else '{ () } + } + case Some(v) => + val vv = v.asInstanceOf[e] + ${ genWriteVal[e]('{ vv }, t.optionParamType.asInstanceOf[RTypeRef[e]], out) } + } + + // No makeFn here... SelfRef is referring to something we've already seen before. There absolutely should already be a geneated + // and cached function for this thing that we can call. + case t: SelfRefRef[?] => + t.refType match + case '[e] => + val key = MethodKey(ReflectOnType[e](q)(TypeRepr.of[e])(using scala.collection.mutable.Map.empty[TypedName, Boolean]), false) + val sym = methodSyms(key) + val tin = aE.asExprOf[b] + '{ + if $tin == null then $out.burpNull() + else ${ Ref(sym).appliedTo(tin.asTerm, out.asTerm).asExprOf[Unit] } + } + + // No makeFn here. All LeftRight types (Either, Union, Intersection) are just type wrappers + case t: LeftRightRef[?] => + val tin = aE.asExprOf[b] + t.leftRef.refType match + case '[lt] => + t.rightRef.refType match + case '[rt] => + // This is a close parallel with maybeWrite handling of Either. If the Either is a field in a class or + // Map, the maybeWrite logic applies--because we need to not write both the Either value AND the field label. + // If the Either is part of a tuple, Seq, etc., then this logic applies. + if t.lrkind == LRKind.EITHER then + cfg.eitherLeftHandling match + case EitherLeftPolicy.AS_VALUE => + '{ + if $tin == null then $out.burpNull() + else + $tin match + case Left(v) => + ${ genWriteVal[lt]('{ v.asInstanceOf[lt] }, t.leftRef.asInstanceOf[RTypeRef[lt]], out) } + case Right(v) => + ${ genWriteVal[rt]('{ v.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out) } + } + case EitherLeftPolicy.AS_NULL => + '{ + if $tin == null then $out.burpNull() + else + $tin match + case Left(v) => $out.burpNull() + case Right(v) => + ${ genWriteVal[rt]('{ v.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out) } + } + case EitherLeftPolicy.NO_WRITE => + '{ + if $tin == null then $out.burpNull() + else + $tin match + case Left(v) => () + case Right(v) => + ${ genWriteVal[rt]('{ v.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out) } + } + case EitherLeftPolicy.ERR_MSG_STRING => + '{ + if $tin == null then $out.burpNull() + else + $tin match + case Left(v) => $out.value("Left Error: " + v.toString) + case Right(v) => + ${ genWriteVal[rt]('{ v.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out) } + } + case EitherLeftPolicy.THROW_EXCEPTION => + '{ + if $tin == null then $out.burpNull() + else + $tin match + case Left(v) => throw new JsonEitherLeftError("Left Error: " + v.toString) + case Right(v) => + ${ genWriteVal[rt]('{ v.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out) } + } + else + '{ + $out.mark() + scala.util.Try { + ${ genWriteVal[rt]('{ $tin.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out) } + } match + case scala.util.Success(_) => () + case scala.util.Failure(_) => + $out.revert() + ${ genWriteVal[lt]('{ $tin.asInstanceOf[lt] }, t.leftRef.asInstanceOf[RTypeRef[lt]], out) } + } + + // No makeFn here. Try is just a wrapper + case t: TryRef[?] => + t.tryRef.refType match + case '[e] => + val tin = aE.asExprOf[scala.util.Try[e]] + '{ + if $tin == null then $out.burpNull() + else + $tin match + case scala.util.Success(v) => + ${ genWriteVal[e]('{ v }.asInstanceOf[Expr[e]], t.tryRef.asInstanceOf[RTypeRef[e]], out) } + case scala.util.Failure(v) => + ${ + cfg.tryFailureHandling match + case TryPolicy.AS_NULL => '{ $out.burpNull() } + case TryPolicy.NO_WRITE => '{ () } + case TryPolicy.ERR_MSG_STRING => '{ $out.value("Try Failure with msg: " + v.getMessage()) } + case TryPolicy.THROW_EXCEPTION => '{ throw v } + } + } + + case t: TupleRef[?] => + makeFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => + '{ + if $in == null then $out.burpNull() + else + $out.startArray() + ${ + // Note: Don't use maybeWrite here... Tuples are fixed-length. We need to write + // something for every position, so write null for None or other "bad" values + val elementsE = t.tupleRefs.zipWithIndex.map { case (ref, i) => + ref.refType match + case '[e] => + val fieldValue = Select.unique(in.asTerm, "_" + (i + 1)).asExprOf[e] + // Special handling if field type is Option and value is None. + ref match + case _: OptionRef[?] => + '{ + if $fieldValue == None then $out.burpNull() + else ${ genWriteVal[e](fieldValue, ref.asInstanceOf[RTypeRef[e]], out) } + } + case _: TryRef[?] => + '{ + $fieldValue match + case scala.util.Failure(_) => $out.burpNull() + case scala.util.Success(None) => $out.burpNull() + case _ => ${ genWriteVal[e](fieldValue, ref.asInstanceOf[RTypeRef[e]], out) } + } + case r: LeftRightRef[?] if r.lrkind != LRKind.EITHER => + if r.leftRef.isInstanceOf[TryRef[_]] || r.rightRef.isInstanceOf[TryRef[_]] then + '{ + $fieldValue match + case scala.util.Failure(_) => $out.burpNull() + case scala.util.Success(None) => $out.burpNull() + case _ => ${ genWriteVal[e](fieldValue, ref.asInstanceOf[RTypeRef[e]], out) } + } + else if r.leftRef.isInstanceOf[OptionRef[_]] || r.rightRef.isInstanceOf[OptionRef[_]] then + '{ + $fieldValue match + case None => $out.burpNull() + case _ => ${ genWriteVal[e](fieldValue, ref.asInstanceOf[RTypeRef[e]], out) } + } + else genWriteVal[e](fieldValue, ref.asInstanceOf[RTypeRef[e]], out) + case _ => + genWriteVal[e](fieldValue, ref.asInstanceOf[RTypeRef[e]], out) + } + if elementsE.size == 1 then elementsE.head + else Expr.block(elementsE.init, elementsE.last) + } + $out.endArray() + } + } + + case t => throw new JsonUnsupportedType("Type represented by " + t.name + " is unsupported for JSON writes") + + // --------------------------------------------------------------------------------------------- def genWriteVal[T: Type]( aE: Expr[T], @@ -275,13 +724,28 @@ object JsonCodecMaker: case t: ZoneIdRef => '{ $out.value(${ aE.asExprOf[java.time.ZoneId] }) } case t: ZoneOffsetRef => '{ $out.value(${ aE.asExprOf[java.time.ZoneOffset] }) } - case t: UUIDRef => '{ $out.value(${ aE.asExprOf[java.util.UUID] }) } + case t: UUIDRef => '{ $out.value(${ aE.asExprOf[java.util.UUID] }) } + case t: ObjectRef => '{ $out.value(${ Expr(t.name) }) } case t: AliasRef[?] => t.unwrappedType.refType match case '[e] => genWriteVal[e](aE.asInstanceOf[Expr[e]], t.unwrappedType.asInstanceOf[RTypeRef[e]], out) + // These one's here becaue Enums and their various flavors can be Map keys + // (EnumRef handles: Scala 3 enum, Scala 2 Enumeration, Java Enumeration) + case t: EnumRef[?] => + val enumAsId = cfg.enumsAsIds match + case None => false + case Some(Nil) => true + case Some(list) if list.contains(t.name) => true + case _ => false + val rtype = t.expr + if enumAsId then + if isStringified then '{ $out.label($rtype.asInstanceOf[EnumRType[_]].ordinal($aE.toString).get.toString) } + else '{ $out.value($rtype.asInstanceOf[EnumRType[_]].ordinal($aE.toString).get) } + else '{ $out.value($aE.toString) } + // Everything else... case _ if isStringified => throw new JsonIllegalKeyType("Non-primitive/non-simple types cannot be map keys") case _ => genFnBody(ref, aE, out) diff --git a/src/main/scala/co.blocke.scalajack/json/writing/JsonOutput.scala b/src/main/scala/co.blocke.scalajack/json/writing/JsonOutput.scala index 7e05cf37..d1cda3d8 100644 --- a/src/main/scala/co.blocke.scalajack/json/writing/JsonOutput.scala +++ b/src/main/scala/co.blocke.scalajack/json/writing/JsonOutput.scala @@ -4,10 +4,16 @@ package writing import java.time.format.DateTimeFormatter.* +/** Wrapper around a StringBuilder that offers support for primitive types, + * including quotes-wrapping those that need it for use in Map keys (aka stringified). + * Handles dangling commas/separators, plus mark & revert capability. It is reusable + * via clear() for speed -- one per thread, of course! + */ case class JsonOutput(): val internal: StringBuilder = new StringBuilder() private var comma: Boolean = false + private var savePoint: Int = 0 def result = internal.result @@ -15,6 +21,12 @@ case class JsonOutput(): internal.clear() this + def mark() = + savePoint = internal.length + + def revert() = // delete everything after the set savePoint + internal.setLength(savePoint) + inline def startObject(): Unit = maybeComma() internal.append('{') diff --git a/src/main/scala/co.blocke.scalajack/json/writing/JsonWriterRT.scalax b/src/main/scala/co.blocke.scalajack/json/writing/JsonWriterRT.scalax deleted file mode 100644 index 4ceced2a..00000000 --- a/src/main/scala/co.blocke.scalajack/json/writing/JsonWriterRT.scalax +++ /dev/null @@ -1,361 +0,0 @@ -package co.blocke.scalajack -package json - -import co.blocke.scala_reflection.* -import co.blocke.scala_reflection.rtypes.* -import co.blocke.scala_reflection.reflect.{ReflectOnType, TypeSymbolMapper} -import co.blocke.scala_reflection.reflect.rtypeRefs.* -import scala.jdk.CollectionConverters.* -import scala.util.{Failure, Success, Try} - -/** This class is horrible. It is a mirror of JsonWriter, except this one executes at runtime, and hence - * doens't have any Expr's. This is bad becuause it's slow--we're not able to take advantage of doing work - * at compile-time. This is ONLY used becuase of trait handling. The problem is we need to express some - * class C in terms of trait T. We know T at compile-time but don't know C until compile-time. There's - * (so far) no good way to get the information passed across the bridge, so... What we need to do in - * JsonWriter is leverage RType.inTermsOf to do the C->T magic, but this returns an RType, not an - * RTypeRef. We must render the rest of the trait (and any structures below it) at runtime, necessitating - * this class, JsonWriterRT. - * - * It should only be used for this special trait handling. Everything else leverages the compile-time - * JsonWriter. - */ - -object JsonWriterRT: - - val secret = "N@rrow !s the w@y" - val encryptionDecryption = new AESEncryptionDecryption() - - def refWriteRT[T]( - cfg: JsonConfig, - rt: RType[T], - a: T, - sb: StringBuilder, - isMapKey: Boolean = false - )(using classesSeen: scala.collection.mutable.Map[TypedName, RType[?]]): StringBuilder = - rt match - case StringRType(_) | CharRType(_) | JavaCharacterRType(_) => if a == null then sb.append("null") else sb.append("\"" + a.toString + "\"") - case t: PrimitiveRType => - if isMapKey then - if t.isNullable && a == null then sb.append("\"null\"") - else - sb.append('"') - sb.append(a.toString) - sb.append('"') - else if t.isNullable && a == null then sb.append("null") - else sb.append(a.toString) - - case t: SeqRType[?] => - if isMapKey then throw new JsonError("Seq instances cannot be map keys") - if a == null then sb.append("null") - else sb.append('[') - val sbLen = sb.length - a.asInstanceOf[Seq[t.elementType.T]].foldLeft(sb) { (acc, one) => - if JsonWriter.isOkToWrite(one, cfg) then - refWriteRT[t.elementType.T](cfg, t.elementType.asInstanceOf[RType[t.elementType.T]], one, acc) - sb.append(',') - else sb - } - if sbLen == sb.length then sb.append(']') - else sb.setCharAt(sb.length() - 1, ']') - - case t: ArrayRType[?] => - if isMapKey then throw new JsonError("Arrays instances cannot be map keys") - if a == null then sb.append("null") - else sb.append('[') - val sbLen = sb.length - a.asInstanceOf[Seq[t.elementType.T]].foldLeft(sb) { (acc, one) => - if JsonWriter.isOkToWrite(one, cfg) then - refWriteRT[t.elementType.T](cfg, t.elementType.asInstanceOf[RType[t.elementType.T]], one, acc) - sb.append(',') - else sb - } - if sbLen == sb.length then sb.append(']') - else sb.setCharAt(sb.length() - 1, ']') - - case t: ScalaClassRType[?] if t.isSealed && t.isAbstractClass => - classesSeen.put(t.typedName, t) - if a == null then sb.append("null") - else - val className = a.getClass.getName - if t.childrenAreObject then - // case object -> just write the simple name of the object - sb.append('"') - sb.append(JsonWriter.lastPart(className)) - sb.append('"') - else - // Wow! If this is a sealed trait, all the children already have correctly-typed parameters--no need for expensive inTermsOf() - t.sealedChildren - .find(_.name == className) - .map(foundKid => - val augmented = foundKid.asInstanceOf[ScalaClassRType[foundKid.T]].copy(renderTrait = Some(t.name)).asInstanceOf[RType[foundKid.T]] - JsonWriterRT.refWriteRT[foundKid.T](cfg, augmented, a.asInstanceOf[foundKid.T], sb) - ) - .getOrElse(throw new JsonError(s"Unrecognized child $className of seald trait " + t.name)) - - case t: ScalaClassRType[?] => - if t.isAbstractClass then throw new JsonError("Cannot serialize an abstract class") - classesSeen.put(t.typedName, t) - if a == null then sb.append("null") - else - sb.append('{') - val sbLen = sb.length - t.renderTrait.map { traitName => - sb.append('"') - sb.append(cfg.typeHintLabelByTrait.getOrElse(traitName, cfg.typeHintLabel)) - sb.append('"') - sb.append(':') - sb.append('"') - val hint = t.annotations - .get("co.blocke.scalajack.TypeHint") - .map(_ => encryptionDecryption.encrypt(JsonWriter.lastPart(t.name), secret)) // only need lat part of class name because the rest is the same as the parent - .getOrElse { - cfg.typeHintTransformer.get(a.getClass.getName) match - case Some(xform) => xform(a) - case None => cfg.typeHintDefaultTransformer(a.getClass.getName) - } - sb.append(hint) - sb.append('"') - sb.append(',') - } - t.fields.foldLeft(sb) { (acc, f) => - val m = a.getClass.getMethod(f.name) - m.setAccessible(true) - val fieldValue = m.invoke(a).asInstanceOf[f.fieldType.T] - if JsonWriter.isOkToWrite(fieldValue, cfg) then - acc.append('"') - acc.append(f.name) - acc.append('"') - acc.append(':') - refWriteRT[f.fieldType.T](cfg, f.fieldType.asInstanceOf[RType[f.fieldType.T]], fieldValue, acc) - acc.append(',') - else acc - } - if !t.isCaseClass && cfg.writeNonConstructorFields then - t.nonConstructorFields.foldLeft(sb) { (acc, f) => - val m = a.getClass.getMethod(f.getterLabel) - m.setAccessible(true) - val fieldValue = m.invoke(a).asInstanceOf[f.fieldType.T] - if JsonWriter.isOkToWrite(fieldValue, cfg) then - acc.append('"') - acc.append(f.name) - acc.append('"') - acc.append(':') - refWriteRT[f.fieldType.T](cfg, f.fieldType.asInstanceOf[RType[f.fieldType.T]], fieldValue, acc) - acc.append(',') - else acc - } - if sbLen == sb.length then sb.append('}') - else sb.setCharAt(sb.length() - 1, '}') - - case t: TraitRType[?] => - classesSeen.put(t.typedName, t) - if a == null then sb.append("null") - if t.childrenAreObject then - sb.append('"') - sb.append(JsonWriter.lastPart(a.getClass.getName)) - sb.append('"') - else if t.isSealed then - // Wow! If this is a sealed trait, all the children already have correctly-typed parameters--no need for expensive inTermsOf() - val className = a.getClass.getName - t.sealedChildren - .find(_.name == className) - .map(foundKid => - val augmented = foundKid.asInstanceOf[ScalaClassRType[foundKid.T]].copy(renderTrait = Some(t.name)).asInstanceOf[RType[foundKid.T]] - JsonWriterRT.refWriteRT[foundKid.T](cfg, augmented, a.asInstanceOf[foundKid.T], sb) - ) - .getOrElse(throw new JsonError(s"Unrecognized child $className of seald trait " + t.name)) - else throw new JsonError("non-sealed traits are not supported") - // val classRType = RType.inTermsOf[T](a.getClass).asInstanceOf[ScalaClassRType[T]].copy(renderTrait = Some(t.name)).asInstanceOf[RType[T]] - // JsonWriterRT.refWriteRT[classRType.T](cfg, classRType, a.asInstanceOf[classRType.T], sb) - - case t: OptionRType[?] => - if isMapKey then throw new JsonError("Option valuess cannot be map keys") - if a == null then sb.append("null") - else - a match - case None => sb.append("null") - case Some(v) => - refWriteRT[t.optionParamType.T](cfg, t.optionParamType.asInstanceOf[RType[t.optionParamType.T]], v.asInstanceOf[t.optionParamType.T], sb) - - case t: MapRType[?] => - if isMapKey then throw new JsonError("Map values cannot be map keys") - if a == null then sb.append("null") - else - sb.append('{') - val sbLen = sb.length - a.asInstanceOf[Map[?, ?]].foreach { case (key, value) => - if JsonWriter.isOkToWrite(value, cfg) then - val b = refWriteRT[t.elementType.T](cfg, t.elementType.asInstanceOf[RType[t.elementType.T]], key.asInstanceOf[t.elementType.T], sb, true) - b.append(':') - val b2 = refWriteRT[t.elementType2.T](cfg, t.elementType2.asInstanceOf[RType[t.elementType2.T]], value.asInstanceOf[t.elementType2.T], sb) - b2.append(',') - } - if sbLen == sb.length then sb.append('}') - else sb.setCharAt(sb.length() - 1, '}') - - case t: EitherRType[?] => - if isMapKey then throw new JsonError("Union, Intersection, or Either-typed values cannot be map keys.") - a match - case Left(v) => - refWriteRT[t.leftType.T](cfg, t.leftType.asInstanceOf[RType[t.leftType.T]], v.asInstanceOf[t.leftType.T], sb) - case Right(v) => - refWriteRT[t.rightType.T](cfg, t.rightType.asInstanceOf[RType[t.rightType.T]], v.asInstanceOf[t.rightType.T], sb) - - case t: UnionRType[?] => - // Intersection/Union types....take your best shot! It's all we've got. No definitive info here. - val trial = new StringBuilder() - val lrSb = scala.util.Try( - refWriteRT[t.rightType.T](cfg, t.rightType.asInstanceOf[RType[t.rightType.T]], a.asInstanceOf[t.rightType.T], trial) - ) match - case Success(trialSb) => trialSb - case Failure(_) => - trial.clear - refWriteRT[t.leftType.T](cfg, t.leftType.asInstanceOf[RType[t.leftType.T]], a.asInstanceOf[t.leftType.T], trial) - sb ++= lrSb - - case t: IntersectionRType[?] => - // Intersection/Union types....take your best shot! It's all we've got. No definitive info here. - val trial = new StringBuilder() - val lrSb = scala.util.Try( - refWriteRT[t.rightType.T](cfg, t.rightType.asInstanceOf[RType[t.rightType.T]], a.asInstanceOf[t.rightType.T], trial) - ) match - case Success(trialSb) => trialSb - case Failure(_) => - trial.clear - refWriteRT[t.leftType.T](cfg, t.leftType.asInstanceOf[RType[t.leftType.T]], a.asInstanceOf[t.leftType.T], trial) - sb ++= lrSb - - case t: TryRType[?] => - if isMapKey then throw new JsonError("Try values (Succeed/Fail) cannot be map keys") - if a == null then sb.append("null") - else - a match - case Success(v) => - refWriteRT[t.tryType.T](cfg, t.tryType.asInstanceOf[RType[t.tryType.T]], v.asInstanceOf[t.tryType.T], sb) - case Failure(_) if cfg.tryFailureHandling == TryOption.AS_NULL => sb.append("null") - case Failure(f) if cfg.tryFailureHandling == TryOption.THROW_EXCEPTION => - throw new JsonError("A try value was Failure with message: " + f.getMessage()) - case Failure(v) => - sb.append('"') - sb.append(v.getMessage) - sb.append('"') - - case t: TupleRType[?] => - if isMapKey then throw new JsonError("Tuples cannot be map keys") - if a == null then sb.append("null") - sb.append('[') - val sbLen = sb.length - val fieldValues = a.asInstanceOf[Product].productIterator.toList - t.typeParamValues.zipWithIndex.foreach { case (rt, i) => - val fieldValue = fieldValues(i).asInstanceOf[rt.T] - refWriteRT[rt.T](cfg, rt.asInstanceOf[RType[rt.T]], fieldValue, sb) - sb.append(',') - } - if sbLen == sb.length then sb.append(']') - else sb.setCharAt(sb.length() - 1, ']') - - case t: JavaCollectionRType[?] => - if isMapKey then throw new JsonError("Collections cannot be map keys.") - if a == null then sb.append("null") - sb.append('[') - val sbLen = sb.length - a.asInstanceOf[java.util.Collection[?]].toArray.foreach { elem => - if JsonWriter.isOkToWrite(elem, cfg) then refWriteRT[t.elementType.T](cfg, t.elementType.asInstanceOf[RType[t.elementType.T]], elem.asInstanceOf[t.elementType.T], sb) - } - sb.append(',') - if sbLen == sb.length then sb.append(']') - else sb.setCharAt(sb.length() - 1, ']') - - case t: JavaMapRType[?] => - if isMapKey then throw new JsonError("Collections cannot be map keys.") - if a == null then sb.append("null") - sb.append('{') - val sbLen = sb.length - a.asInstanceOf[java.util.Map[?, ?]].asScala.foreach { case (key, value) => - if JsonWriter.isOkToWrite(value, cfg) then - refWriteRT[t.elementType.T](cfg, t.elementType.asInstanceOf[RType[t.elementType.T]], key.asInstanceOf[t.elementType.T], sb, true) - sb.append(':') - refWriteRT[t.elementType2.T](cfg, t.elementType2.asInstanceOf[RType[t.elementType2.T]], value.asInstanceOf[t.elementType2.T], sb) - sb.append(',') - } - if sbLen == sb.length then sb.append('}') - else sb.setCharAt(sb.length() - 1, '}') - - case t: AliasRType[?] => - refWriteRT[t.unwrappedType.T](cfg, t.unwrappedType.asInstanceOf[RType[t.unwrappedType.T]], a.asInstanceOf[t.unwrappedType.T], sb) - - case t: SelfRefRType[?] => - if isMapKey then throw new JsonError("Classes or traits cannot be map keys.") - val again = classesSeen.getOrElse( - t.typedName, { - // Need to add to cache. Since we're coming from compile-time side, the runtime side may not have seen this class before... - val v = RType.of[T] - classesSeen.put(t.typedName, v) - v - } - ) - JsonWriterRT.refWriteRT[again.T](cfg, again, a.asInstanceOf[again.T], sb) - - case t: EnumRType[?] => - if a == null then sb.append("null") - else - val enumAsId = cfg.enumsAsIds match - case '*' => true - case aList: List[String] if aList.contains(t.name) => true - case _ => false - if enumAsId then - val enumVal = t.ordinal(a.toString).getOrElse(throw new JsonError(s"Value $a is not a valid enum value for ${t.name}")) - if isMapKey then - sb.append('"') - sb.append(enumVal.toString) - sb.append('"') - else sb.append(enumVal.toString) - else - sb.append('"') - sb.append(a.toString) - sb.append('"') - - case t: JavaClassRType[?] => - classesSeen.put(t.typedName, t) - if a == null then sb.append("null") - else - sb.append('{') - val sbLen = sb.length - t.fields.foldLeft(sb) { (acc, f) => - val field = f.asInstanceOf[NonConstructorFieldInfo] - val m = a.getClass.getMethod(field.getterLabel) - m.setAccessible(true) - val fieldValue = m.invoke(a).asInstanceOf[field.fieldType.T] - if JsonWriter.isOkToWrite(fieldValue, cfg) then - acc.append('"') - acc.append(field.name) - acc.append('"') - acc.append(':') - JsonWriterRT.refWriteRT[field.fieldType.T](cfg, field.fieldType, a.asInstanceOf[field.fieldType.T], sb)(using scala.collection.mutable.Map.empty[TypedName, RType[?]]) - acc.append(',') - else acc - } - if sbLen == sb.length then sb.append('}') - else sb.setCharAt(sb.length() - 1, '}') - - case t: ObjectRef => - sb.append("\"" + t.name + "\"") - - case t: Scala2RType[?] => - cfg.undefinedFieldHandling match - case UndefinedValueOption.AS_NULL => sb.append("null") - case UndefinedValueOption.AS_SYMBOL => sb.append("\"" + t.name + "\"") - case UndefinedValueOption.THROW_EXCEPTION => throw new JsonError("Unknown/unsupported Scala 2 type " + t.name) - - case t: UnknownRType[?] => - cfg.undefinedFieldHandling match - case UndefinedValueOption.AS_NULL => sb.append("null") - case UndefinedValueOption.AS_SYMBOL => sb.append("\"" + t.name + "\"") - case UndefinedValueOption.THROW_EXCEPTION => throw new JsonError("Unknown/unsupported type " + t.name) - - case t: TypeSymbolRType => - cfg.undefinedFieldHandling match - case UndefinedValueOption.AS_NULL => sb.append("null") - case UndefinedValueOption.AS_SYMBOL => sb.append("\"" + t.name + "\"") - case UndefinedValueOption.THROW_EXCEPTION => throw new JsonError("Unknown/unsupported type " + t.name + ". (Class didn't fully define all its type parameters.)") diff --git a/src/main/scala/co.blocke.scalajack/json/writing/JsonWriter_old.scalax b/src/main/scala/co.blocke.scalajack/json/writing/JsonWriter_old.scalax index a2aae7fb..528c062e 100644 --- a/src/main/scala/co.blocke.scalajack/json/writing/JsonWriter_old.scalax +++ b/src/main/scala/co.blocke.scalajack/json/writing/JsonWriter_old.scalax @@ -12,36 +12,40 @@ import scala.quoted.staging.* /* TODO: - [*] - Primitive types - [*] - Simple Types - [*] - Seq/Set/Array - [*] - Map - [*] - Scala case class - [ ] - Scala non-case class - [ ] - Java class - [ ] - Enum - [ ] - Enumeration - [ ] - Java Enum - [ ] - Java Collections - [ ] - Java Map - [ ] - Intersection - [ ] - Union - [ ] - Either - [*] - Alias type - [ ] - Object (How???) - [X] - Trait (How???) - [ ] - Sealed Trait - [ ] - Sealed Abstract Class (handle like sealed trait....) - [ ] - SelfRef - [ ] - Tuple - [ ] - Unknown (throw exception) - [ ] - Scala 2 (throw exception) - [ ] - TypeSymbol (throw exception) - [ ] - Value class - - [ ] -- type hint label mapping - [ ] -- type hint value mapping - [ ] -- update runtime-size TraitRType handling to match new compile-time code + Code Test + [*] [W] - Primitive types + [*] [W] - Simple Types + + [*] [W] - Seq/Set/Array + [*] [W] - Map + [*] [W] - Java Collections + [*] [W] - Java Map + [*] [W] - Tuple + + [*] [ ] - Scala case class + [*] [ ] - Scala non-case class + [*] [ ] - Java class + [*] [ ] - Sealed Trait + [*] [ ] - Sealed Abstract Class (handle like sealed trait....) + + [*] [ ] - Option/Optional + [*] [ ] - Either + [*] [ ] - Intersection + [*] [ ] - Union + [*] [ ] - Enum + [*] [ ] - Enumeration + [*] [ ] - Java Enum + [*] [ ] - Alias type + [*] [ ] - Object + [*] [ ] - SelfRef + [ ] [ ] - TryRef + [*] [X] - Unknown (throw exception) + [*] [X] - Scala 2 (throw exception) + [*] [X] - TypeSymbol (throw exception) + [*] [W] - Value class (tested in MapSpec) + + [*] [ ] - type hint label mapping + [*] [ ] - type hint value mapping [ ] -- Streaming JSON write support [ ] -- BigJSON support (eg. multi-gig file) diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index 37431f9f..9ad4953b 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -22,16 +22,19 @@ object RunMe extends App: import json.* import ScalaJack.* - implicit val blah: ScalaJack[Foo] = sj[Foo](JsonConfig.withTypeHintPolicy(TypeHintPolicy.SCRAMBLE_CLASSNAME)) + val inst = Blah("wow", Some(111)) // Some(Some(None))) // Some(Some(3))) + val js = sj[Blah].toJson(inst) + println(js) // co.blocke.scalajack.internal.CodePrinter.code { // sj[Record] // } - val v = Foo("Hey", Fish("Bloop", false)) + // val y = Foo("You", Dog("Fido", 4), None) + // val v = Foo("Hey", Fish("Bloop", None), None, Color.Blue) // val v = Foo("Hey", "Boo") - println(ScalaJack[Foo].toJson(v)) + // println(ScalaJack[Foo].toJson(v)) // println(sj[Foo](JsonConfig.withTypeHintLabel("bogus")).toJson(v)) // println(sj[Record].toJson(record)) diff --git a/src/main/scala/co.blocke.scalajack/run/Record.scala b/src/main/scala/co.blocke.scalajack/run/Record.scala index 963fb7d1..b13c62ca 100644 --- a/src/main/scala/co.blocke.scalajack/run/Record.scala +++ b/src/main/scala/co.blocke.scalajack/run/Record.scala @@ -36,13 +36,25 @@ case class Record( pets: List[Pet] ) +case class Empl(name: String, age: Int) +case class Foo(a: Empl, b: List[Empl]) +case class Blah(a: String, b: String | Option[Int]) +/* +case class ArrayHolder[T](a: Array[T]) + // case class Foo(name: String, maybe: Option[Int], age: Int, expected: String = "nada", gotit: Option[Int] = Some(5)) -case class Foo(name: String, a: Animal, expected: String = "nada") +case class Foo(name: String, a: Animal, other: Option[Foo], color: Color, expected: String = "nada") // case class Foo(name: String, age: Int, expected: String = "nada") +enum Color: + case Red, Blue, Green + sealed trait Animal +@TypeHint(hintValue = "bow-wow") case class Dog(name: String, numLegs: Int) extends Animal -case class Fish(name: String, isFreshwater: Boolean) extends Animal +@TypeHint(hintValue = "flippy") +case class Fish(name: String, isFreshwater: Option[Boolean]) extends Animal + */ val jsData = """{ diff --git a/src/test/scala/co.blocke.scalajack/json/collections/JavaCollSpec.scala b/src/test/scala/co.blocke.scalajack/json/collections/JavaCollSpec.scala new file mode 100644 index 00000000..8d8d31db --- /dev/null +++ b/src/test/scala/co.blocke.scalajack/json/collections/JavaCollSpec.scala @@ -0,0 +1,120 @@ +package co.blocke.scalajack +package json +package collections + +import ScalaJack.* +import co.blocke.scala_reflection.* +import org.scalatest.funspec.AnyFunSpec +import org.scalatest.matchers.should.Matchers.* +import org.scalatest.* +import TestUtil.* + +import java.util.{ArrayList, Arrays, HashSet} + +class JavaCollSpec() extends AnyFunSpec with JsonMatchers: + + describe(colorString("-------------------------------\n: Java Collection Tests :\n-------------------------------", Console.YELLOW)) { + describe(colorString("+++ Positive Tests +++")) { + it("Set is null must work") { + val inst = JSetHolder[Int](null) + val js = sj[JSetHolder[Int]].toJson(inst) + js should matchJson("""{"a":null}""") + } + it("Set of numeric must work") { + val inst = JSetHolder[Int](HashSet(Arrays.asList(1,2,3))) + val js = sj[JSetHolder[Int]].toJson(inst) + js should matchJson("""{"a":[1,2,3]}""") + } + it("Set of string must work") { + val inst = JSetHolder[String](HashSet(Arrays.asList("a","b","c"))) + val js = sj[JSetHolder[String]].toJson(inst) + js should matchJson("""{"a":["a","b","c"]}""") + } + it("Set of boolean must work") { + val inst = JSetHolder[Boolean](HashSet(Arrays.asList(true,false,true))) + val js = sj[JSetHolder[Boolean]].toJson(inst) + js should matchJson("""{"a":[false,true]}""") + } + it("Set of Set (nested) must work") { + val inst = JSetHolder[List[Int]](HashSet(Arrays.asList(List(1,2),List(3,4)))) + val js = sj[JSetHolder[List[Int]]].toJson(inst) + js should matchJson("""{"a":[[3,4],[1,2]]}""") + } + it("Set of either must work") { + val inst = JSetHolder[Either[Int,Boolean]](HashSet(Arrays.asList(Right(true),Left(15),Right(false)))) + val js = sj[JSetHolder[Either[Int,Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)).toJson(inst) + js should matchJson("""{"a":[true,15,false]}""") + } + it("Set of union must work") { + val inst = JSetHolder[Int|Boolean](HashSet(Arrays.asList(true,15,false))) + val js = sj[JSetHolder[Int|Boolean]].toJson(inst) + js should matchJson("""{"a":[false,true,15]}""") + } + it("Set of option must work") { + val inst = JSetHolder[Option[Int]](HashSet(Arrays.asList(Some(1),None,Some(3)))) + val js = sj[JSetHolder[Option[Int]]].toJson(inst) + js should matchJson("""{"a":[1,3]}""") + } + it("Set of map must work") { + val inst = JSetHolder[Map[String,Int]](HashSet(Arrays.asList(Map("a"->1,"b"->2),Map("c"->3,"d"->4)))) + val js = sj[JSetHolder[Map[String,Int]]].toJson(inst) + js should matchJson("""{"a":[{"a":1,"b":2},{"c":3,"d":4}]}""") + } + it("Set of class must work") { + val inst = JSetHolder[Person](HashSet(Arrays.asList(Person("Bob",35),Person("Sally",54)))) + val js = sj[JSetHolder[Person]].toJson(inst) + js should matchJson("""{"a":[{"name":"Bob","age",35},{"name":"Sally","age",54}]}""") + } + + it("ArrayList is null must work") { + val inst = ArrayListHolder[Int](null) + val js = sj[ArrayListHolder[Int]].toJson(inst) + js should matchJson("""{"a":null}""") + } + it("ArrayList of numeric must work") { + val inst = ArrayListHolder[Int](ArrayList[Int](Arrays.asList(1,2,3))) + val js = sj[ArrayListHolder[Int]].toJson(inst) + js should matchJson("""{"a":[1,2,3]}""") + } + it("ArrayList of string must work") { + val inst = ArrayListHolder[String](ArrayList[String](Arrays.asList("a","b","c"))) + val js = sj[ArrayListHolder[String]].toJson(inst) + js should matchJson("""{"a":["a","b","c"]}""") + } + it("ArrayList of boolean must work") { + val inst = ArrayListHolder[Boolean](ArrayList[Boolean](Arrays.asList(true,false,true))) + val js = sj[ArrayListHolder[Boolean]].toJson(inst) + js should matchJson("""{"a":[true,false,true]}""") + } + it("ArrayList of ArrayList (nested) must work") { + val inst = ArrayListHolder[ArrayList[Int]](ArrayList[ArrayList[Int]](Arrays.asList(ArrayList(Arrays.asList(1,2)),ArrayList(Arrays.asList(3,4))))) + val js = sj[ArrayListHolder[ArrayList[Int]]].toJson(inst) + js should matchJson("""{"a":[[1,2],[3,4]]}""") + } + it("ArrayList of either must work") { + val inst = ArrayListHolder[Either[Int,Boolean]](ArrayList[Either[Int,Boolean]](Arrays.asList(Right(true),Left(15),Right(false)))) + val js = sj[ArrayListHolder[Either[Int,Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)).toJson(inst) + js should matchJson("""{"a":[true,15,false]}""") + } + it("ArrayList of union must work") { + val inst = ArrayListHolder[Int|Boolean](ArrayList[Int|Boolean](Arrays.asList(true,15,false))) + val js = sj[ArrayListHolder[Int|Boolean]].toJson(inst) + js should matchJson("""{"a":[true,15,false]}""") + } + it("ArrayList of option must work") { + val inst = ArrayListHolder[Option[Int]](ArrayList[Option[Int]](Arrays.asList(Some(1),None,Some(3)))) + val js = sj[ArrayListHolder[Option[Int]]].toJson(inst) + js should matchJson("""{"a":[1,3]}""") + } + it("ArrayList of map must work") { + val inst = ArrayListHolder[Map[String,Int]](ArrayList[Map[String,Int]](Arrays.asList(Map("a"->1,"b"->2),Map("c"->3,"d"->4)))) + val js = sj[ArrayListHolder[Map[String,Int]]].toJson(inst) + js should matchJson("""{"a":[{"a":1,"b":2},{"c":3,"d":4}]}""") + } + it("ArrayList of class must work") { + val inst = ArrayListHolder[Person](ArrayList[Person](Arrays.asList(Person("Bob",35),Person("Sally",54)))) + val js = sj[ArrayListHolder[Person]].toJson(inst) + js should matchJson("""{"a":[{"name":"Bob","age",35},{"name":"Sally","age",54}]}""") + } + } + } \ No newline at end of file diff --git a/src/test/scala/co.blocke.scalajack/json/collections/JavaMapSpec.scala b/src/test/scala/co.blocke.scalajack/json/collections/JavaMapSpec.scala new file mode 100644 index 00000000..ecbc316e --- /dev/null +++ b/src/test/scala/co.blocke.scalajack/json/collections/JavaMapSpec.scala @@ -0,0 +1,129 @@ +package co.blocke.scalajack +package json +package collections + +import ScalaJack.* +import co.blocke.scala_reflection.* +import org.scalatest.funspec.AnyFunSpec +import org.scalatest.matchers.should.Matchers.* +import org.scalatest.* +import TestUtil.* + +import java.util.UUID + +class JavaMapSpec() extends AnyFunSpec with JsonMatchers: + + describe(colorString("-------------------------------\n: Java Map Tests :\n-------------------------------", Console.YELLOW)) { + describe(colorString("+++ Positive Tests +++")) { + it("Map is null must work") { + val inst = JMapHolder[Int,Int](null) + val js = sj[JMapHolder[Int,Int]].toJson(inst) + js should matchJson("""{"a":null}""") + } + it("Map key of string must work") { + val m: java.util.Map[String,Int] = new java.util.HashMap[String,Int]() + m.put("x",1) + m.put("y",2) + val inst = JMapHolder[String,Int](new java.util.HashMap(m)) + val js = sj[JMapHolder[String,Int]].toJson(inst) + js should matchJson("""{"a":{"x":1,"y":2}}""") + } + it("Map key of long must work") { + val m: java.util.Map[Long,Int] = new java.util.HashMap[Long,Int]() + m.put(15L,1) + m.put(25L,2) + val inst = JMapHolder[Long,Int](new java.util.HashMap(m)) + val js = sj[JMapHolder[Long,Int]].toJson(inst) + js should matchJson("""{"a":{"15":1,"25":2}}""") + } + it("Map key of boolean must work") { + val m: java.util.Map[Boolean,Int] = new java.util.HashMap[Boolean,Int]() + m.put(true,1) + m.put(false,2) + val inst = JMapHolder[Boolean,Int](new java.util.HashMap(m)) + val js = sj[JMapHolder[Boolean,Int]].toJson(inst) + js should matchJson("""{"a":{"true":1,"false":2}}""") + } + it("Map key of uuid must work") { + val m: java.util.Map[UUID,String] = new java.util.HashMap[UUID,String]() + m.put(UUID.fromString("1b9ab03f-26a3-4ec5-a8dd-d5122ff86b03"),"x") + m.put(UUID.fromString("09abdeb1-8b07-4683-8f97-1f5621696008"),"y") + val inst = JMapHolder[UUID,String](new java.util.HashMap(m)) + val js = sj[JMapHolder[UUID,String]].toJson(inst) + js should matchJson("""{"a":{"1b9ab03f-26a3-4ec5-a8dd-d5122ff86b03":"x","09abdeb1-8b07-4683-8f97-1f5621696008":"y"}}""") + } + + it("Map value of string must work") { + val m: java.util.Map[String,String] = new java.util.HashMap[String,String]() + m.put("w","x") + m.put("y","z") + val inst = JMapHolder[String,String](new java.util.HashMap(m)) + val js = sj[JMapHolder[String,String]].toJson(inst) + js should matchJson("""{"a":{"w":"x","y":"z"}}""") + } + it("Map value of long must work") { + val m: java.util.Map[String,Long] = new java.util.HashMap[String,Long]() + m.put("w",3L) + m.put("y",4L) + val inst = JMapHolder[String,Long](new java.util.HashMap(m)) + val js = sj[JMapHolder[String,Long]].toJson(inst) + js should matchJson("""{"a":{"w":3,"y":4}}""") + } + it("Map value of boolean must work") { + val m: java.util.Map[String,Boolean] = new java.util.HashMap[String,Boolean]() + m.put("w",true) + m.put("y",false) + val inst = JMapHolder[String,Boolean](new java.util.HashMap(m)) + val js = sj[JMapHolder[String,Boolean]].toJson(inst) + js should matchJson("""{"a":{"w":true,"y":false}}""") + } + it("Map value of uuid must work") { + val m: java.util.Map[String,UUID] = new java.util.HashMap[String,UUID]() + m.put("x",UUID.fromString("1b9ab03f-26a3-4ec5-a8dd-d5122ff86b03")) + m.put("y",UUID.fromString("09abdeb1-8b07-4683-8f97-1f5621696008")) + val inst = JMapHolder[String,UUID](new java.util.HashMap(m)) + val js = sj[JMapHolder[String,UUID]].toJson(inst) + js should matchJson("""{"a":{"x":"1b9ab03f-26a3-4ec5-a8dd-d5122ff86b03","y":"09abdeb1-8b07-4683-8f97-1f5621696008"}}""") + } + it("Map value of Seq must work") { + val m: java.util.Map[String,List[Int]] = new java.util.HashMap[String,List[Int]]() + m.put("w",List(1,2)) + m.put("y",List(3,4)) + val inst = JMapHolder[String,List[Int]](new java.util.HashMap(m)) + val js = sj[JMapHolder[String,List[Int]]].toJson(inst) + js should matchJson("""{"a":{"w":[1,2],"y":[3,4]}}""") + } + it("Map value of Map (nested) must work") { + val m: java.util.Map[String,Map[String,Int]] = new java.util.HashMap[String,Map[String,Int]]() + m.put("w",Map("r"->3,"t"->4)) + m.put("y",Map("s"->7,"q"->9)) + val inst = JMapHolder[String,Map[String,Int]](new java.util.HashMap(m)) + val js = sj[JMapHolder[String,Map[String,Int]]].toJson(inst) + js should matchJson("""{"a":{"w":{"r":3,"t":4},"y":{"s":7,"q":9}}}""") + } + it("Map value of class must work") { + val m: java.util.Map[String,Person] = new java.util.HashMap[String,Person]() + m.put("w",Person("Bob",34)) + m.put("y",Person("Sally",25)) + val inst = JMapHolder[String,Person](new java.util.HashMap(m)) + val js = sj[JMapHolder[String,Person]].toJson(inst) + js should matchJson("""{"a":{"w":{"name":"Bob","age":34},"y":{"name":"Sally","age":25}}}""") + } + it("Map value of union type must work") { + val m: java.util.Map[String,Int|List[String]] = new java.util.HashMap[String,Int|List[String]]() + m.put("w",3) + m.put("y",List("wow","blah")) + val inst = JMapHolder[String,Int|List[String]](new java.util.HashMap(m)) + val js = sj[JMapHolder[String,Int|List[String]]].toJson(inst) + js should matchJson("""{"a":{"w":3,"y":["wow","blah"]}}""") + } + it("Map value of value class must work") { + val m: java.util.Map[String,Distance] = new java.util.HashMap[String,Distance]() + m.put("w",new Distance(1.23)) + m.put("y",Distance(4.56)) + val inst = JMapHolder[String,Distance](new java.util.HashMap(m)) + val js = sj[JMapHolder[String,Distance]].toJson(inst) + js should matchJson("""{"a":{"w":1.23,"y":4.56}}""") + } + } + } \ No newline at end of file diff --git a/src/test/scala/co.blocke.scalajack/json/collections/MapSpec.scala b/src/test/scala/co.blocke.scalajack/json/collections/MapSpec.scala new file mode 100644 index 00000000..55ff9277 --- /dev/null +++ b/src/test/scala/co.blocke.scalajack/json/collections/MapSpec.scala @@ -0,0 +1,95 @@ +package co.blocke.scalajack +package json +package collections + +import ScalaJack.* +import co.blocke.scala_reflection.* +import org.scalatest.funspec.AnyFunSpec +import org.scalatest.matchers.should.Matchers.* +import org.scalatest.* +import TestUtil.* + +import java.util.UUID + +class MapSpec() extends AnyFunSpec with JsonMatchers: + + describe(colorString("-------------------------------\n: Map Tests :\n-------------------------------", Console.YELLOW)) { + describe(colorString("+++ Positive Tests +++")) { + it("Map is null must work") { + val inst = MapHolder[Int,Int](null) + val js = sj[MapHolder[Int,Int]].toJson(inst) + js should matchJson("""{"a":null}""") + } + it("Map key of string must work") { + val inst = MapHolder[String,Int](Map("x"->1,"y"->2)) + val js = sj[MapHolder[String,Int]].toJson(inst) + js should matchJson("""{"a":{"x":1,"y":2}}""") + } + it("Map key of long must work") { + val inst = MapHolder[Long,Int](Map(15L->1,25L->2)) + val js = sj[MapHolder[Long,Int]].toJson(inst) + js should matchJson("""{"a":{"15":1,"25":2}}""") + } + it("Map key of boolean must work") { + val inst = MapHolder[Boolean,Int](Map(true->1,false->2)) + val js = sj[MapHolder[Boolean,Int]].toJson(inst) + js should matchJson("""{"a":{"true":1,"false":2}}""") + } + it("Map key of uuid must work") { + val inst = MapHolder[UUID,String](Map(UUID.fromString("1b9ab03f-26a3-4ec5-a8dd-d5122ff86b03")->"x",UUID.fromString("09abdeb1-8b07-4683-8f97-1f5621696008")->"y")) + val js = sj[MapHolder[UUID,String]].toJson(inst) + js should matchJson("""{"a":{"1b9ab03f-26a3-4ec5-a8dd-d5122ff86b03":"x","09abdeb1-8b07-4683-8f97-1f5621696008":"y"}}""") + } + + it("Map value of string must work") { + val inst = MapHolder[String,String](Map("w"->"x","y"->"z")) + val js = sj[MapHolder[String,String]].toJson(inst) + js should matchJson("""{"a":{"w":"x","y":"z"}}""") + } + it("Map value of long must work") { + val inst = MapHolder[String,Long](Map("w"->3L,"y"->4L)) + val js = sj[MapHolder[String,Long]].toJson(inst) + js should matchJson("""{"a":{"w":3,"y":4}}""") + } + it("Map value of boolean must work") { + val inst = MapHolder[String,Boolean](Map("w"->true,"y"->false)) + val js = sj[MapHolder[String,Boolean]].toJson(inst) + js should matchJson("""{"a":{"w":true,"y":false}}""") + } + it("Map value of uuid must work") { + val inst = MapHolder[String,UUID](Map("x"->UUID.fromString("1b9ab03f-26a3-4ec5-a8dd-d5122ff86b03"),"y"->UUID.fromString("09abdeb1-8b07-4683-8f97-1f5621696008"))) + val js = sj[MapHolder[String,UUID]].toJson(inst) + js should matchJson("""{"a":{"x":"1b9ab03f-26a3-4ec5-a8dd-d5122ff86b03","y":"09abdeb1-8b07-4683-8f97-1f5621696008"}}""") + } + it("Map value of Seq must work") { + val inst = MapHolder[String,List[Int]](Map("w"->List(1,2),"y"->List(3,4))) + val js = sj[MapHolder[String,List[Int]]].toJson(inst) + js should matchJson("""{"a":{"w":[1,2],"y":[3,4]}}""") + } + it("Map value of Map (nested) must work") { + val inst = MapHolder[String,Map[String,Int]](Map("w"->Map("r"->3,"t"->4),"y"->Map("s"->7,"q"->9))) + val js = sj[MapHolder[String,Map[String,Int]]].toJson(inst) + js should matchJson("""{"a":{"w":{"r":3,"t":4},"y":{"s":7,"q":9}}}""") + } + it("Map value of class must work") { + val inst = MapHolder[String,Person](Map("w"->Person("Bob",34),"y"->Person("Sally",25))) + val js = sj[MapHolder[String,Person]].toJson(inst) + js should matchJson("""{"a":{"w":{"name":"Bob","age":34},"y":{"name":"Sally","age":25}}}""") + } + it("Map value of union type must work") { + val inst = MapHolder[String,Int|List[String]](Map("w"->3,"y"->List("wow","blah"))) + val js = sj[MapHolder[String,Int|List[String]]].toJson(inst) + js should matchJson("""{"a":{"w":3,"y":["wow","blah"]}}""") + } + it("Map value of value class must work") { + val inst = MapHolder[String,Distance](Map("w"->new Distance(1.23),"y"->Distance(4.56))) + val js = sj[MapHolder[String,Distance]].toJson(inst) + js should matchJson("""{"a":{"w":1.23,"y":4.56}}""") + } + it("Mutable Map value must work") { + val inst = MMapHolder[String,Distance](scala.collection.mutable.HashMap("w"->new Distance(1.23),"y"->Distance(4.56))) + val js = sj[MMapHolder[String,Distance]].toJson(inst) + js should matchJson("""{"a":{"w":1.23,"y":4.56}}""") + } + } + } \ No newline at end of file diff --git a/src/test/scala/co.blocke.scalajack/json/collections/Model.scala b/src/test/scala/co.blocke.scalajack/json/collections/Model.scala new file mode 100644 index 00000000..c8729899 --- /dev/null +++ b/src/test/scala/co.blocke.scalajack/json/collections/Model.scala @@ -0,0 +1,22 @@ +package co.blocke.scalajack +package json +package collections + +import java.util.{ArrayList, Set=>JSet, Map=>JMap} + +case class Person(name: String, age: Int) + +case class SeqHolder[T](a: Seq[T]) +case class SetHolder[T](a: Set[T]) +case class ArrayHolder[T](a: Array[T]) + +case class MapHolder[T,V](a: Map[T,V]) +case class MMapHolder[T,V](a: scala.collection.mutable.Map[T,V]) +case class JMapHolder[T,V](a: JMap[T,V]) + +class Distance(val meters: Double) extends AnyVal + +case class TupleHolder[A,B,C]( a:(A,B,C)) + +case class ArrayListHolder[T](a: ArrayList[T]) +case class JSetHolder[T](a: JSet[T]) \ No newline at end of file diff --git a/src/test/scala/co.blocke.scalajack/json/collections/SeqSetArraySpec.scala b/src/test/scala/co.blocke.scalajack/json/collections/SeqSetArraySpec.scala new file mode 100644 index 00000000..e5568d26 --- /dev/null +++ b/src/test/scala/co.blocke.scalajack/json/collections/SeqSetArraySpec.scala @@ -0,0 +1,171 @@ +package co.blocke.scalajack +package json +package collections + +import ScalaJack.* +import co.blocke.scala_reflection.* +import org.scalatest.funspec.AnyFunSpec +import org.scalatest.matchers.should.Matchers.* +import org.scalatest.* +import TestUtil.* + +import scala.collection.immutable.HashSet + +class SeqSetArraySpec() extends AnyFunSpec with JsonMatchers: + + describe(colorString("-------------------------------\n: Seq, Set, and Array Tests :\n-------------------------------", Console.YELLOW)) { + describe(colorString("+++ Positive Tests +++")) { + it("Seq is null must work") { + val inst = SeqHolder[Int](null) + val js = sj[SeqHolder[Int]].toJson(inst) + js should matchJson("""{"a":null}""") + } + it("Seq of numeric must work") { + val inst = SeqHolder[Int](List(1,2,3)) + val js = sj[SeqHolder[Int]].toJson(inst) + js should matchJson("""{"a":[1,2,3]}""") + } + it("Seq of string must work") { + val inst = SeqHolder[String](List("a","b","c")) + val js = sj[SeqHolder[String]].toJson(inst) + js should matchJson("""{"a":["a","b","c"]}""") + } + it("Seq of boolean must work") { + val inst = SeqHolder[Boolean](List(true,false,true)) + val js = sj[SeqHolder[Boolean]].toJson(inst) + js should matchJson("""{"a":[true,false,true]}""") + } + it("Seq of seq (nested) must work") { + val inst = SeqHolder[List[Int]](List(List(1,2),List(3,4))) + val js = sj[SeqHolder[List[Int]]].toJson(inst) + js should matchJson("""{"a":[[1,2],[3,4]]}""") + } + it("Seq of either must work") { + val inst = SeqHolder[Either[Int,Boolean]](List(Right(true),Left(15),Right(false))) + val js = sj[SeqHolder[Either[Int,Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)).toJson(inst) + js should matchJson("""{"a":[true,15,false]}""") + } + it("Seq of union must work") { + val inst = SeqHolder[Int|Boolean](List(true,15,false)) + val js = sj[SeqHolder[Int|Boolean]].toJson(inst) + js should matchJson("""{"a":[true,15,false]}""") + } + it("Seq of option must work") { + val inst = SeqHolder[Option[Int]](List(Some(1),None,Some(3))) + val js = sj[SeqHolder[Option[Int]]].toJson(inst) + js should matchJson("""{"a":[1,3]}""") + } + it("Seq of map must work") { + val inst = SeqHolder[Map[String,Int]](List(Map("a"->1,"b"->2),Map("c"->3,"d"->4))) + val js = sj[SeqHolder[Map[String,Int]]].toJson(inst) + js should matchJson("""{"a":[{"a":1,"b":2},{"c":3,"d":4}]}""") + } + it("Seq of class must work") { + val inst = SeqHolder[Person](List(Person("Bob",35),Person("Sally",54))) + val js = sj[SeqHolder[Person]].toJson(inst) + js should matchJson("""{"a":[{"name":"Bob","age",35},{"name":"Sally","age",54}]}""") + } + + it("Set is null must work") { + val inst = SetHolder[Int](null) + val js = sj[SetHolder[Int]].toJson(inst) + js should matchJson("""{"a":null}""") + } + it("Set of numeric must work") { + val inst = SetHolder[Int](HashSet(1,2,3)) + val js = sj[SetHolder[Int]].toJson(inst) + js should matchJson("""{"a":[1,2,3]}""") + } + it("Set of string must work") { + val inst = SetHolder[String](HashSet("a","b","c")) + val js = sj[SetHolder[String]].toJson(inst) + js should matchJson("""{"a":["a","b","c"]}""") + } + it("Set of boolean must work") { + val inst = SetHolder[Boolean](HashSet(true,false,true)) + val js = sj[SetHolder[Boolean]].toJson(inst) + js should matchJson("""{"a":[false,true]}""") + } + it("Set of Set (nested) must work") { + val inst = SetHolder[HashSet[Int]](HashSet(HashSet(1,2),HashSet(3,4))) + val js = sj[SetHolder[HashSet[Int]]].toJson(inst) + js should matchJson("""{"a":[[3,4],[1,2]]}""") + } + it("Set of either must work") { + val inst = SetHolder[Either[Int,Boolean]](HashSet(Right(true),Left(15),Right(false))) + val js = sj[SetHolder[Either[Int,Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)).toJson(inst) + js should matchJson("""{"a":[15,true,false]}""") + } + it("Set of union must work") { + val inst = SetHolder[Int|Boolean](HashSet(true,15,false)) + val js = sj[SetHolder[Int|Boolean]].toJson(inst) + js should matchJson("""{"a":[false,true,15]}""") + } + it("Set of option must work") { + val inst = SetHolder[Option[Int]](HashSet(Some(1),None,Some(3))) + val js = sj[SetHolder[Option[Int]]].toJson(inst) + js should matchJson("""{"a":[3,1]}""") + } + it("Set of map must work") { + val inst = SetHolder[Map[String,Int]](HashSet(Map("a"->1,"b"->2),Map("c"->3,"d"->4))) + val js = sj[SetHolder[Map[String,Int]]].toJson(inst) + js should matchJson("""{"a":[{"c":3,"d":4},{"a":1,"b":2}]}""") + } + it("Set of class must work") { + val inst = SetHolder[Person](HashSet(Person("Bob",35),Person("Sally",54))) + val js = sj[SetHolder[Person]].toJson(inst) + js should matchJson("""{"a":[{"name":"Bob","age",35},{"name":"Sally","age",54}]}""") + } + + it("Array is null must work") { + val inst = ArrayHolder[Int](null) + val js = sj[ArrayHolder[Int]].toJson(inst) + js should matchJson("""{"a":null}""") + } + it("Array of numeric must work") { + val inst = ArrayHolder[Int](Array(1,2,3)) + val js = sj[ArrayHolder[Int]].toJson(inst) + js should matchJson("""{"a":[1,2,3]}""") + } + it("Array of string must work") { + val inst = ArrayHolder[String](Array("a","b","c")) + val js = sj[ArrayHolder[String]].toJson(inst) + js should matchJson("""{"a":["a","b","c"]}""") + } + it("Array of boolean must work") { + val inst = ArrayHolder[Boolean](Array(true,false,true)) + val js = sj[ArrayHolder[Boolean]].toJson(inst) + js should matchJson("""{"a":[true,false,true]}""") + } + it("Array of Array (nested) must work") { + val inst = ArrayHolder[Array[Int]](Array(Array(1,2),Array(3,4))) + val js = sj[ArrayHolder[Array[Int]]].toJson(inst) + js should matchJson("""{"a":[[1,2],[3,4]]}""") + } + it("Array of either must work") { + val inst = ArrayHolder[Either[Int,Boolean]](Array(Right(true),Left(15),Right(false))) + val js = sj[ArrayHolder[Either[Int,Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)).toJson(inst) + js should matchJson("""{"a":[true,15,false]}""") + } + it("Array of union must work") { + val inst = ArrayHolder[Int|Boolean](Array(true,15,false)) + val js = sj[ArrayHolder[Int|Boolean]].toJson(inst) + js should matchJson("""{"a":[true,15,false]}""") + } + it("Array of option must work") { + val inst = ArrayHolder[Option[Int]](Array(Some(1),None,Some(3))) + val js = sj[ArrayHolder[Option[Int]]].toJson(inst) + js should matchJson("""{"a":[1,3]}""") + } + it("Array of map must work") { + val inst = ArrayHolder[Map[String,Int]](Array(Map("a"->1,"b"->2),Map("c"->3,"d"->4))) + val js = sj[ArrayHolder[Map[String,Int]]].toJson(inst) + js should matchJson("""{"a":[{"a":1,"b":2},{"c":3,"d":4}]}""") + } + it("Array of class must work") { + val inst = ArrayHolder[Person](Array(Person("Bob",35),Person("Sally",54))) + val js = sj[ArrayHolder[Person]].toJson(inst) + js should matchJson("""{"a":[{"name":"Bob","age",35},{"name":"Sally","age",54}]}""") + } + } + } \ No newline at end of file diff --git a/src/test/scala/co.blocke.scalajack/json/collections/TupleSpec.scala b/src/test/scala/co.blocke.scalajack/json/collections/TupleSpec.scala new file mode 100644 index 00000000..5b4b8dff --- /dev/null +++ b/src/test/scala/co.blocke.scalajack/json/collections/TupleSpec.scala @@ -0,0 +1,34 @@ +package co.blocke.scalajack +package json +package collections + +import ScalaJack.* +import co.blocke.scala_reflection.* +import org.scalatest.funspec.AnyFunSpec +import org.scalatest.matchers.should.Matchers.* +import org.scalatest.* +import TestUtil.* + +import java.util.UUID + +class TupleSpec() extends AnyFunSpec with JsonMatchers: + + describe(colorString("-------------------------------\n: Tuple Tests :\n-------------------------------", Console.YELLOW)) { + describe(colorString("+++ Positive Tests +++")) { + it("Tuple is null must work") { + val inst = TupleHolder[Int,String,Boolean](null) + val js = sj[TupleHolder[Int,String,Boolean]].toJson(inst) + js should matchJson("""{"a":null}""") + } + it("Tuple of simple types must work") { + val inst = TupleHolder[Int,String,Boolean]((15,"wow",true)) + val js = sj[TupleHolder[Int,String,Boolean]].toJson(inst) + js should matchJson("""{"a":[15,"wow",true]}""") + } + it("Tuple of collecitons (including another tuple) must work") { + val inst = TupleHolder[Seq[Int],Map[String,Long],(Double,Char,Boolean)]((List(1,2),Map("a"->3L,"b"->4L),(1.23D,'X',true))) + val js = sj[TupleHolder[Seq[Int],Map[String,Long],(Double,Char,Boolean)]].toJson(inst) + js should matchJson("""{"a":[[1,2],{"a":3,"b":4},[1.23,"X",true]]}""") + } + } + } \ No newline at end of file diff --git a/src/test/scala/co.blocke.scalajack/json/misc/Model.scala b/src/test/scala/co.blocke.scalajack/json/misc/Model.scala new file mode 100644 index 00000000..cb8347ba --- /dev/null +++ b/src/test/scala/co.blocke.scalajack/json/misc/Model.scala @@ -0,0 +1,29 @@ +package co.blocke.scalajack +package json +package misc + +import java.util.Optional +import scala.util.* + +case class Person(name: String, age: Int) + +case class OptionHolder[T]( + a: Option[T], // straight Option + b: (Option[T],String), // tuple w/Option + c: List[Option[T]], // Seq of Option + d: Map[Int, Option[T]], // Map of Option + e: T|Option[T], // Union of Option (R) + f: Option[T]|T, // Union of Option (L) + g: Option[Option[T]], // Nested Option + h: Option[Person], // Option of Class + i: Either[T,Option[T]], // Either of Option (R) + j: Either[Option[T],T] // Either of Option (L) + ) + +case class TryHolder[T]( a: Try[T] ) +case class TryHolder2[T]( a: Seq[Try[T]], b: (Try[T],Try[T]) ) + +case class LRHolder[T,U](a: Seq[T|U], b: (T|U,T|U)) + +case class ComplexEither[T](a: Option[Either[String,Option[T]]]) + diff --git a/src/test/scala/co.blocke.scalajack/json/misc/OptionLRTrySpec.scala b/src/test/scala/co.blocke.scalajack/json/misc/OptionLRTrySpec.scala new file mode 100644 index 00000000..ef4a2778 --- /dev/null +++ b/src/test/scala/co.blocke.scalajack/json/misc/OptionLRTrySpec.scala @@ -0,0 +1,188 @@ +package co.blocke.scalajack +package json +package misc + +import ScalaJack.* +import co.blocke.scala_reflection.* +import org.scalatest.funspec.AnyFunSpec +import org.scalatest.matchers.should.Matchers.* +import org.scalatest.* +import scala.util.* +import TestUtil.* + +import java.util.UUID + +class OptionLRTrySpec() extends AnyFunSpec with JsonMatchers: + + describe(colorString("-------------------------------\n: Option Tests :\n-------------------------------", Console.YELLOW)) { + describe(colorString("+++ Positive Tests +++")) { + it("Non-empty Options must work") { + val inst = OptionHolder[Int]( + Some(5), // straight Option + (Some(1),"ok"), // tuple w/Option + List(Some(1),None,Some(3)), // Seq of Option + Map(1->Some(2),3->Some(4)), // Map of Option + Some(99), // Union of Option (R) + Some(100), // Union of Option (L) + Some(Some(0)), // Nested Option + Some(Person("BoB",34)), // Option of class + Right(Some(15)), // Either of Option (R) + Left(Some(-3)) // Either of Option (L) + ) + val js = sj[OptionHolder[Int]].toJson(inst) + js should matchJson("""{"a":5,"b":[1,"ok"],"c":[1,3],"d":{"1":2,"3":4},"e":99,"f":100,"g":0,"h":{"name":"BoB","age":34},"i":15}""") + } + it("Empty Options must work (default)") { + val inst = OptionHolder[Int]( + None, // straight Option + (None,"ok"), // tuple w/Option + List(None,None,None), // Seq of Option + Map(1->None,3->None), // Map of Option + None, // Union of Option (R) + None, // Union of Option (L) + Some(None), // Nested Option + None, // Option of class + Right(None), // Either of Option (R) + Left(None) // Either of Option (L) + ) + val js = sj[OptionHolder[Int]].toJson(inst) + js should matchJson("""{"b":[null,"ok"],"c":[],"d":{}}""") + } + it("Empty Options must work (config noneAsNull = true)") { + val inst = OptionHolder[Int]( + None, // straight Option + (None,"ok"), // tuple w/Option + List(None,None,None), // Seq of Option + Map(1->None,3->None), // Map of Option + None, // Union of Option (R) + None, // Union of Option (L) + Some(None), // Nested Option + None, // Option of class + Right(None), // Either of Option (R) + Left(None) // Either of Option (L) + ) + val js = sj[OptionHolder[Int]]( + JsonConfig.withNoneAsNull(true).withEitherLeftHandling(EitherLeftPolicy.AS_VALUE) + ).toJson(inst) + js should matchJson("""{"a":null,"b":[null,"ok"],"c":[null,null,null],"d":{"1":null,"3":null},"e":null,"f":null,"g":null,"h":null,"i":null,"j":null}""") + } + } + } + + describe(colorString("-------------------------------\n: Either Tests :\n-------------------------------", Console.YELLOW)) { + describe(colorString("+++ Positive Tests +++")) { + it("Complex Either/Option must work (non-None)") { + val inst = ComplexEither[Int](Some(Right(Some(3)))) + val js = sj[ComplexEither[Int]].toJson(inst) + js should matchJson("""{"a":3}""") + } + it("Complex Either/Option must work (None no-write default)") { + val inst = ComplexEither[Int](Some(Right(None))) + val js = sj[ComplexEither[Int]].toJson(inst) + js should matchJson("""{}""") + } + it("Complex Either/Option must work (NoneAsNull)") { + val inst = ComplexEither[Int](Some(Right(None))) + val js = sj[ComplexEither[Int]](JsonConfig.withNoneAsNull(true)).toJson(inst) + js should matchJson("""{"a":null}""") + } + it("Complex Either/Option must work (Failure-NO_WRITE)") { + val inst = ComplexEither[Int](Some(Left("err"))) + val js = sj[ComplexEither[Int]].toJson(inst) + js should matchJson("""{}""") + } + it("Complex Either/Option must work (Failure-AS_VALUE)") { + val inst = ComplexEither[Int](Some(Left("err"))) + val js = sj[ComplexEither[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)).toJson(inst) + js should matchJson("""{"a":"err"}""") + } + } + } + + describe(colorString("-------------------------------\n: LR Tests :\n-------------------------------", Console.YELLOW)) { + describe(colorString("+++ Positive Tests +++")) { + it("LR (union) must work with Option (non-None)") { + val inst = LRHolder[Option[Int],String](List(Some(5),"x"), ("y",Some(10))) + val js = sj[LRHolder[Option[Int],String]].toJson(inst) + js should matchJson("""{"a":[5,"x"],"b":["y",10]}""") + } + it("LR (union) must work with Option (None)") { + val inst = LRHolder[Option[Int],String](List(None,"x"), ("y",None)) + val js = sj[LRHolder[Option[Int],String]].toJson(inst) + js should matchJson("""{"a":["x"],"b":["y",null]}""") + } + it("LR (union) must work with Try of Option (non-None)") { + val inst = LRHolder[Try[Option[Int]],String](List(Success(Some(5)),"x"), ("y",Success(Some(10)))) + val js = sj[LRHolder[Try[Option[Int]],String]].toJson(inst) + js should matchJson("""{"a":[5,"x"],"b":["y",10]}""") + } + it("LR (union) must work with Try of Option (Success(None))") { + val inst = LRHolder[Try[Option[Int]],String](List(Success(None),"x"), ("y",Success(None))) + val js = sj[LRHolder[Try[Option[Int]],String]].toJson(inst) + js should matchJson("""{"a":["x"],"b":["y",null]}""") + } + it("LR (union) must work with Try of Option (Failure)") { + val inst = LRHolder[Try[Option[Int]],String](List(Failure(new Exception("boom")),"x"), ("y",Failure(new Exception("boom2")))) + val js = sj[LRHolder[Try[Option[Int]],String]].toJson(inst) + js should matchJson("""{"a":["x"],"b":["y",null]}""") + } + } + } + + describe(colorString("-------------------------------\n: Try Tests :\n-------------------------------", Console.YELLOW)) { + describe(colorString("+++ Positive Tests +++")) { + it("Try must work (Success)") { + val inst = TryHolder(Success(15)) + val js = sj[TryHolder[Int]].toJson(inst) + js should matchJson("""{"a":15}""") + } + it("Try of Option (non-None) must work (Success)") { + val inst = TryHolder[Option[Int]](Success(Some(15))) + val js = sj[TryHolder[Option[Int]]].toJson(inst) + js should matchJson("""{"a":15}""") + } + it("Try of Option (None) must work (Success)") { + val inst = TryHolder[Option[Int]](Success(None)) + val js = sj[TryHolder[Option[Int]]].toJson(inst) + js should matchJson("""{}""") + } + it("Try w/policy AS_NULL must work (Failure)") { + val inst = TryHolder[Int](Failure(new Exception("boom"))) + val js = sj[TryHolder[Int]](JsonConfig.withTryFailureHandling(TryPolicy.AS_NULL)).toJson(inst) + js should matchJson("""{"a":null}""") + } + it("Try w/policy NO_WRITE must work (Failure)") { + val inst = TryHolder[Int](Failure(new Exception("boom"))) + val js = sj[TryHolder[Int]](JsonConfig.withTryFailureHandling(TryPolicy.NO_WRITE)).toJson(inst) + js should matchJson("""{}""") + } + it("Try w/policy ERR_MSG_STRING must work (Failure)") { + val inst = TryHolder[Int](Failure(new Exception("boom"))) + val js = sj[TryHolder[Int]](JsonConfig.withTryFailureHandling(TryPolicy.ERR_MSG_STRING)).toJson(inst) + js should matchJson("""{"a":"Try Failure with msg: boom"}""") + } + it("Try w/policy ATHROW_EXCEPTIONS_NULL must work (Failure)") { + val inst = TryHolder[Int](Failure(new Exception("boom"))) + val caught = + intercept[java.lang.Exception] { + sj[TryHolder[Int]](JsonConfig.withTryFailureHandling(TryPolicy.THROW_EXCEPTION)).toJson(inst) + } + assert(caught.getMessage == "boom") + } + it("Seq and Tuple of Try must work for AS_NULL (Failure)") { + val inst = TryHolder2[Int]( List(Success(1),Failure(new Exception("boom")),Success(3)), (Failure(new Exception("boom")), Success(0))) + val js = sj[TryHolder2[Int]](JsonConfig.withTryFailureHandling(TryPolicy.AS_NULL)).toJson(inst) + js should matchJson("""{"a":[1,null,3],"b":[null,0]}""") + } + it("Seq and Tuple of Try must work for NO_WRITE (Failure)") { + val inst = TryHolder2[Int]( List(Success(1),Failure(new Exception("boom")),Success(3)), (Failure(new Exception("boom")), Success(0))) + val js = sj[TryHolder2[Int]](JsonConfig.withTryFailureHandling(TryPolicy.NO_WRITE)).toJson(inst) + js should matchJson("""{"a":[1,3],"b":[null,0]}""") + } + it("Seq and Tuple of Try of an Option must work for NO_WRITE (Failure)") { + val inst = TryHolder2[Option[Int]]( List(Success(None),Failure(new Exception("boom")),Success(Some(3))), (Failure(new Exception("boom")), Success(None)) ) + val js = sj[TryHolder2[Option[Int]]](JsonConfig.withTryFailureHandling(TryPolicy.NO_WRITE)).toJson(inst) + js should matchJson("""{"a":[3],"b":[null,null]}""") + } + } + } \ No newline at end of file diff --git a/src/test/scala/co.blocke.scalajack/json/primitives/JavaPrim.scala b/src/test/scala/co.blocke.scalajack/json/primitives/JavaPrimSpec.scala similarity index 79% rename from src/test/scala/co.blocke.scalajack/json/primitives/JavaPrim.scala rename to src/test/scala/co.blocke.scalajack/json/primitives/JavaPrimSpec.scala index 05a3b009..16781ef5 100644 --- a/src/test/scala/co.blocke.scalajack/json/primitives/JavaPrim.scala +++ b/src/test/scala/co.blocke.scalajack/json/primitives/JavaPrimSpec.scala @@ -9,31 +9,23 @@ import org.scalatest.matchers.should.Matchers.* import org.scalatest.* import TestUtil.* -import java.lang.{ - Boolean => JBoolean, - Byte => JByte, - Double => JDouble, - Float => JFloat, - Integer => JInt, - Long => JLong, - Short => JShort -} -import java.math.{ BigDecimal => JBigDecimal, BigInteger => JBigInteger } +import java.lang.{Boolean as JBoolean, Byte as JByte, Double as JDouble, Float as JFloat, Integer as JInt, Long as JLong, Short as JShort} +import java.math.{BigDecimal as JBigDecimal, BigInteger as JBigInteger} -class JavaPrim() extends AnyFunSpec with JsonMatchers: +class JavaPrimSpec() extends AnyFunSpec with JsonMatchers: - describe(colorString("---------------------------\n: Java Primitive Tests :\n---------------------------", Console.YELLOW)) { + describe(colorString("--------------------------\n: Java Primitive Tests :\n--------------------------", Console.YELLOW)) { describe(colorString("+++ Positive Tests +++")) { it("BigDecimal must work") { val inst = SampleJBigDecimal( - JBigDecimal.ZERO, - JBigDecimal.ONE, - JBigDecimal.TEN, - new JBigDecimal( - "0.1499999999999999944488848768742172978818416595458984375" - ), - null - ) + JBigDecimal.ZERO, + JBigDecimal.ONE, + JBigDecimal.TEN, + new JBigDecimal( + "0.1499999999999999944488848768742172978818416595458984375" + ), + null + ) val js = sj[SampleJBigDecimal].toJson(inst) js should matchJson("""{"bd1":0,"bd2":1,"bd3":10,"bd4":0.1499999999999999944488848768742172978818416595458984375,"bd5":null}""") // inst shouldEqual ScalaJack.read[SampleJBigDecimal](js) @@ -41,14 +33,14 @@ class JavaPrim() extends AnyFunSpec with JsonMatchers: it("BigInteger must work") { val inst = SampleJBigInteger( - JBigInteger.ZERO, - JBigInteger.ONE, - JBigInteger.TEN, - new JBigInteger("-90182736451928374653345"), - new JBigInteger("90182736451928374653345"), - new JBigInteger("0"), - null - ) + JBigInteger.ZERO, + JBigInteger.ONE, + JBigInteger.TEN, + new JBigInteger("-90182736451928374653345"), + new JBigInteger("90182736451928374653345"), + new JBigInteger("0"), + null + ) val js = sj[SampleJBigInteger].toJson(inst) js should matchJson("""{"bi1":0,"bi2":1,"bi3":10,"bi4":-90182736451928374653345,"bi5":90182736451928374653345,"bi6":0,"bi7":null}""") // inst shouldEqual ScalaJack.read[SampleJBigInteger](js) @@ -63,12 +55,12 @@ class JavaPrim() extends AnyFunSpec with JsonMatchers: it("Byte must work") { val inst = SampleJByte( - JByte.MAX_VALUE, - JByte.MIN_VALUE, - 0.asInstanceOf[Byte], - 64.asInstanceOf[Byte], - null - ) + JByte.MAX_VALUE, + JByte.MIN_VALUE, + 0.asInstanceOf[Byte], + 64.asInstanceOf[Byte], + null + ) val js = sj[SampleJByte].toJson(inst) js should matchJson("""{"b1":127,"b2":-128,"b3":0,"b4":64,"b5":null}""") // inst shouldEqual ScalaJack.read[SampleJByte](js) @@ -83,12 +75,12 @@ class JavaPrim() extends AnyFunSpec with JsonMatchers: it("Double must work") { val inst = SampleJDouble( - JDouble.MAX_VALUE, - JDouble.MIN_VALUE, - 0.0, - -123.4567, - null - ) + JDouble.MAX_VALUE, + JDouble.MIN_VALUE, + 0.0, + -123.4567, + null + ) val js = sj[SampleJDouble].toJson(inst) js should matchJson("""{"d1":1.7976931348623157E308,"d2":4.9E-324,"d3":0.0,"d4":-123.4567,"d5":null}""") // inst shouldEqual ScalaJack.read[SampleJDouble](js) @@ -96,12 +88,12 @@ class JavaPrim() extends AnyFunSpec with JsonMatchers: it("Float must work") { val inst = SampleJFloat( - JFloat.MAX_VALUE, - JFloat.MIN_VALUE, - 0.0F, - -123.4567F, - null - ) + JFloat.MAX_VALUE, + JFloat.MIN_VALUE, + 0.0f, + -123.4567f, + null + ) val js = sj[SampleJFloat].toJson(inst) js should matchJson("""{"f1":3.4028235E38,"f2":1.4E-45,"f3":0.0,"f4":-123.4567,"f5":null}""") // inst shouldEqual ScalaJack.read[SampleJFloat](js) @@ -123,37 +115,39 @@ class JavaPrim() extends AnyFunSpec with JsonMatchers: it("Number must work") { val inst = SampleJNumber( - JByte.valueOf("-128"), - JByte.valueOf("127"), - JShort.valueOf("-32768"), - JShort.valueOf("32767"), - JInt.valueOf("-2147483648"), - JInt.valueOf("2147483647"), - JLong.valueOf("-9223372036854775808"), - JLong.valueOf("9223372036854755807"), - null, //new JBigInteger("9923372036854755810"), - JByte.valueOf("0"), - JFloat.valueOf("3.4e-038"), - JFloat.valueOf("3.4e+038"), - JDouble.valueOf("1.7e-308"), - JDouble.valueOf("1.7e+308"), - null, //new JBigDecimal("1.8e+308"), - JFloat.valueOf("0.0"), - null - ) + JByte.valueOf("-128"), + JByte.valueOf("127"), + JShort.valueOf("-32768"), + JShort.valueOf("32767"), + JInt.valueOf("-2147483648"), + JInt.valueOf("2147483647"), + JLong.valueOf("-9223372036854775808"), + JLong.valueOf("9223372036854755807"), + null, // new JBigInteger("9923372036854755810"), + JByte.valueOf("0"), + JFloat.valueOf("3.4e-038"), + JFloat.valueOf("3.4e+038"), + JDouble.valueOf("1.7e-308"), + JDouble.valueOf("1.7e+308"), + null, // new JBigDecimal("1.8e+308"), + JFloat.valueOf("0.0"), + null + ) val js = sj[SampleJNumber].toJson(inst) - js should matchJson("""{"n1":-128,"n2":127,"n3":-32768,"n4":32767,"n5":-2147483648,"n6":2147483647,"n7":-9223372036854775808,"n8":9223372036854755807,"n9":null,"n10":0,"n11":3.4E-38,"n12":3.4E38,"n13":1.7E-308,"n14":1.7E308,"n15":null,"n16":0.0,"n17":null}""") + js should matchJson( + """{"n1":-128,"n2":127,"n3":-32768,"n4":32767,"n5":-2147483648,"n6":2147483647,"n7":-9223372036854775808,"n8":9223372036854755807,"n9":null,"n10":0,"n11":3.4E-38,"n12":3.4E38,"n13":1.7E-308,"n14":1.7E308,"n15":null,"n16":0.0,"n17":null}""" + ) // inst shouldEqual ScalaJack.read[SampleJNumber](js) } it("Short must work") { val inst = SampleJShort( - JShort.MAX_VALUE, - JShort.MIN_VALUE, - 0.asInstanceOf[Short], - 123.asInstanceOf[Short], - null - ) + JShort.MAX_VALUE, + JShort.MIN_VALUE, + 0.asInstanceOf[Short], + 123.asInstanceOf[Short], + null + ) val js = sj[SampleJShort].toJson(inst) js should matchJson("""{"s1":32767,"s2":-32768,"s3":0,"s4":123,"s5":null}""") // inst shouldEqual ScalaJack.read[SampleJShort](js) @@ -228,7 +222,7 @@ class JavaPrim() extends AnyFunSpec with JsonMatchers: sj.read[SampleJChar](js2) } } - + test("Double must break") { val js = """{"d1":1.7976931348623157E308,"d2":4.9E-324,"d3":"0.0","d4":-123.4567,"d5":null}""".asInstanceOf[JSON] @@ -267,7 +261,7 @@ class JavaPrim() extends AnyFunSpec with JsonMatchers: sj.read[SampleJInt](js2) } } - + test("Long must break") { val js = """{"l1":9223372036854775807,"l2":-9223372036854775808,"l3":"0","l4":123,"l5":null}""".asInstanceOf[JSON] @@ -309,4 +303,4 @@ class JavaPrim() extends AnyFunSpec with JsonMatchers: sj.read[SampleJShort](js2) } } -*/ \ No newline at end of file + */ diff --git a/src/test/scala/co.blocke.scalajack/json/primitives/ScalaPrim.scala b/src/test/scala/co.blocke.scalajack/json/primitives/ScalaPrimSpec.scala similarity index 99% rename from src/test/scala/co.blocke.scalajack/json/primitives/ScalaPrim.scala rename to src/test/scala/co.blocke.scalajack/json/primitives/ScalaPrimSpec.scala index e3cdc112..fa3e29c8 100644 --- a/src/test/scala/co.blocke.scalajack/json/primitives/ScalaPrim.scala +++ b/src/test/scala/co.blocke.scalajack/json/primitives/ScalaPrimSpec.scala @@ -11,7 +11,7 @@ import org.scalatest.funspec.AnyFunSpec import org.scalatest.matchers.should.Matchers.* import org.scalatest.* -class ScalaPrim() extends AnyFunSpec with JsonMatchers: +class ScalaPrimSpec() extends AnyFunSpec with JsonMatchers: describe(colorString("---------------------------\n: Scala Primitive Tests :\n---------------------------", Console.YELLOW)) { describe(colorString("+++ Positive Tests +++")) { diff --git a/src/test/scala/co.blocke.scalajack/json/primitives/Simple.scala b/src/test/scala/co.blocke.scalajack/json/primitives/SimpleSpec.scala similarity index 89% rename from src/test/scala/co.blocke.scalajack/json/primitives/Simple.scala rename to src/test/scala/co.blocke.scalajack/json/primitives/SimpleSpec.scala index a46e9939..1e6b9ca8 100644 --- a/src/test/scala/co.blocke.scalajack/json/primitives/Simple.scala +++ b/src/test/scala/co.blocke.scalajack/json/primitives/SimpleSpec.scala @@ -8,10 +8,10 @@ import org.scalatest.funspec.AnyFunSpec import org.scalatest.matchers.should.Matchers.* import org.scalatest.* import TestUtil.* -import java.time._ +import java.time.* import java.util.UUID -class Simple() extends AnyFunSpec with JsonMatchers: +class SimpleSpec() extends AnyFunSpec with JsonMatchers: describe(colorString("-----------------------\n: Simple Type Tests :\n-----------------------", Console.YELLOW)) { describe(colorString("+++ Positive Tests +++")) { @@ -24,12 +24,12 @@ class Simple() extends AnyFunSpec with JsonMatchers: it("Instant must work") { val inst = SampleInstant( - Instant.EPOCH, - Instant.MAX, - Instant.MIN, - Instant.parse("2007-12-03T10:15:30.00Z"), - null - ) + Instant.EPOCH, + Instant.MAX, + Instant.MIN, + Instant.parse("2007-12-03T10:15:30.00Z"), + null + ) val js = sj[SampleInstant].toJson(inst) js should matchJson("""{"i1":"1970-01-01T00:00:00Z","i2":"+1000000000-12-31T23:59:59.999999999Z","i3":"-1000000000-01-01T00:00:00Z","i4":"2007-12-03T10:15:30Z","i5":null}""") // inst shouldEqual ScalaJack.read[SampleInstant](js) @@ -37,11 +37,11 @@ class Simple() extends AnyFunSpec with JsonMatchers: it("LocalDate must work") { val inst = SampleLocalDate( - LocalDate.MAX, - LocalDate.MIN, - LocalDate.parse("2007-12-03"), - null - ) + LocalDate.MAX, + LocalDate.MIN, + LocalDate.parse("2007-12-03"), + null + ) val js = sj[SampleLocalDate].toJson(inst) js should matchJson("""{"d1":"+999999999-12-31","d2":"-999999999-01-01","d3":"2007-12-03","d4":null}""") // inst shouldEqual ScalaJack.read[SampleLocalDate](js) @@ -49,11 +49,11 @@ class Simple() extends AnyFunSpec with JsonMatchers: it("LocalDateTime must work") { val inst = SampleLocalDateTime( - LocalDateTime.MAX, - LocalDateTime.MIN, - LocalDateTime.parse("2007-12-03T10:15:30"), - null - ) + LocalDateTime.MAX, + LocalDateTime.MIN, + LocalDateTime.parse("2007-12-03T10:15:30"), + null + ) val js = sj[SampleLocalDateTime].toJson(inst) js should matchJson("""{"d1":"+999999999-12-31T23:59:59.999999999","d2":"-999999999-01-01T00:00:00","d3":"2007-12-03T10:15:30","d4":null}""") // inst shouldEqual ScalaJack.read[SampleLocalDateTime](js) @@ -61,13 +61,13 @@ class Simple() extends AnyFunSpec with JsonMatchers: it("LocalTime must work") { val inst = SampleLocalTime( - LocalTime.MAX, - LocalTime.MIN, - LocalTime.MIDNIGHT, - LocalTime.NOON, - LocalTime.parse("10:15:30"), - null - ) + LocalTime.MAX, + LocalTime.MIN, + LocalTime.MIDNIGHT, + LocalTime.NOON, + LocalTime.parse("10:15:30"), + null + ) val js = sj[SampleLocalTime].toJson(inst) js should matchJson("""{"d1":"23:59:59.999999999","d2":"00:00:00","d3":"00:00:00","d4":"12:00:00","d5":"10:15:30","d6":null}""") // inst shouldEqual ScalaJack.read[SampleLocalTime](js) @@ -75,9 +75,9 @@ class Simple() extends AnyFunSpec with JsonMatchers: it("MonthDay must work") { val inst = SampleMonthDay( - MonthDay.of(7,1), - null - ) + MonthDay.of(7, 1), + null + ) val js = sj[SampleMonthDay].toJson(inst) js should matchJson("""{"m1":"--07-01","m2":null}""") // inst shouldEqual ScalaJack.read[SampleMonthDay](js) @@ -85,11 +85,11 @@ class Simple() extends AnyFunSpec with JsonMatchers: it("OffsetDateTime must work") { val inst = SampleOffsetDateTime( - OffsetDateTime.MAX, - OffsetDateTime.MIN, - OffsetDateTime.parse("2007-12-03T10:15:30+01:00"), - null - ) + OffsetDateTime.MAX, + OffsetDateTime.MIN, + OffsetDateTime.parse("2007-12-03T10:15:30+01:00"), + null + ) val js = sj[SampleOffsetDateTime].toJson(inst) js should matchJson("""{"o1":"+999999999-12-31T23:59:59.999999999-18:00","o2":"-999999999-01-01T00:00:00+18:00","o3":"2007-12-03T10:15:30+01:00","o4":null}""") // inst shouldEqual ScalaJack.read[SampleOffsetDateTime](js) @@ -97,11 +97,11 @@ class Simple() extends AnyFunSpec with JsonMatchers: it("OffsetTime must work") { val inst = SampleOffsetTime( - OffsetTime.MAX, - OffsetTime.MIN, - OffsetTime.parse("10:15:30+01:00"), - null - ) + OffsetTime.MAX, + OffsetTime.MIN, + OffsetTime.parse("10:15:30+01:00"), + null + ) val js = sj[SampleOffsetTime].toJson(inst) js should matchJson("""{"o1":"23:59:59.999999999-18:00","o2":"00:00:00+18:00","o3":"10:15:30+01:00","o4":null}""") // inst shouldEqual ScalaJack.read[SampleOffsetTime](js) @@ -122,7 +122,7 @@ class Simple() extends AnyFunSpec with JsonMatchers: } it("YearMonth must work") { - val inst = SampleYearMonth(YearMonth.of(2020,7), null) + val inst = SampleYearMonth(YearMonth.of(2020, 7), null) val js = sj[SampleYearMonth].toJson(inst) js should matchJson("""{"y1":"2020-07","y2":null}""") // inst shouldEqual ScalaJack.read[SampleYearMonth](js) @@ -130,9 +130,9 @@ class Simple() extends AnyFunSpec with JsonMatchers: it("ZonedDateTime must work") { val inst = SampleZonedDateTime( - ZonedDateTime.parse("2007-12-03T10:15:30+01:00[Europe/Paris]"), - null - ) + ZonedDateTime.parse("2007-12-03T10:15:30+01:00[Europe/Paris]"), + null + ) val js = sj[SampleZonedDateTime].toJson(inst) js should matchJson("""{"o1":"2007-12-03T10:15:30+01:00[Europe/Paris]","o2":null}""") // inst shouldEqual ScalaJack.read[SampleZonedDateTime](js) @@ -140,9 +140,9 @@ class Simple() extends AnyFunSpec with JsonMatchers: it("ZonedId must work") { val inst = SampleZoneId( - ZoneId.of("America/Puerto_Rico"), - null - ) + ZoneId.of("America/Puerto_Rico"), + null + ) val js = sj[SampleZoneId].toJson(inst) js should matchJson("""{"z1":"America/Puerto_Rico","z2":null}""") // inst shouldEqual ScalaJack.read[SampleZoneId](js) @@ -153,9 +153,9 @@ class Simple() extends AnyFunSpec with JsonMatchers: val zone = ZoneId.of("Europe/Berlin") val zoneOffSet = zone.getRules().getOffset(ldt) val inst = SampleZoneOffset( - null, - zoneOffSet - ) + null, + zoneOffSet + ) val js = sj[SampleZoneOffset].toJson(inst) js should matchJson("""{"z1":null,"z2":"+01:00"}""") // inst shouldEqual ScalaJack.read[SampleZoneOffset](js) @@ -163,9 +163,9 @@ class Simple() extends AnyFunSpec with JsonMatchers: it("UUID must work") { val inst = SampleUUID( - null, - UUID.fromString("580afe0d-81c0-458f-9e09-4486c7af0fe9") - ) + null, + UUID.fromString("580afe0d-81c0-458f-9e09-4486c7af0fe9") + ) val js = sj[SampleUUID].toJson(inst) js should matchJson("""{"u1":null,"u2":"580afe0d-81c0-458f-9e09-4486c7af0fe9"}""") // inst shouldEqual ScalaJack.read[SampleUUID](js) @@ -185,14 +185,14 @@ class Simple() extends AnyFunSpec with JsonMatchers: |------------------^""".stripMargin interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ sj.read[SampleDuration](js) - } + } val js2 = """{"d1":"PT0S","d2":"bogus","d3":null}""".asInstanceOf[JSON] val msg2 = """Failed to parse Duration from input 'bogus' |{"d1":"PT0S","d2":"bogus","d3":null} |------------------------^""".stripMargin interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ sj.read[SampleDuration](js2) - } + } } test("Instant must break") { @@ -204,7 +204,7 @@ class Simple() extends AnyFunSpec with JsonMatchers: |----------------------------------^""".stripMargin interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ sj.read[SampleInstant](js) - } + } val js2 = """{"i1":"1970-01-01T00:00:00Z","i2":"bogus","i3":"-1000000000-01-01T00:00:00Z","i4":"2007-12-03T10:15:30Z","i5":null}""".asInstanceOf[JSON] val msg2 = @@ -213,7 +213,7 @@ class Simple() extends AnyFunSpec with JsonMatchers: |----------------------------------------^""".stripMargin interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ sj.read[SampleInstant](js2) - } + } } test("LocalDateTime must break") { @@ -354,4 +354,4 @@ class Simple() extends AnyFunSpec with JsonMatchers: sj.read[SampleZonedDateTime](js2) } } -*/ \ No newline at end of file + */ From 7f1ce409306b0a62dbaf9ffa2b6aece392090c5f Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Wed, 6 Dec 2023 19:41:34 -0600 Subject: [PATCH 35/65] more tests and fixes --- TODO.txt | 21 +-- build.sbt | 2 +- .../co.blocke.scalajack/json/JsonConfig.scala | 43 +++--- .../json/writing/JsonCodecMaker.scala | 76 ++++------ .../scalajack/json/collections/CarEnum.java | 7 + .../json/classes/Model.scala | 38 +++++ .../json/collections/JavaCollSpec.scala | 50 +++---- .../json/collections/JavaMapSpec.scala | 136 +++++++++--------- .../json/collections/MapSpec.scala | 101 +++++++++---- .../json/collections/Model.scala | 21 ++- .../json/collections/SeqSetArraySpec.scala | 74 +++++----- .../json/collections/TupleSpec.scala | 14 +- .../json/misc/AliasSpec.scala | 44 ++++++ .../json/misc/MiscTests.scala | 34 +++++ .../co.blocke.scalajack/json/misc/Model.scala | 35 +++-- .../json/misc/OptionLRTrySpec.scala | 130 ++++++++++------- 16 files changed, 503 insertions(+), 323 deletions(-) create mode 100644 src/test/java/co/blocke/scalajack/json/collections/CarEnum.java create mode 100644 src/test/scala/co.blocke.scalajack/json/classes/Model.scala create mode 100644 src/test/scala/co.blocke.scalajack/json/misc/AliasSpec.scala create mode 100644 src/test/scala/co.blocke.scalajack/json/misc/MiscTests.scala diff --git a/TODO.txt b/TODO.txt index 3fc2d442..dd84a406 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,13 +1,6 @@ Path to Performance ------------------- -[ ] - Fix string value in JsonOutput to handle escaping " and anything else--maybe use apache library - (Make it a config option (for speed) to handle escaped string values or not--in the case where you know your strings are pure) - -[ ] - Add enum as a map key to MapSpec - -[ ] - Implement special Tuple handling for Either and write Seq/Tuple Either tests - Fature Support: Code Test [*] [W] - Primitive types @@ -22,19 +15,19 @@ Path to Performance [*] [ ] - Scala case class [*] [ ] - Scala non-case class [*] [ ] - Java class + [*] [ ] - SelfRef [*] [ ] - Sealed Trait [*] [ ] - Sealed Abstract Class (handle like sealed trait....) [*] [W] - Option/Optional - [*] [ ] - Either + [*] [W] - Either [*] [W] - Intersection [*] [W] - Union - [*] [ ] - Enum - [*] [ ] - Enumeration - [*] [ ] - Java Enum - [*] [ ] - Alias type - [*] [ ] - Object - [*] [ ] - SelfRef + [*] [W] - Enum (tested as part of Map tests) + [*] [W] - Enumeration (tested as part of Map tests) + [*] [W] - Java Enum (tested as part of Map tests) + [*] [W] - Alias type + [*] [X] - Object (recommend do not use except for case objects for sealed traits/classes) [*] [W] - TryRef [*] [X] - Unknown (throw exception) [*] [X] - Scala 2 (throw exception) diff --git a/build.sbt b/build.sbt index 4353302f..b4dd583d 100644 --- a/build.sbt +++ b/build.sbt @@ -36,7 +36,7 @@ lazy val root = project scalafmtOnCompile := !isCI, libraryDependencies ++= Seq( "co.blocke" %% "scala-reflection" % "sj_fixes_f43af7", - "org.apache.commons" % "commons-text" % "1.10.0", + "org.apache.commons" % "commons-text" % "1.11.0", "io.github.kitlangton" %% "neotype" % "0.0.9", "org.scalatest" %% "scalatest" % "3.2.17" % Test, "org.json4s" %% "json4s-core" % "4.0.6" % Test, diff --git a/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala b/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala index c93fbf2b..22943f1c 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala @@ -18,7 +18,8 @@ class JsonConfig private[scalajack] ( val typeHintLabel: String, val typeHintPolicy: TypeHintPolicy, // -------------------------- - val enumsAsIds: Option[List[String]] // None=no enums as ids, Some(Nil)=all enums as ids, Some(List(...))=specified classes enums as ids + val enumsAsIds: Option[List[String]], // None=no enums as ids, Some(Nil)=all enums as ids, Some(List(...))=specified classes enums as ids + val escapeStrings: Boolean ): def withNoneAsNull(nan: Boolean): JsonConfig = copy(noneAsNull = nan) def withTryFailureHandling(tryPolicy: TryPolicy): JsonConfig = copy(tryFailureHandling = tryPolicy) @@ -27,6 +28,7 @@ class JsonConfig private[scalajack] ( def withTypeHintLabel(label: String): JsonConfig = copy(typeHintLabel = label) def withTypeHintPolicy(hintPolicy: TypeHintPolicy): JsonConfig = copy(typeHintPolicy = hintPolicy) def withEnumsAsIds(asIds: Option[List[String]]): JsonConfig = copy(enumsAsIds = asIds) + def withEscapeStrings(escStr: Boolean): JsonConfig = copy(escapeStrings = escStr) private[this] def copy( noneAsNull: Boolean = noneAsNull, @@ -35,7 +37,8 @@ class JsonConfig private[scalajack] ( writeNonConstructorFields: Boolean = writeNonConstructorFields, typeHintLabel: String = typeHintLabel, typeHintPolicy: TypeHintPolicy = typeHintPolicy, - enumsAsIds: Option[List[String]] = enumsAsIds + enumsAsIds: Option[List[String]] = enumsAsIds, + escapeStrings: Boolean = escapeStrings ): JsonConfig = new JsonConfig( noneAsNull, tryFailureHandling, @@ -43,7 +46,8 @@ class JsonConfig private[scalajack] ( writeNonConstructorFields, typeHintLabel, typeHintPolicy, - enumsAsIds + enumsAsIds, + escapeStrings ) enum TryPolicy: @@ -52,8 +56,8 @@ enum TryPolicy: enum EitherLeftPolicy: case AS_VALUE, AS_NULL, NO_WRITE, ERR_MSG_STRING, THROW_EXCEPTION -enum UndefinedValueOption: - case AS_NULL, AS_SYMBOL, THROW_EXCEPTION +// enum UndefinedValueOption: +// case AS_NULL, AS_SYMBOL, THROW_EXCEPTION enum TypeHintPolicy: case SIMPLE_CLASSNAME, SCRAMBLE_CLASSNAME, USE_ANNOTATION @@ -66,7 +70,8 @@ object JsonConfig writeNonConstructorFields = true, typeHintLabel = "_hint", typeHintPolicy = TypeHintPolicy.SIMPLE_CLASSNAME, - enumsAsIds = None + enumsAsIds = None, + escapeStrings = true ): import scala.quoted.FromExpr.* @@ -91,7 +96,8 @@ object JsonConfig $writeNonConstructorFieldsE, $typeHintLabelE, $typeHintPolicyE, - $enumsAsIdsE + $enumsAsIdsE, + $escapeStringsE ) } => try @@ -107,7 +113,8 @@ object JsonConfig // extract2[String]("typeHintLabel", x) extract("typeHintLabel", typeHintLabelE), extract("typeHintPolicy", typeHintPolicyE), - extract("enumsAsIds", enumsAsIdsE) + extract("enumsAsIds", enumsAsIdsE), + extract("escapeStrings", escapeStringsE) ) ) catch { @@ -123,9 +130,7 @@ object JsonConfig case '{ ($x: JsonConfig).withTypeHintLabel($v) } => Some(x.valueOrAbort.withTypeHintLabel(v.valueOrAbort)) case '{ ($x: JsonConfig).withTypeHintPolicy($v) } => Some(x.valueOrAbort.withTypeHintPolicy(v.valueOrAbort)) case '{ ($x: JsonConfig).withEnumsAsIds($v) } => Some(x.valueOrAbort.withEnumsAsIds(v.valueOrAbort)) - case z => - println("Z: " + z.show) - None + case '{ ($x: JsonConfig).withEscapeStrings($v) } => Some(x.valueOrAbort.withEscapeStrings(v.valueOrAbort)) } private[scalajack] given FromExpr[TryPolicy] with { @@ -149,14 +154,14 @@ object JsonConfig case '{ EitherLeftPolicy.THROW_EXCEPTION } => Some(EitherLeftPolicy.THROW_EXCEPTION) } - private[scalajack] given FromExpr[UndefinedValueOption] with { - def unapply(x: Expr[UndefinedValueOption])(using Quotes): Option[UndefinedValueOption] = - import quotes.reflect.* - x match - case '{ UndefinedValueOption.AS_NULL } => Some(UndefinedValueOption.AS_NULL) - case '{ UndefinedValueOption.AS_SYMBOL } => Some(UndefinedValueOption.AS_SYMBOL) - case '{ UndefinedValueOption.THROW_EXCEPTION } => Some(UndefinedValueOption.THROW_EXCEPTION) - } + // private[scalajack] given FromExpr[UndefinedValueOption] with { + // def unapply(x: Expr[UndefinedValueOption])(using Quotes): Option[UndefinedValueOption] = + // import quotes.reflect.* + // x match + // case '{ UndefinedValueOption.AS_NULL } => Some(UndefinedValueOption.AS_NULL) + // case '{ UndefinedValueOption.AS_SYMBOL } => Some(UndefinedValueOption.AS_SYMBOL) + // case '{ UndefinedValueOption.THROW_EXCEPTION } => Some(UndefinedValueOption.THROW_EXCEPTION) + // } private[scalajack] given FromExpr[TypeHintPolicy] with { def unapply(x: Expr[TypeHintPolicy])(using Quotes): Option[TypeHintPolicy] = diff --git a/src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala b/src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala index 4880a6a4..f3d5e0f6 100644 --- a/src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala +++ b/src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala @@ -9,6 +9,8 @@ import co.blocke.scala_reflection.rtypes.{EnumRType, JavaClassRType, NonConstruc import scala.jdk.CollectionConverters.* import scala.quoted.* import dotty.tools.dotc.ast.Trees.EmptyTree +import org.apache.commons.text.StringEscapeUtils +import org.apache.commons.lang3.text.translate.CharSequenceTranslator object JsonCodecMaker: @@ -141,7 +143,9 @@ object JsonCodecMaker: '{ if $tin == null then $out.burpNull() $tin match - case Left(_) => $out.burpNull() + case Left(_) => + $prefix + $out.burpNull() case Right(v) => ${ _maybeWrite[rt](prefix, '{ v.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out, cfg) } } case EitherLeftPolicy.ERR_MSG_STRING => @@ -199,7 +203,7 @@ object JsonCodecMaker: // --------------------------------------------------------------------------------------------- - def genFnBody[T](r: RTypeRef[?], aE: Expr[T], out: Expr[JsonOutput], emitDiscriminator: Boolean = false)(using Quotes): Expr[Unit] = + def genFnBody[T](r: RTypeRef[?], aE: Expr[T], out: Expr[JsonOutput], emitDiscriminator: Boolean = false, inTuple: Boolean = false)(using Quotes): Expr[Unit] = r.refType match case '[b] => r match @@ -460,7 +464,7 @@ object JsonCodecMaker: $tin match case None => ${ - if cfg.noneAsNull then '{ $out.burpNull() } + if cfg.noneAsNull || inTuple then '{ $out.burpNull() } else '{ () } } case Some(v) => @@ -499,9 +503,9 @@ object JsonCodecMaker: else $tin match case Left(v) => - ${ genWriteVal[lt]('{ v.asInstanceOf[lt] }, t.leftRef.asInstanceOf[RTypeRef[lt]], out) } + ${ genWriteVal[lt]('{ v.asInstanceOf[lt] }, t.leftRef.asInstanceOf[RTypeRef[lt]], out, inTuple = inTuple) } case Right(v) => - ${ genWriteVal[rt]('{ v.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out) } + ${ genWriteVal[rt]('{ v.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out, inTuple = inTuple) } } case EitherLeftPolicy.AS_NULL => '{ @@ -510,7 +514,7 @@ object JsonCodecMaker: $tin match case Left(v) => $out.burpNull() case Right(v) => - ${ genWriteVal[rt]('{ v.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out) } + ${ genWriteVal[rt]('{ v.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out, inTuple = inTuple) } } case EitherLeftPolicy.NO_WRITE => '{ @@ -519,7 +523,7 @@ object JsonCodecMaker: $tin match case Left(v) => () case Right(v) => - ${ genWriteVal[rt]('{ v.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out) } + ${ genWriteVal[rt]('{ v.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out, inTuple = inTuple) } } case EitherLeftPolicy.ERR_MSG_STRING => '{ @@ -528,7 +532,7 @@ object JsonCodecMaker: $tin match case Left(v) => $out.value("Left Error: " + v.toString) case Right(v) => - ${ genWriteVal[rt]('{ v.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out) } + ${ genWriteVal[rt]('{ v.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out, inTuple = inTuple) } } case EitherLeftPolicy.THROW_EXCEPTION => '{ @@ -537,18 +541,18 @@ object JsonCodecMaker: $tin match case Left(v) => throw new JsonEitherLeftError("Left Error: " + v.toString) case Right(v) => - ${ genWriteVal[rt]('{ v.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out) } + ${ genWriteVal[rt]('{ v.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out, inTuple = inTuple) } } else '{ $out.mark() scala.util.Try { - ${ genWriteVal[rt]('{ $tin.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out) } + ${ genWriteVal[rt]('{ $tin.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out, inTuple = inTuple) } } match - case scala.util.Success(_) => () + case scala.util.Success(_) => () // do nothing further--write to out already happened case scala.util.Failure(_) => $out.revert() - ${ genWriteVal[lt]('{ $tin.asInstanceOf[lt] }, t.leftRef.asInstanceOf[RTypeRef[lt]], out) } + ${ genWriteVal[lt]('{ $tin.asInstanceOf[lt] }, t.leftRef.asInstanceOf[RTypeRef[lt]], out, inTuple = inTuple) } } // No makeFn here. Try is just a wrapper @@ -561,10 +565,11 @@ object JsonCodecMaker: else $tin match case scala.util.Success(v) => - ${ genWriteVal[e]('{ v }.asInstanceOf[Expr[e]], t.tryRef.asInstanceOf[RTypeRef[e]], out) } + ${ genWriteVal[e]('{ v }.asInstanceOf[Expr[e]], t.tryRef.asInstanceOf[RTypeRef[e]], out, inTuple = inTuple) } case scala.util.Failure(v) => ${ cfg.tryFailureHandling match + case _ if inTuple => '{ $out.burpNull() } case TryPolicy.AS_NULL => '{ $out.burpNull() } case TryPolicy.NO_WRITE => '{ () } case TryPolicy.ERR_MSG_STRING => '{ $out.value("Try Failure with msg: " + v.getMessage()) } @@ -585,37 +590,7 @@ object JsonCodecMaker: ref.refType match case '[e] => val fieldValue = Select.unique(in.asTerm, "_" + (i + 1)).asExprOf[e] - // Special handling if field type is Option and value is None. - ref match - case _: OptionRef[?] => - '{ - if $fieldValue == None then $out.burpNull() - else ${ genWriteVal[e](fieldValue, ref.asInstanceOf[RTypeRef[e]], out) } - } - case _: TryRef[?] => - '{ - $fieldValue match - case scala.util.Failure(_) => $out.burpNull() - case scala.util.Success(None) => $out.burpNull() - case _ => ${ genWriteVal[e](fieldValue, ref.asInstanceOf[RTypeRef[e]], out) } - } - case r: LeftRightRef[?] if r.lrkind != LRKind.EITHER => - if r.leftRef.isInstanceOf[TryRef[_]] || r.rightRef.isInstanceOf[TryRef[_]] then - '{ - $fieldValue match - case scala.util.Failure(_) => $out.burpNull() - case scala.util.Success(None) => $out.burpNull() - case _ => ${ genWriteVal[e](fieldValue, ref.asInstanceOf[RTypeRef[e]], out) } - } - else if r.leftRef.isInstanceOf[OptionRef[_]] || r.rightRef.isInstanceOf[OptionRef[_]] then - '{ - $fieldValue match - case None => $out.burpNull() - case _ => ${ genWriteVal[e](fieldValue, ref.asInstanceOf[RTypeRef[e]], out) } - } - else genWriteVal[e](fieldValue, ref.asInstanceOf[RTypeRef[e]], out) - case _ => - genWriteVal[e](fieldValue, ref.asInstanceOf[RTypeRef[e]], out) + genWriteVal[e](fieldValue, ref.asInstanceOf[RTypeRef[e]], out, inTuple = true) } if elementsE.size == 1 then elementsE.head else Expr.block(elementsE.init, elementsE.last) @@ -634,7 +609,8 @@ object JsonCodecMaker: // optWriteDiscriminator: Option[WriteDiscriminator], out: Expr[JsonOutput], // cfgE: Expr[JsonConfig], - isStringified: Boolean = false // e.g. Map key values. Doesn't apply to stringish values, which are always quotes-wrapped + isStringified: Boolean = false, // e.g. Map key values. Doesn't apply to stringish values, which are always quotes-wrapped + inTuple: Boolean = false )(using Quotes): Expr[Unit] = val methodKey = MethodKey(ref, false) methodSyms @@ -673,7 +649,9 @@ object JsonCodecMaker: case t: ShortRef => if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Short] }) } else '{ $out.value(${ aE.asExprOf[Short] }) } - case t: StringRef => '{ $out.value(${ aE.asExprOf[String] }) } + case t: StringRef => + if cfg.escapeStrings then '{ $out.value(StringEscapeUtils.escapeJson(${ aE.asExprOf[String] })) } + else '{ $out.value(${ aE.asExprOf[String] }) } case t: JBigDecimalRef => if isStringified then '{ $out.valueStringified(${ aE.asExprOf[java.math.BigDecimal] }) } @@ -730,7 +708,7 @@ object JsonCodecMaker: case t: AliasRef[?] => t.unwrappedType.refType match case '[e] => - genWriteVal[e](aE.asInstanceOf[Expr[e]], t.unwrappedType.asInstanceOf[RTypeRef[e]], out) + genWriteVal[e](aE.asInstanceOf[Expr[e]], t.unwrappedType.asInstanceOf[RTypeRef[e]], out, inTuple = inTuple) // These one's here becaue Enums and their various flavors can be Map keys // (EnumRef handles: Scala 3 enum, Scala 2 Enumeration, Java Enumeration) @@ -742,13 +720,13 @@ object JsonCodecMaker: case _ => false val rtype = t.expr if enumAsId then - if isStringified then '{ $out.label($rtype.asInstanceOf[EnumRType[_]].ordinal($aE.toString).get.toString) } + if isStringified then '{ $out.value($rtype.asInstanceOf[EnumRType[_]].ordinal($aE.toString).get.toString) } else '{ $out.value($rtype.asInstanceOf[EnumRType[_]].ordinal($aE.toString).get) } else '{ $out.value($aE.toString) } // Everything else... case _ if isStringified => throw new JsonIllegalKeyType("Non-primitive/non-simple types cannot be map keys") - case _ => genFnBody(ref, aE, out) + case _ => genFnBody(ref, aE, out, inTuple = inTuple) ) // ================================================================ diff --git a/src/test/java/co/blocke/scalajack/json/collections/CarEnum.java b/src/test/java/co/blocke/scalajack/json/collections/CarEnum.java new file mode 100644 index 00000000..a9eb34be --- /dev/null +++ b/src/test/java/co/blocke/scalajack/json/collections/CarEnum.java @@ -0,0 +1,7 @@ +package co.blocke.scalajack.json.collections; + +enum CarEnum{ + TOYOTA, + VW, + PORSCHE +} \ No newline at end of file diff --git a/src/test/scala/co.blocke.scalajack/json/classes/Model.scala b/src/test/scala/co.blocke.scalajack/json/classes/Model.scala new file mode 100644 index 00000000..bae2594b --- /dev/null +++ b/src/test/scala/co.blocke.scalajack/json/classes/Model.scala @@ -0,0 +1,38 @@ +package co.blocke.scalajack +package json +package classes + +case class Person(name: String, age: Int) + +class Parent(phase:Int): + private var _hidden: Boolean = false + def hidden: Boolean = _hidden + def hidden_=(h: Boolean) = _hidden = h + +case class Child(name: String, age: Int, phase: Int) extends Parent(phase) + +case class Params[X,Y](a: List[X], b: Option[Y]) + +// Simple case class serialization + +// Test inheritance with annotations + +// Test non-constructor fields of plain classes (Parent) + +// Parameterized classes + +// Sealed trait w/case classes +// Sealed trait w/non-case classes +// Sealed trait w/case objects + +// Sealed abstract class w/case classes +// Sealed abstract class w/non-case classes +// Sealed abstract class w/case objects + +// Java class + +// Self-referencing class +// Self-referencing class where self-ref has a parameter Thing[T](a:Int, b: Option[Thing[T]]) + +// [*] [ ] - type hint label mapping +// [*] [ ] - type hint value mapping \ No newline at end of file diff --git a/src/test/scala/co.blocke.scalajack/json/collections/JavaCollSpec.scala b/src/test/scala/co.blocke.scalajack/json/collections/JavaCollSpec.scala index 8d8d31db..5097f27a 100644 --- a/src/test/scala/co.blocke.scalajack/json/collections/JavaCollSpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/collections/JavaCollSpec.scala @@ -21,47 +21,47 @@ class JavaCollSpec() extends AnyFunSpec with JsonMatchers: js should matchJson("""{"a":null}""") } it("Set of numeric must work") { - val inst = JSetHolder[Int](HashSet(Arrays.asList(1,2,3))) + val inst = JSetHolder[Int](HashSet(Arrays.asList(1, 2, 3))) val js = sj[JSetHolder[Int]].toJson(inst) js should matchJson("""{"a":[1,2,3]}""") } it("Set of string must work") { - val inst = JSetHolder[String](HashSet(Arrays.asList("a","b","c"))) + val inst = JSetHolder[String](HashSet(Arrays.asList("a", "b", "c"))) val js = sj[JSetHolder[String]].toJson(inst) js should matchJson("""{"a":["a","b","c"]}""") } it("Set of boolean must work") { - val inst = JSetHolder[Boolean](HashSet(Arrays.asList(true,false,true))) + val inst = JSetHolder[Boolean](HashSet(Arrays.asList(true, false, true))) val js = sj[JSetHolder[Boolean]].toJson(inst) js should matchJson("""{"a":[false,true]}""") } it("Set of Set (nested) must work") { - val inst = JSetHolder[List[Int]](HashSet(Arrays.asList(List(1,2),List(3,4)))) + val inst = JSetHolder[List[Int]](HashSet(Arrays.asList(List(1, 2), List(3, 4)))) val js = sj[JSetHolder[List[Int]]].toJson(inst) js should matchJson("""{"a":[[3,4],[1,2]]}""") } it("Set of either must work") { - val inst = JSetHolder[Either[Int,Boolean]](HashSet(Arrays.asList(Right(true),Left(15),Right(false)))) - val js = sj[JSetHolder[Either[Int,Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)).toJson(inst) + val inst = JSetHolder[Either[Int, Boolean]](HashSet(Arrays.asList(Right(true), Left(15), Right(false)))) + val js = sj[JSetHolder[Either[Int, Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)).toJson(inst) js should matchJson("""{"a":[true,15,false]}""") } it("Set of union must work") { - val inst = JSetHolder[Int|Boolean](HashSet(Arrays.asList(true,15,false))) - val js = sj[JSetHolder[Int|Boolean]].toJson(inst) + val inst = JSetHolder[Int | Boolean](HashSet(Arrays.asList(true, 15, false))) + val js = sj[JSetHolder[Int | Boolean]].toJson(inst) js should matchJson("""{"a":[false,true,15]}""") } it("Set of option must work") { - val inst = JSetHolder[Option[Int]](HashSet(Arrays.asList(Some(1),None,Some(3)))) + val inst = JSetHolder[Option[Int]](HashSet(Arrays.asList(Some(1), None, Some(3)))) val js = sj[JSetHolder[Option[Int]]].toJson(inst) js should matchJson("""{"a":[1,3]}""") } it("Set of map must work") { - val inst = JSetHolder[Map[String,Int]](HashSet(Arrays.asList(Map("a"->1,"b"->2),Map("c"->3,"d"->4)))) - val js = sj[JSetHolder[Map[String,Int]]].toJson(inst) + val inst = JSetHolder[Map[String, Int]](HashSet(Arrays.asList(Map("a" -> 1, "b" -> 2), Map("c" -> 3, "d" -> 4)))) + val js = sj[JSetHolder[Map[String, Int]]].toJson(inst) js should matchJson("""{"a":[{"a":1,"b":2},{"c":3,"d":4}]}""") } it("Set of class must work") { - val inst = JSetHolder[Person](HashSet(Arrays.asList(Person("Bob",35),Person("Sally",54)))) + val inst = JSetHolder[Person](HashSet(Arrays.asList(Person("Bob", 35), Person("Sally", 54)))) val js = sj[JSetHolder[Person]].toJson(inst) js should matchJson("""{"a":[{"name":"Bob","age",35},{"name":"Sally","age",54}]}""") } @@ -72,49 +72,49 @@ class JavaCollSpec() extends AnyFunSpec with JsonMatchers: js should matchJson("""{"a":null}""") } it("ArrayList of numeric must work") { - val inst = ArrayListHolder[Int](ArrayList[Int](Arrays.asList(1,2,3))) + val inst = ArrayListHolder[Int](ArrayList[Int](Arrays.asList(1, 2, 3))) val js = sj[ArrayListHolder[Int]].toJson(inst) js should matchJson("""{"a":[1,2,3]}""") } it("ArrayList of string must work") { - val inst = ArrayListHolder[String](ArrayList[String](Arrays.asList("a","b","c"))) + val inst = ArrayListHolder[String](ArrayList[String](Arrays.asList("a", "b", "c"))) val js = sj[ArrayListHolder[String]].toJson(inst) js should matchJson("""{"a":["a","b","c"]}""") } it("ArrayList of boolean must work") { - val inst = ArrayListHolder[Boolean](ArrayList[Boolean](Arrays.asList(true,false,true))) + val inst = ArrayListHolder[Boolean](ArrayList[Boolean](Arrays.asList(true, false, true))) val js = sj[ArrayListHolder[Boolean]].toJson(inst) js should matchJson("""{"a":[true,false,true]}""") } it("ArrayList of ArrayList (nested) must work") { - val inst = ArrayListHolder[ArrayList[Int]](ArrayList[ArrayList[Int]](Arrays.asList(ArrayList(Arrays.asList(1,2)),ArrayList(Arrays.asList(3,4))))) + val inst = ArrayListHolder[ArrayList[Int]](ArrayList[ArrayList[Int]](Arrays.asList(ArrayList(Arrays.asList(1, 2)), ArrayList(Arrays.asList(3, 4))))) val js = sj[ArrayListHolder[ArrayList[Int]]].toJson(inst) js should matchJson("""{"a":[[1,2],[3,4]]}""") } it("ArrayList of either must work") { - val inst = ArrayListHolder[Either[Int,Boolean]](ArrayList[Either[Int,Boolean]](Arrays.asList(Right(true),Left(15),Right(false)))) - val js = sj[ArrayListHolder[Either[Int,Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)).toJson(inst) + val inst = ArrayListHolder[Either[Int, Boolean]](ArrayList[Either[Int, Boolean]](Arrays.asList(Right(true), Left(15), Right(false)))) + val js = sj[ArrayListHolder[Either[Int, Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)).toJson(inst) js should matchJson("""{"a":[true,15,false]}""") } it("ArrayList of union must work") { - val inst = ArrayListHolder[Int|Boolean](ArrayList[Int|Boolean](Arrays.asList(true,15,false))) - val js = sj[ArrayListHolder[Int|Boolean]].toJson(inst) + val inst = ArrayListHolder[Int | Boolean](ArrayList[Int | Boolean](Arrays.asList(true, 15, false))) + val js = sj[ArrayListHolder[Int | Boolean]].toJson(inst) js should matchJson("""{"a":[true,15,false]}""") } it("ArrayList of option must work") { - val inst = ArrayListHolder[Option[Int]](ArrayList[Option[Int]](Arrays.asList(Some(1),None,Some(3)))) + val inst = ArrayListHolder[Option[Int]](ArrayList[Option[Int]](Arrays.asList(Some(1), None, Some(3)))) val js = sj[ArrayListHolder[Option[Int]]].toJson(inst) js should matchJson("""{"a":[1,3]}""") } it("ArrayList of map must work") { - val inst = ArrayListHolder[Map[String,Int]](ArrayList[Map[String,Int]](Arrays.asList(Map("a"->1,"b"->2),Map("c"->3,"d"->4)))) - val js = sj[ArrayListHolder[Map[String,Int]]].toJson(inst) + val inst = ArrayListHolder[Map[String, Int]](ArrayList[Map[String, Int]](Arrays.asList(Map("a" -> 1, "b" -> 2), Map("c" -> 3, "d" -> 4)))) + val js = sj[ArrayListHolder[Map[String, Int]]].toJson(inst) js should matchJson("""{"a":[{"a":1,"b":2},{"c":3,"d":4}]}""") } it("ArrayList of class must work") { - val inst = ArrayListHolder[Person](ArrayList[Person](Arrays.asList(Person("Bob",35),Person("Sally",54)))) + val inst = ArrayListHolder[Person](ArrayList[Person](Arrays.asList(Person("Bob", 35), Person("Sally", 54)))) val js = sj[ArrayListHolder[Person]].toJson(inst) js should matchJson("""{"a":[{"name":"Bob","age",35},{"name":"Sally","age",54}]}""") } } - } \ No newline at end of file + } diff --git a/src/test/scala/co.blocke.scalajack/json/collections/JavaMapSpec.scala b/src/test/scala/co.blocke.scalajack/json/collections/JavaMapSpec.scala index ecbc316e..cbf0a57d 100644 --- a/src/test/scala/co.blocke.scalajack/json/collections/JavaMapSpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/collections/JavaMapSpec.scala @@ -16,114 +16,114 @@ class JavaMapSpec() extends AnyFunSpec with JsonMatchers: describe(colorString("-------------------------------\n: Java Map Tests :\n-------------------------------", Console.YELLOW)) { describe(colorString("+++ Positive Tests +++")) { it("Map is null must work") { - val inst = JMapHolder[Int,Int](null) - val js = sj[JMapHolder[Int,Int]].toJson(inst) + val inst = JMapHolder[Int, Int](null) + val js = sj[JMapHolder[Int, Int]].toJson(inst) js should matchJson("""{"a":null}""") } it("Map key of string must work") { - val m: java.util.Map[String,Int] = new java.util.HashMap[String,Int]() - m.put("x",1) - m.put("y",2) - val inst = JMapHolder[String,Int](new java.util.HashMap(m)) - val js = sj[JMapHolder[String,Int]].toJson(inst) + val m: java.util.Map[String, Int] = new java.util.HashMap[String, Int]() + m.put("x", 1) + m.put("y", 2) + val inst = JMapHolder[String, Int](new java.util.HashMap(m)) + val js = sj[JMapHolder[String, Int]].toJson(inst) js should matchJson("""{"a":{"x":1,"y":2}}""") } it("Map key of long must work") { - val m: java.util.Map[Long,Int] = new java.util.HashMap[Long,Int]() - m.put(15L,1) - m.put(25L,2) - val inst = JMapHolder[Long,Int](new java.util.HashMap(m)) - val js = sj[JMapHolder[Long,Int]].toJson(inst) + val m: java.util.Map[Long, Int] = new java.util.HashMap[Long, Int]() + m.put(15L, 1) + m.put(25L, 2) + val inst = JMapHolder[Long, Int](new java.util.HashMap(m)) + val js = sj[JMapHolder[Long, Int]].toJson(inst) js should matchJson("""{"a":{"15":1,"25":2}}""") } it("Map key of boolean must work") { - val m: java.util.Map[Boolean,Int] = new java.util.HashMap[Boolean,Int]() - m.put(true,1) - m.put(false,2) - val inst = JMapHolder[Boolean,Int](new java.util.HashMap(m)) - val js = sj[JMapHolder[Boolean,Int]].toJson(inst) + val m: java.util.Map[Boolean, Int] = new java.util.HashMap[Boolean, Int]() + m.put(true, 1) + m.put(false, 2) + val inst = JMapHolder[Boolean, Int](new java.util.HashMap(m)) + val js = sj[JMapHolder[Boolean, Int]].toJson(inst) js should matchJson("""{"a":{"true":1,"false":2}}""") } it("Map key of uuid must work") { - val m: java.util.Map[UUID,String] = new java.util.HashMap[UUID,String]() - m.put(UUID.fromString("1b9ab03f-26a3-4ec5-a8dd-d5122ff86b03"),"x") - m.put(UUID.fromString("09abdeb1-8b07-4683-8f97-1f5621696008"),"y") - val inst = JMapHolder[UUID,String](new java.util.HashMap(m)) - val js = sj[JMapHolder[UUID,String]].toJson(inst) + val m: java.util.Map[UUID, String] = new java.util.HashMap[UUID, String]() + m.put(UUID.fromString("1b9ab03f-26a3-4ec5-a8dd-d5122ff86b03"), "x") + m.put(UUID.fromString("09abdeb1-8b07-4683-8f97-1f5621696008"), "y") + val inst = JMapHolder[UUID, String](new java.util.HashMap(m)) + val js = sj[JMapHolder[UUID, String]].toJson(inst) js should matchJson("""{"a":{"1b9ab03f-26a3-4ec5-a8dd-d5122ff86b03":"x","09abdeb1-8b07-4683-8f97-1f5621696008":"y"}}""") } it("Map value of string must work") { - val m: java.util.Map[String,String] = new java.util.HashMap[String,String]() - m.put("w","x") - m.put("y","z") - val inst = JMapHolder[String,String](new java.util.HashMap(m)) - val js = sj[JMapHolder[String,String]].toJson(inst) + val m: java.util.Map[String, String] = new java.util.HashMap[String, String]() + m.put("w", "x") + m.put("y", "z") + val inst = JMapHolder[String, String](new java.util.HashMap(m)) + val js = sj[JMapHolder[String, String]].toJson(inst) js should matchJson("""{"a":{"w":"x","y":"z"}}""") } it("Map value of long must work") { - val m: java.util.Map[String,Long] = new java.util.HashMap[String,Long]() - m.put("w",3L) - m.put("y",4L) - val inst = JMapHolder[String,Long](new java.util.HashMap(m)) - val js = sj[JMapHolder[String,Long]].toJson(inst) + val m: java.util.Map[String, Long] = new java.util.HashMap[String, Long]() + m.put("w", 3L) + m.put("y", 4L) + val inst = JMapHolder[String, Long](new java.util.HashMap(m)) + val js = sj[JMapHolder[String, Long]].toJson(inst) js should matchJson("""{"a":{"w":3,"y":4}}""") } it("Map value of boolean must work") { - val m: java.util.Map[String,Boolean] = new java.util.HashMap[String,Boolean]() - m.put("w",true) - m.put("y",false) - val inst = JMapHolder[String,Boolean](new java.util.HashMap(m)) - val js = sj[JMapHolder[String,Boolean]].toJson(inst) + val m: java.util.Map[String, Boolean] = new java.util.HashMap[String, Boolean]() + m.put("w", true) + m.put("y", false) + val inst = JMapHolder[String, Boolean](new java.util.HashMap(m)) + val js = sj[JMapHolder[String, Boolean]].toJson(inst) js should matchJson("""{"a":{"w":true,"y":false}}""") } it("Map value of uuid must work") { - val m: java.util.Map[String,UUID] = new java.util.HashMap[String,UUID]() - m.put("x",UUID.fromString("1b9ab03f-26a3-4ec5-a8dd-d5122ff86b03")) - m.put("y",UUID.fromString("09abdeb1-8b07-4683-8f97-1f5621696008")) - val inst = JMapHolder[String,UUID](new java.util.HashMap(m)) - val js = sj[JMapHolder[String,UUID]].toJson(inst) + val m: java.util.Map[String, UUID] = new java.util.HashMap[String, UUID]() + m.put("x", UUID.fromString("1b9ab03f-26a3-4ec5-a8dd-d5122ff86b03")) + m.put("y", UUID.fromString("09abdeb1-8b07-4683-8f97-1f5621696008")) + val inst = JMapHolder[String, UUID](new java.util.HashMap(m)) + val js = sj[JMapHolder[String, UUID]].toJson(inst) js should matchJson("""{"a":{"x":"1b9ab03f-26a3-4ec5-a8dd-d5122ff86b03","y":"09abdeb1-8b07-4683-8f97-1f5621696008"}}""") } it("Map value of Seq must work") { - val m: java.util.Map[String,List[Int]] = new java.util.HashMap[String,List[Int]]() - m.put("w",List(1,2)) - m.put("y",List(3,4)) - val inst = JMapHolder[String,List[Int]](new java.util.HashMap(m)) - val js = sj[JMapHolder[String,List[Int]]].toJson(inst) + val m: java.util.Map[String, List[Int]] = new java.util.HashMap[String, List[Int]]() + m.put("w", List(1, 2)) + m.put("y", List(3, 4)) + val inst = JMapHolder[String, List[Int]](new java.util.HashMap(m)) + val js = sj[JMapHolder[String, List[Int]]].toJson(inst) js should matchJson("""{"a":{"w":[1,2],"y":[3,4]}}""") } it("Map value of Map (nested) must work") { - val m: java.util.Map[String,Map[String,Int]] = new java.util.HashMap[String,Map[String,Int]]() - m.put("w",Map("r"->3,"t"->4)) - m.put("y",Map("s"->7,"q"->9)) - val inst = JMapHolder[String,Map[String,Int]](new java.util.HashMap(m)) - val js = sj[JMapHolder[String,Map[String,Int]]].toJson(inst) + val m: java.util.Map[String, Map[String, Int]] = new java.util.HashMap[String, Map[String, Int]]() + m.put("w", Map("r" -> 3, "t" -> 4)) + m.put("y", Map("s" -> 7, "q" -> 9)) + val inst = JMapHolder[String, Map[String, Int]](new java.util.HashMap(m)) + val js = sj[JMapHolder[String, Map[String, Int]]].toJson(inst) js should matchJson("""{"a":{"w":{"r":3,"t":4},"y":{"s":7,"q":9}}}""") } it("Map value of class must work") { - val m: java.util.Map[String,Person] = new java.util.HashMap[String,Person]() - m.put("w",Person("Bob",34)) - m.put("y",Person("Sally",25)) - val inst = JMapHolder[String,Person](new java.util.HashMap(m)) - val js = sj[JMapHolder[String,Person]].toJson(inst) + val m: java.util.Map[String, Person] = new java.util.HashMap[String, Person]() + m.put("w", Person("Bob", 34)) + m.put("y", Person("Sally", 25)) + val inst = JMapHolder[String, Person](new java.util.HashMap(m)) + val js = sj[JMapHolder[String, Person]].toJson(inst) js should matchJson("""{"a":{"w":{"name":"Bob","age":34},"y":{"name":"Sally","age":25}}}""") } it("Map value of union type must work") { - val m: java.util.Map[String,Int|List[String]] = new java.util.HashMap[String,Int|List[String]]() - m.put("w",3) - m.put("y",List("wow","blah")) - val inst = JMapHolder[String,Int|List[String]](new java.util.HashMap(m)) - val js = sj[JMapHolder[String,Int|List[String]]].toJson(inst) + val m: java.util.Map[String, Int | List[String]] = new java.util.HashMap[String, Int | List[String]]() + m.put("w", 3) + m.put("y", List("wow", "blah")) + val inst = JMapHolder[String, Int | List[String]](new java.util.HashMap(m)) + val js = sj[JMapHolder[String, Int | List[String]]].toJson(inst) js should matchJson("""{"a":{"w":3,"y":["wow","blah"]}}""") } it("Map value of value class must work") { - val m: java.util.Map[String,Distance] = new java.util.HashMap[String,Distance]() - m.put("w",new Distance(1.23)) - m.put("y",Distance(4.56)) - val inst = JMapHolder[String,Distance](new java.util.HashMap(m)) - val js = sj[JMapHolder[String,Distance]].toJson(inst) + val m: java.util.Map[String, Distance] = new java.util.HashMap[String, Distance]() + m.put("w", new Distance(1.23)) + m.put("y", Distance(4.56)) + val inst = JMapHolder[String, Distance](new java.util.HashMap(m)) + val js = sj[JMapHolder[String, Distance]].toJson(inst) js should matchJson("""{"a":{"w":1.23,"y":4.56}}""") } } - } \ No newline at end of file + } diff --git a/src/test/scala/co.blocke.scalajack/json/collections/MapSpec.scala b/src/test/scala/co.blocke.scalajack/json/collections/MapSpec.scala index 55ff9277..0119ae8a 100644 --- a/src/test/scala/co.blocke.scalajack/json/collections/MapSpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/collections/MapSpec.scala @@ -16,80 +16,119 @@ class MapSpec() extends AnyFunSpec with JsonMatchers: describe(colorString("-------------------------------\n: Map Tests :\n-------------------------------", Console.YELLOW)) { describe(colorString("+++ Positive Tests +++")) { it("Map is null must work") { - val inst = MapHolder[Int,Int](null) - val js = sj[MapHolder[Int,Int]].toJson(inst) + val inst = MapHolder[Int, Int](null) + val js = sj[MapHolder[Int, Int]].toJson(inst) js should matchJson("""{"a":null}""") } it("Map key of string must work") { - val inst = MapHolder[String,Int](Map("x"->1,"y"->2)) - val js = sj[MapHolder[String,Int]].toJson(inst) + val inst = MapHolder[String, Int](Map("x" -> 1, "y" -> 2)) + val js = sj[MapHolder[String, Int]].toJson(inst) js should matchJson("""{"a":{"x":1,"y":2}}""") } it("Map key of long must work") { - val inst = MapHolder[Long,Int](Map(15L->1,25L->2)) - val js = sj[MapHolder[Long,Int]].toJson(inst) + val inst = MapHolder[Long, Int](Map(15L -> 1, 25L -> 2)) + val js = sj[MapHolder[Long, Int]].toJson(inst) js should matchJson("""{"a":{"15":1,"25":2}}""") } it("Map key of boolean must work") { - val inst = MapHolder[Boolean,Int](Map(true->1,false->2)) - val js = sj[MapHolder[Boolean,Int]].toJson(inst) + val inst = MapHolder[Boolean, Int](Map(true -> 1, false -> 2)) + val js = sj[MapHolder[Boolean, Int]].toJson(inst) js should matchJson("""{"a":{"true":1,"false":2}}""") } it("Map key of uuid must work") { - val inst = MapHolder[UUID,String](Map(UUID.fromString("1b9ab03f-26a3-4ec5-a8dd-d5122ff86b03")->"x",UUID.fromString("09abdeb1-8b07-4683-8f97-1f5621696008")->"y")) - val js = sj[MapHolder[UUID,String]].toJson(inst) + val inst = MapHolder[UUID, String](Map(UUID.fromString("1b9ab03f-26a3-4ec5-a8dd-d5122ff86b03") -> "x", UUID.fromString("09abdeb1-8b07-4683-8f97-1f5621696008") -> "y")) + val js = sj[MapHolder[UUID, String]].toJson(inst) js should matchJson("""{"a":{"1b9ab03f-26a3-4ec5-a8dd-d5122ff86b03":"x","09abdeb1-8b07-4683-8f97-1f5621696008":"y"}}""") } it("Map value of string must work") { - val inst = MapHolder[String,String](Map("w"->"x","y"->"z")) - val js = sj[MapHolder[String,String]].toJson(inst) + val inst = MapHolder[String, String](Map("w" -> "x", "y" -> "z")) + val js = sj[MapHolder[String, String]].toJson(inst) js should matchJson("""{"a":{"w":"x","y":"z"}}""") } it("Map value of long must work") { - val inst = MapHolder[String,Long](Map("w"->3L,"y"->4L)) - val js = sj[MapHolder[String,Long]].toJson(inst) + val inst = MapHolder[String, Long](Map("w" -> 3L, "y" -> 4L)) + val js = sj[MapHolder[String, Long]].toJson(inst) js should matchJson("""{"a":{"w":3,"y":4}}""") } it("Map value of boolean must work") { - val inst = MapHolder[String,Boolean](Map("w"->true,"y"->false)) - val js = sj[MapHolder[String,Boolean]].toJson(inst) + val inst = MapHolder[String, Boolean](Map("w" -> true, "y" -> false)) + val js = sj[MapHolder[String, Boolean]].toJson(inst) js should matchJson("""{"a":{"w":true,"y":false}}""") } it("Map value of uuid must work") { - val inst = MapHolder[String,UUID](Map("x"->UUID.fromString("1b9ab03f-26a3-4ec5-a8dd-d5122ff86b03"),"y"->UUID.fromString("09abdeb1-8b07-4683-8f97-1f5621696008"))) - val js = sj[MapHolder[String,UUID]].toJson(inst) + val inst = MapHolder[String, UUID](Map("x" -> UUID.fromString("1b9ab03f-26a3-4ec5-a8dd-d5122ff86b03"), "y" -> UUID.fromString("09abdeb1-8b07-4683-8f97-1f5621696008"))) + val js = sj[MapHolder[String, UUID]].toJson(inst) js should matchJson("""{"a":{"x":"1b9ab03f-26a3-4ec5-a8dd-d5122ff86b03","y":"09abdeb1-8b07-4683-8f97-1f5621696008"}}""") } it("Map value of Seq must work") { - val inst = MapHolder[String,List[Int]](Map("w"->List(1,2),"y"->List(3,4))) - val js = sj[MapHolder[String,List[Int]]].toJson(inst) + val inst = MapHolder[String, List[Int]](Map("w" -> List(1, 2), "y" -> List(3, 4))) + val js = sj[MapHolder[String, List[Int]]].toJson(inst) js should matchJson("""{"a":{"w":[1,2],"y":[3,4]}}""") } it("Map value of Map (nested) must work") { - val inst = MapHolder[String,Map[String,Int]](Map("w"->Map("r"->3,"t"->4),"y"->Map("s"->7,"q"->9))) - val js = sj[MapHolder[String,Map[String,Int]]].toJson(inst) + val inst = MapHolder[String, Map[String, Int]](Map("w" -> Map("r" -> 3, "t" -> 4), "y" -> Map("s" -> 7, "q" -> 9))) + val js = sj[MapHolder[String, Map[String, Int]]].toJson(inst) js should matchJson("""{"a":{"w":{"r":3,"t":4},"y":{"s":7,"q":9}}}""") } it("Map value of class must work") { - val inst = MapHolder[String,Person](Map("w"->Person("Bob",34),"y"->Person("Sally",25))) - val js = sj[MapHolder[String,Person]].toJson(inst) + val inst = MapHolder[String, Person](Map("w" -> Person("Bob", 34), "y" -> Person("Sally", 25))) + val js = sj[MapHolder[String, Person]].toJson(inst) js should matchJson("""{"a":{"w":{"name":"Bob","age":34},"y":{"name":"Sally","age":25}}}""") } it("Map value of union type must work") { - val inst = MapHolder[String,Int|List[String]](Map("w"->3,"y"->List("wow","blah"))) - val js = sj[MapHolder[String,Int|List[String]]].toJson(inst) + val inst = MapHolder[String, Int | List[String]](Map("w" -> 3, "y" -> List("wow", "blah"))) + val js = sj[MapHolder[String, Int | List[String]]].toJson(inst) js should matchJson("""{"a":{"w":3,"y":["wow","blah"]}}""") } it("Map value of value class must work") { - val inst = MapHolder[String,Distance](Map("w"->new Distance(1.23),"y"->Distance(4.56))) - val js = sj[MapHolder[String,Distance]].toJson(inst) + val inst = MapHolder[String, Distance](Map("w" -> new Distance(1.23), "y" -> Distance(4.56))) + val js = sj[MapHolder[String, Distance]].toJson(inst) js should matchJson("""{"a":{"w":1.23,"y":4.56}}""") } it("Mutable Map value must work") { - val inst = MMapHolder[String,Distance](scala.collection.mutable.HashMap("w"->new Distance(1.23),"y"->Distance(4.56))) - val js = sj[MMapHolder[String,Distance]].toJson(inst) + val inst = MMapHolder[String, Distance](scala.collection.mutable.HashMap("w" -> new Distance(1.23), "y" -> Distance(4.56))) + val js = sj[MMapHolder[String, Distance]].toJson(inst) js should matchJson("""{"a":{"w":1.23,"y":4.56}}""") } + + it("Enum as Map key and value must work") { + val inst = MapHolder[Color, Color](Map(Color.Red -> Color.Blue, Color.Green -> Color.Red)) + val js = sj[MapHolder[Color, Color]].toJson(inst) + js should matchJson("""{"a":{"Red":"Blue","Green":"Red"}}""") + } + it("Enum as Map key and value must work (using id)") { + val inst = MapHolder[Color, Color](Map(Color.Red -> Color.Blue, Color.Green -> Color.Red)) + val js = sj[MapHolder[Color, Color]](JsonConfig.withEnumsAsIds(Some(Nil))).toJson(inst) + js should matchJson("""{"a":{"0":2,"1":0}}""") + } + it("Enumeration as Map key and value must work") { + import Permissions.* + val inst = MapHolder[Permissions, Permissions](Map(Permissions.READ -> Permissions.WRITE, Permissions.EXEC -> Permissions.NONE)) + val js = sj[MapHolder[Permissions, Permissions]].toJson(inst) + js should matchJson("""{"a":{"READ":"WRITE","EXEC":"NONE"}}""") + } + it("Enumeration as Map key and value must work (using id)") { + import Permissions.* + val inst = MapHolder[Permissions, Permissions](Map(Permissions.READ -> Permissions.WRITE, Permissions.EXEC -> Permissions.NONE)) + val js = sj[MapHolder[Permissions, Permissions]](JsonConfig.withEnumsAsIds(Some(Nil))).toJson(inst) + js should matchJson("""{"a":{"0":1,"2":3}}""") + } + it("Java Enumeration as Map key and value must work") { + val inst = MapHolder[CarEnum, CarEnum](Map(CarEnum.VW -> CarEnum.PORSCHE, CarEnum.PORSCHE -> CarEnum.TOYOTA)) + val js = sj[MapHolder[CarEnum, CarEnum]].toJson(inst) + js should matchJson("""{"a":{"VW":"PORSCHE","PORSCHE":"TOYOTA"}}""") + } + it("Java Enumeration as Map key and value must work (using id)") { + val inst = MapHolder[CarEnum, CarEnum](Map(CarEnum.VW -> CarEnum.PORSCHE, CarEnum.PORSCHE -> CarEnum.TOYOTA)) + val js = sj[MapHolder[CarEnum, CarEnum]](JsonConfig.withEnumsAsIds(Some(Nil))).toJson(inst) + js should matchJson("""{"a":{"1":2,"2":0}}""") + } + it("Enum/Enumeration mix of enum as value must work") { + import Permissions.* + val inst = MapHolder[Color, Permissions](Map(Color.Red -> Permissions.WRITE, Color.Blue -> Permissions.NONE)) + val js = sj[MapHolder[Color, Permissions]](JsonConfig.withEnumsAsIds(Some(List("co.blocke.scalajack.json.collections.Color")))).toJson(inst) + js should matchJson("""{"a":{"0":"WRITE","2":"NONE"}}""") + } } - } \ No newline at end of file + } diff --git a/src/test/scala/co.blocke.scalajack/json/collections/Model.scala b/src/test/scala/co.blocke.scalajack/json/collections/Model.scala index c8729899..ed1d450e 100644 --- a/src/test/scala/co.blocke.scalajack/json/collections/Model.scala +++ b/src/test/scala/co.blocke.scalajack/json/collections/Model.scala @@ -2,7 +2,7 @@ package co.blocke.scalajack package json package collections -import java.util.{ArrayList, Set=>JSet, Map=>JMap} +import java.util.{ArrayList, Map as JMap, Set as JSet} case class Person(name: String, age: Int) @@ -10,13 +10,22 @@ case class SeqHolder[T](a: Seq[T]) case class SetHolder[T](a: Set[T]) case class ArrayHolder[T](a: Array[T]) -case class MapHolder[T,V](a: Map[T,V]) -case class MMapHolder[T,V](a: scala.collection.mutable.Map[T,V]) -case class JMapHolder[T,V](a: JMap[T,V]) +case class MapHolder[T, V](a: Map[T, V]) +case class MMapHolder[T, V](a: scala.collection.mutable.Map[T, V]) +case class JMapHolder[T, V](a: JMap[T, V]) class Distance(val meters: Double) extends AnyVal -case class TupleHolder[A,B,C]( a:(A,B,C)) +case class TupleHolder[A, B, C](a: (A, B, C)) case class ArrayListHolder[T](a: ArrayList[T]) -case class JSetHolder[T](a: JSet[T]) \ No newline at end of file +case class JSetHolder[T](a: JSet[T]) + +enum Color: + case Red, Green, Blue + +object Permissions extends Enumeration { + type Permissions = Value + val READ, WRITE, EXEC, NONE = Value +} +import Permissions.* diff --git a/src/test/scala/co.blocke.scalajack/json/collections/SeqSetArraySpec.scala b/src/test/scala/co.blocke.scalajack/json/collections/SeqSetArraySpec.scala index e5568d26..daad32e2 100644 --- a/src/test/scala/co.blocke.scalajack/json/collections/SeqSetArraySpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/collections/SeqSetArraySpec.scala @@ -21,47 +21,47 @@ class SeqSetArraySpec() extends AnyFunSpec with JsonMatchers: js should matchJson("""{"a":null}""") } it("Seq of numeric must work") { - val inst = SeqHolder[Int](List(1,2,3)) + val inst = SeqHolder[Int](List(1, 2, 3)) val js = sj[SeqHolder[Int]].toJson(inst) js should matchJson("""{"a":[1,2,3]}""") } it("Seq of string must work") { - val inst = SeqHolder[String](List("a","b","c")) + val inst = SeqHolder[String](List("a", "b", "c")) val js = sj[SeqHolder[String]].toJson(inst) js should matchJson("""{"a":["a","b","c"]}""") } it("Seq of boolean must work") { - val inst = SeqHolder[Boolean](List(true,false,true)) + val inst = SeqHolder[Boolean](List(true, false, true)) val js = sj[SeqHolder[Boolean]].toJson(inst) js should matchJson("""{"a":[true,false,true]}""") } it("Seq of seq (nested) must work") { - val inst = SeqHolder[List[Int]](List(List(1,2),List(3,4))) + val inst = SeqHolder[List[Int]](List(List(1, 2), List(3, 4))) val js = sj[SeqHolder[List[Int]]].toJson(inst) js should matchJson("""{"a":[[1,2],[3,4]]}""") } it("Seq of either must work") { - val inst = SeqHolder[Either[Int,Boolean]](List(Right(true),Left(15),Right(false))) - val js = sj[SeqHolder[Either[Int,Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)).toJson(inst) + val inst = SeqHolder[Either[Int, Boolean]](List(Right(true), Left(15), Right(false))) + val js = sj[SeqHolder[Either[Int, Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)).toJson(inst) js should matchJson("""{"a":[true,15,false]}""") } it("Seq of union must work") { - val inst = SeqHolder[Int|Boolean](List(true,15,false)) - val js = sj[SeqHolder[Int|Boolean]].toJson(inst) + val inst = SeqHolder[Int | Boolean](List(true, 15, false)) + val js = sj[SeqHolder[Int | Boolean]].toJson(inst) js should matchJson("""{"a":[true,15,false]}""") } it("Seq of option must work") { - val inst = SeqHolder[Option[Int]](List(Some(1),None,Some(3))) + val inst = SeqHolder[Option[Int]](List(Some(1), None, Some(3))) val js = sj[SeqHolder[Option[Int]]].toJson(inst) js should matchJson("""{"a":[1,3]}""") } it("Seq of map must work") { - val inst = SeqHolder[Map[String,Int]](List(Map("a"->1,"b"->2),Map("c"->3,"d"->4))) - val js = sj[SeqHolder[Map[String,Int]]].toJson(inst) + val inst = SeqHolder[Map[String, Int]](List(Map("a" -> 1, "b" -> 2), Map("c" -> 3, "d" -> 4))) + val js = sj[SeqHolder[Map[String, Int]]].toJson(inst) js should matchJson("""{"a":[{"a":1,"b":2},{"c":3,"d":4}]}""") } it("Seq of class must work") { - val inst = SeqHolder[Person](List(Person("Bob",35),Person("Sally",54))) + val inst = SeqHolder[Person](List(Person("Bob", 35), Person("Sally", 54))) val js = sj[SeqHolder[Person]].toJson(inst) js should matchJson("""{"a":[{"name":"Bob","age",35},{"name":"Sally","age",54}]}""") } @@ -72,47 +72,47 @@ class SeqSetArraySpec() extends AnyFunSpec with JsonMatchers: js should matchJson("""{"a":null}""") } it("Set of numeric must work") { - val inst = SetHolder[Int](HashSet(1,2,3)) + val inst = SetHolder[Int](HashSet(1, 2, 3)) val js = sj[SetHolder[Int]].toJson(inst) js should matchJson("""{"a":[1,2,3]}""") } it("Set of string must work") { - val inst = SetHolder[String](HashSet("a","b","c")) + val inst = SetHolder[String](HashSet("a", "b", "c")) val js = sj[SetHolder[String]].toJson(inst) js should matchJson("""{"a":["a","b","c"]}""") } it("Set of boolean must work") { - val inst = SetHolder[Boolean](HashSet(true,false,true)) + val inst = SetHolder[Boolean](HashSet(true, false, true)) val js = sj[SetHolder[Boolean]].toJson(inst) js should matchJson("""{"a":[false,true]}""") } it("Set of Set (nested) must work") { - val inst = SetHolder[HashSet[Int]](HashSet(HashSet(1,2),HashSet(3,4))) + val inst = SetHolder[HashSet[Int]](HashSet(HashSet(1, 2), HashSet(3, 4))) val js = sj[SetHolder[HashSet[Int]]].toJson(inst) js should matchJson("""{"a":[[3,4],[1,2]]}""") } it("Set of either must work") { - val inst = SetHolder[Either[Int,Boolean]](HashSet(Right(true),Left(15),Right(false))) - val js = sj[SetHolder[Either[Int,Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)).toJson(inst) + val inst = SetHolder[Either[Int, Boolean]](HashSet(Right(true), Left(15), Right(false))) + val js = sj[SetHolder[Either[Int, Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)).toJson(inst) js should matchJson("""{"a":[15,true,false]}""") } it("Set of union must work") { - val inst = SetHolder[Int|Boolean](HashSet(true,15,false)) - val js = sj[SetHolder[Int|Boolean]].toJson(inst) + val inst = SetHolder[Int | Boolean](HashSet(true, 15, false)) + val js = sj[SetHolder[Int | Boolean]].toJson(inst) js should matchJson("""{"a":[false,true,15]}""") } it("Set of option must work") { - val inst = SetHolder[Option[Int]](HashSet(Some(1),None,Some(3))) + val inst = SetHolder[Option[Int]](HashSet(Some(1), None, Some(3))) val js = sj[SetHolder[Option[Int]]].toJson(inst) js should matchJson("""{"a":[3,1]}""") } it("Set of map must work") { - val inst = SetHolder[Map[String,Int]](HashSet(Map("a"->1,"b"->2),Map("c"->3,"d"->4))) - val js = sj[SetHolder[Map[String,Int]]].toJson(inst) + val inst = SetHolder[Map[String, Int]](HashSet(Map("a" -> 1, "b" -> 2), Map("c" -> 3, "d" -> 4))) + val js = sj[SetHolder[Map[String, Int]]].toJson(inst) js should matchJson("""{"a":[{"c":3,"d":4},{"a":1,"b":2}]}""") } it("Set of class must work") { - val inst = SetHolder[Person](HashSet(Person("Bob",35),Person("Sally",54))) + val inst = SetHolder[Person](HashSet(Person("Bob", 35), Person("Sally", 54))) val js = sj[SetHolder[Person]].toJson(inst) js should matchJson("""{"a":[{"name":"Bob","age",35},{"name":"Sally","age",54}]}""") } @@ -123,49 +123,49 @@ class SeqSetArraySpec() extends AnyFunSpec with JsonMatchers: js should matchJson("""{"a":null}""") } it("Array of numeric must work") { - val inst = ArrayHolder[Int](Array(1,2,3)) + val inst = ArrayHolder[Int](Array(1, 2, 3)) val js = sj[ArrayHolder[Int]].toJson(inst) js should matchJson("""{"a":[1,2,3]}""") } it("Array of string must work") { - val inst = ArrayHolder[String](Array("a","b","c")) + val inst = ArrayHolder[String](Array("a", "b", "c")) val js = sj[ArrayHolder[String]].toJson(inst) js should matchJson("""{"a":["a","b","c"]}""") } it("Array of boolean must work") { - val inst = ArrayHolder[Boolean](Array(true,false,true)) + val inst = ArrayHolder[Boolean](Array(true, false, true)) val js = sj[ArrayHolder[Boolean]].toJson(inst) js should matchJson("""{"a":[true,false,true]}""") } it("Array of Array (nested) must work") { - val inst = ArrayHolder[Array[Int]](Array(Array(1,2),Array(3,4))) + val inst = ArrayHolder[Array[Int]](Array(Array(1, 2), Array(3, 4))) val js = sj[ArrayHolder[Array[Int]]].toJson(inst) js should matchJson("""{"a":[[1,2],[3,4]]}""") } it("Array of either must work") { - val inst = ArrayHolder[Either[Int,Boolean]](Array(Right(true),Left(15),Right(false))) - val js = sj[ArrayHolder[Either[Int,Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)).toJson(inst) + val inst = ArrayHolder[Either[Int, Boolean]](Array(Right(true), Left(15), Right(false))) + val js = sj[ArrayHolder[Either[Int, Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)).toJson(inst) js should matchJson("""{"a":[true,15,false]}""") } it("Array of union must work") { - val inst = ArrayHolder[Int|Boolean](Array(true,15,false)) - val js = sj[ArrayHolder[Int|Boolean]].toJson(inst) + val inst = ArrayHolder[Int | Boolean](Array(true, 15, false)) + val js = sj[ArrayHolder[Int | Boolean]].toJson(inst) js should matchJson("""{"a":[true,15,false]}""") } it("Array of option must work") { - val inst = ArrayHolder[Option[Int]](Array(Some(1),None,Some(3))) + val inst = ArrayHolder[Option[Int]](Array(Some(1), None, Some(3))) val js = sj[ArrayHolder[Option[Int]]].toJson(inst) js should matchJson("""{"a":[1,3]}""") } it("Array of map must work") { - val inst = ArrayHolder[Map[String,Int]](Array(Map("a"->1,"b"->2),Map("c"->3,"d"->4))) - val js = sj[ArrayHolder[Map[String,Int]]].toJson(inst) + val inst = ArrayHolder[Map[String, Int]](Array(Map("a" -> 1, "b" -> 2), Map("c" -> 3, "d" -> 4))) + val js = sj[ArrayHolder[Map[String, Int]]].toJson(inst) js should matchJson("""{"a":[{"a":1,"b":2},{"c":3,"d":4}]}""") } it("Array of class must work") { - val inst = ArrayHolder[Person](Array(Person("Bob",35),Person("Sally",54))) + val inst = ArrayHolder[Person](Array(Person("Bob", 35), Person("Sally", 54))) val js = sj[ArrayHolder[Person]].toJson(inst) js should matchJson("""{"a":[{"name":"Bob","age",35},{"name":"Sally","age",54}]}""") } } - } \ No newline at end of file + } diff --git a/src/test/scala/co.blocke.scalajack/json/collections/TupleSpec.scala b/src/test/scala/co.blocke.scalajack/json/collections/TupleSpec.scala index 5b4b8dff..ef31ceab 100644 --- a/src/test/scala/co.blocke.scalajack/json/collections/TupleSpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/collections/TupleSpec.scala @@ -16,19 +16,19 @@ class TupleSpec() extends AnyFunSpec with JsonMatchers: describe(colorString("-------------------------------\n: Tuple Tests :\n-------------------------------", Console.YELLOW)) { describe(colorString("+++ Positive Tests +++")) { it("Tuple is null must work") { - val inst = TupleHolder[Int,String,Boolean](null) - val js = sj[TupleHolder[Int,String,Boolean]].toJson(inst) + val inst = TupleHolder[Int, String, Boolean](null) + val js = sj[TupleHolder[Int, String, Boolean]].toJson(inst) js should matchJson("""{"a":null}""") } it("Tuple of simple types must work") { - val inst = TupleHolder[Int,String,Boolean]((15,"wow",true)) - val js = sj[TupleHolder[Int,String,Boolean]].toJson(inst) + val inst = TupleHolder[Int, String, Boolean]((15, "wow", true)) + val js = sj[TupleHolder[Int, String, Boolean]].toJson(inst) js should matchJson("""{"a":[15,"wow",true]}""") } it("Tuple of collecitons (including another tuple) must work") { - val inst = TupleHolder[Seq[Int],Map[String,Long],(Double,Char,Boolean)]((List(1,2),Map("a"->3L,"b"->4L),(1.23D,'X',true))) - val js = sj[TupleHolder[Seq[Int],Map[String,Long],(Double,Char,Boolean)]].toJson(inst) + val inst = TupleHolder[Seq[Int], Map[String, Long], (Double, Char, Boolean)]((List(1, 2), Map("a" -> 3L, "b" -> 4L), (1.23d, 'X', true))) + val js = sj[TupleHolder[Seq[Int], Map[String, Long], (Double, Char, Boolean)]].toJson(inst) js should matchJson("""{"a":[[1,2],{"a":3,"b":4},[1.23,"X",true]]}""") } } - } \ No newline at end of file + } diff --git a/src/test/scala/co.blocke.scalajack/json/misc/AliasSpec.scala b/src/test/scala/co.blocke.scalajack/json/misc/AliasSpec.scala new file mode 100644 index 00000000..dddae13a --- /dev/null +++ b/src/test/scala/co.blocke.scalajack/json/misc/AliasSpec.scala @@ -0,0 +1,44 @@ +package co.blocke.scalajack +package json +package misc + +import ScalaJack.* +import co.blocke.scala_reflection.* +import org.scalatest.funspec.AnyFunSpec +import org.scalatest.matchers.should.Matchers.* +import org.scalatest.* +import scala.util.* +import TestUtil.* + +import java.util.UUID + +class AliasSpec() extends AnyFunSpec with JsonMatchers: + opaque type Count = Int + opaque type CountX = Option[Int] + type CountY = String + type CountZ = Option[String] + + describe(colorString("-------------------------------\n: Alias Tests :\n-------------------------------", Console.YELLOW)) { + describe(colorString("+++ Positive Tests +++")) { + it("Type aliases (opaque types) must be dereferenced") { + val inst = AliasHolder[Count](5, List(1,2,3), Map(1->"wow"), Map("wow"->2)) + val js = sj[AliasHolder[Count]].toJson(inst) + js should matchJson("""{"a":5,"b":[1,2,3],"c":{"1":"wow"},"d":{"wow":2}}""") + } + it("Type aliases (opaque types) must be dereferenced (with Option)") { + val inst = AliasHolder2[CountX](Some(5), List(Some(1),None,Some(3)), Map("wow"->None)) + val js = sj[AliasHolder2[CountX]].toJson(inst) + js should matchJson("""{"a":5,"b":[1,3],"c":{}}""") + } + it("Type aliases (non-opaque types) must be dereferenced") { + val inst = AliasHolder[CountY]("q", List("r","s","t"), Map("u"->"wow"), Map("wow"->"v")) + val js = sj[AliasHolder[CountY]].toJson(inst) + js should matchJson("""{"a":"q","b":["r","s","t"],"c":{"u":"wow"},"d":{"wow":"v"}}""") + } + it("Type aliases (non-opaque types) must be dereferenced (with Option)") { + val inst = AliasHolder2[CountZ](Some("q"), List(Some("r"),None,Some("t")), Map("wow"->None)) + val js = sj[AliasHolder2[CountZ]].toJson(inst) + js should matchJson("""{"a":"q","b":["r","t"],"c":{}}""") + } + } + } diff --git a/src/test/scala/co.blocke.scalajack/json/misc/MiscTests.scala b/src/test/scala/co.blocke.scalajack/json/misc/MiscTests.scala new file mode 100644 index 00000000..d5f0f4e1 --- /dev/null +++ b/src/test/scala/co.blocke.scalajack/json/misc/MiscTests.scala @@ -0,0 +1,34 @@ +package co.blocke.scalajack +package json +package misc + +import ScalaJack.* +import co.blocke.scala_reflection.* +import org.scalatest.funspec.AnyFunSpec +import org.scalatest.matchers.should.Matchers.* +import org.scalatest.* +import scala.util.* +import TestUtil.* + +import java.util.UUID + +class MiscSpec() extends AnyFunSpec with JsonMatchers: + opaque type Count = Int + opaque type CountX = Option[Int] + type CountY = String + type CountZ = Option[String] + + describe(colorString("-------------------------------\n: Misc Tests :\n-------------------------------", Console.YELLOW)) { + describe(colorString("+++ Positive Tests +++")) { + it("String escaping must work") { + val inst = StringHolder("""This is a "strange" test\non another level.""") + val js = sj[StringHolder].toJson(inst) + js should matchJson("""{"a":"This is a \"strange\" test\\non another level."}""") + } + it("String without escaping must work (bad JSON, but proves escape can be turned off)") { + val inst = StringHolder("""This is a "strange" test\non another level.""") + val js = sj[StringHolder](JsonConfig.withEscapeStrings(false)).toJson(inst) + js should equal("""{"a":"This is a "strange" test\non another level."}""") + } + } + } diff --git a/src/test/scala/co.blocke.scalajack/json/misc/Model.scala b/src/test/scala/co.blocke.scalajack/json/misc/Model.scala index cb8347ba..78c3e8ca 100644 --- a/src/test/scala/co.blocke.scalajack/json/misc/Model.scala +++ b/src/test/scala/co.blocke.scalajack/json/misc/Model.scala @@ -8,22 +8,27 @@ import scala.util.* case class Person(name: String, age: Int) case class OptionHolder[T]( - a: Option[T], // straight Option - b: (Option[T],String), // tuple w/Option - c: List[Option[T]], // Seq of Option - d: Map[Int, Option[T]], // Map of Option - e: T|Option[T], // Union of Option (R) - f: Option[T]|T, // Union of Option (L) - g: Option[Option[T]], // Nested Option - h: Option[Person], // Option of Class - i: Either[T,Option[T]], // Either of Option (R) - j: Either[Option[T],T] // Either of Option (L) - ) + a: Option[T], // straight Option + b: (Option[T], String), // tuple w/Option + c: List[Option[T]], // Seq of Option + d: Map[Int, Option[T]], // Map of Option + e: T | Option[T], // Union of Option (R) + f: Option[T] | T, // Union of Option (L) + g: Option[Option[T]], // Nested Option + h: Option[Person], // Option of Class + i: Either[T, Option[T]], // Either of Option (R) + j: Either[Option[T], T] // Either of Option (L) +) -case class TryHolder[T]( a: Try[T] ) -case class TryHolder2[T]( a: Seq[Try[T]], b: (Try[T],Try[T]) ) +case class TryHolder[T](a: Try[T]) +case class TryHolder2[T](a: Seq[Try[T]], b: (Try[T], Try[T])) -case class LRHolder[T,U](a: Seq[T|U], b: (T|U,T|U)) +case class LRHolder[T, U](a: Seq[T | U], b: (T | U, T | U)) +case class EitherHolder[T](a: Either[T, String], b: Either[String, T]) -case class ComplexEither[T](a: Option[Either[String,Option[T]]]) +case class ComplexEither[T](a: Option[Either[String, Option[T]]]) +case class AliasHolder[T](a: T, b: List[T], c: Map[T, String], d: Map[String, T]) +case class AliasHolder2[T](a: T, b: List[T], c: Map[String, T]) + +case class StringHolder(a: String) diff --git a/src/test/scala/co.blocke.scalajack/json/misc/OptionLRTrySpec.scala b/src/test/scala/co.blocke.scalajack/json/misc/OptionLRTrySpec.scala index ef4a2778..da009751 100644 --- a/src/test/scala/co.blocke.scalajack/json/misc/OptionLRTrySpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/misc/OptionLRTrySpec.scala @@ -18,52 +18,52 @@ class OptionLRTrySpec() extends AnyFunSpec with JsonMatchers: describe(colorString("+++ Positive Tests +++")) { it("Non-empty Options must work") { val inst = OptionHolder[Int]( - Some(5), // straight Option - (Some(1),"ok"), // tuple w/Option - List(Some(1),None,Some(3)), // Seq of Option - Map(1->Some(2),3->Some(4)), // Map of Option - Some(99), // Union of Option (R) - Some(100), // Union of Option (L) - Some(Some(0)), // Nested Option - Some(Person("BoB",34)), // Option of class - Right(Some(15)), // Either of Option (R) - Left(Some(-3)) // Either of Option (L) - ) + Some(5), // straight Option + (Some(1), "ok"), // tuple w/Option + List(Some(1), None, Some(3)), // Seq of Option + Map(1 -> Some(2), 3 -> Some(4)), // Map of Option + Some(99), // Union of Option (R) + Some(100), // Union of Option (L) + Some(Some(0)), // Nested Option + Some(Person("BoB", 34)), // Option of class + Right(Some(15)), // Either of Option (R) + Left(Some(-3)) // Either of Option (L) + ) val js = sj[OptionHolder[Int]].toJson(inst) js should matchJson("""{"a":5,"b":[1,"ok"],"c":[1,3],"d":{"1":2,"3":4},"e":99,"f":100,"g":0,"h":{"name":"BoB","age":34},"i":15}""") } it("Empty Options must work (default)") { val inst = OptionHolder[Int]( - None, // straight Option - (None,"ok"), // tuple w/Option - List(None,None,None), // Seq of Option - Map(1->None,3->None), // Map of Option - None, // Union of Option (R) - None, // Union of Option (L) - Some(None), // Nested Option - None, // Option of class - Right(None), // Either of Option (R) - Left(None) // Either of Option (L) - ) + None, // straight Option + (None, "ok"), // tuple w/Option + List(None, None, None), // Seq of Option + Map(1 -> None, 3 -> None), // Map of Option + None, // Union of Option (R) + None, // Union of Option (L) + Some(None), // Nested Option + None, // Option of class + Right(None), // Either of Option (R) + Left(None) // Either of Option (L) + ) val js = sj[OptionHolder[Int]].toJson(inst) - js should matchJson("""{"b":[null,"ok"],"c":[],"d":{}}""") + js should matchJson("""{"b":[null,"ok"],"c":[],"d":{}}""") } it("Empty Options must work (config noneAsNull = true)") { val inst = OptionHolder[Int]( - None, // straight Option - (None,"ok"), // tuple w/Option - List(None,None,None), // Seq of Option - Map(1->None,3->None), // Map of Option - None, // Union of Option (R) - None, // Union of Option (L) - Some(None), // Nested Option - None, // Option of class - Right(None), // Either of Option (R) - Left(None) // Either of Option (L) - ) + None, // straight Option + (None, "ok"), // tuple w/Option + List(None, None, None), // Seq of Option + Map(1 -> None, 3 -> None), // Map of Option + None, // Union of Option (R) + None, // Union of Option (L) + Some(None), // Nested Option + None, // Option of class + Right(None), // Either of Option (R) + Left(None) // Either of Option (L) + ) val js = sj[OptionHolder[Int]]( JsonConfig.withNoneAsNull(true).withEitherLeftHandling(EitherLeftPolicy.AS_VALUE) - ).toJson(inst) + ).toJson(inst) js should matchJson("""{"a":null,"b":[null,"ok"],"c":[null,null,null],"d":{"1":null,"3":null},"e":null,"f":null,"g":null,"h":null,"i":null,"j":null}""") } } @@ -86,44 +86,72 @@ class OptionLRTrySpec() extends AnyFunSpec with JsonMatchers: val js = sj[ComplexEither[Int]](JsonConfig.withNoneAsNull(true)).toJson(inst) js should matchJson("""{"a":null}""") } - it("Complex Either/Option must work (Failure-NO_WRITE)") { + it("Complex Either/Option must work (Left-NO_WRITE)") { val inst = ComplexEither[Int](Some(Left("err"))) val js = sj[ComplexEither[Int]].toJson(inst) js should matchJson("""{}""") } - it("Complex Either/Option must work (Failure-AS_VALUE)") { + it("Complex Either/Option must work (Left-AS_VALUE)") { val inst = ComplexEither[Int](Some(Left("err"))) val js = sj[ComplexEither[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)).toJson(inst) js should matchJson("""{"a":"err"}""") } + it("Either with AS_VALUE left policy must work") { + val inst = EitherHolder[Int](Left(5), Right(3)) + val js = sj[EitherHolder[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)).toJson(inst) + js should matchJson("""{"a":5,"b":3}""") + } + it("Either with AS_NULL left policy must work") { + val inst = EitherHolder[Int](Left(5), Right(3)) + val js = sj[EitherHolder[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_NULL)).toJson(inst) + js should matchJson("""{"a":null,"b":3}""") + } + it("Either with NO_WRITE left policy must work") { + val inst = EitherHolder[Int](Left(5), Right(3)) + val js = sj[EitherHolder[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.NO_WRITE)).toJson(inst) + js should matchJson("""{"b":3}""") + } + it("Either with ERR_MSG_STRING left policy must work") { + val inst = EitherHolder[Int](Left(5), Right(3)) + val js = sj[EitherHolder[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.ERR_MSG_STRING)).toJson(inst) + js should matchJson("""{"a":"Left Error: 5","b":3}""") + } + it("Either with THROW_EXCEPTION left policy must work") { + val inst = EitherHolder[Int](Left(5), Right(3)) + val caught = + intercept[JsonEitherLeftError] { + sj[EitherHolder[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.THROW_EXCEPTION)).toJson(inst) + } + assert(caught.getMessage == "Left Error: 5") + } } } describe(colorString("-------------------------------\n: LR Tests :\n-------------------------------", Console.YELLOW)) { describe(colorString("+++ Positive Tests +++")) { it("LR (union) must work with Option (non-None)") { - val inst = LRHolder[Option[Int],String](List(Some(5),"x"), ("y",Some(10))) - val js = sj[LRHolder[Option[Int],String]].toJson(inst) + val inst = LRHolder[Option[Int], String](List(Some(5), "x"), ("y", Some(10))) + val js = sj[LRHolder[Option[Int], String]].toJson(inst) js should matchJson("""{"a":[5,"x"],"b":["y",10]}""") } it("LR (union) must work with Option (None)") { - val inst = LRHolder[Option[Int],String](List(None,"x"), ("y",None)) - val js = sj[LRHolder[Option[Int],String]].toJson(inst) + val inst = LRHolder[Option[Int], String](List(None, "x"), ("y", None)) + val js = sj[LRHolder[Option[Int], String]].toJson(inst) js should matchJson("""{"a":["x"],"b":["y",null]}""") } it("LR (union) must work with Try of Option (non-None)") { - val inst = LRHolder[Try[Option[Int]],String](List(Success(Some(5)),"x"), ("y",Success(Some(10)))) - val js = sj[LRHolder[Try[Option[Int]],String]].toJson(inst) + val inst = LRHolder[Try[Option[Int]], String](List(Success(Some(5)), "x"), ("y", Success(Some(10)))) + val js = sj[LRHolder[Try[Option[Int]], String]].toJson(inst) js should matchJson("""{"a":[5,"x"],"b":["y",10]}""") } it("LR (union) must work with Try of Option (Success(None))") { - val inst = LRHolder[Try[Option[Int]],String](List(Success(None),"x"), ("y",Success(None))) - val js = sj[LRHolder[Try[Option[Int]],String]].toJson(inst) + val inst = LRHolder[Try[Option[Int]], String](List(Success(None), "x"), ("y", Success(None))) + val js = sj[LRHolder[Try[Option[Int]], String]].toJson(inst) js should matchJson("""{"a":["x"],"b":["y",null]}""") } it("LR (union) must work with Try of Option (Failure)") { - val inst = LRHolder[Try[Option[Int]],String](List(Failure(new Exception("boom")),"x"), ("y",Failure(new Exception("boom2")))) - val js = sj[LRHolder[Try[Option[Int]],String]].toJson(inst) + val inst = LRHolder[Try[Option[Int]], String](List(Failure(new Exception("boom")), "x"), ("y", Failure(new Exception("boom2")))) + val js = sj[LRHolder[Try[Option[Int]], String]].toJson(inst) js should matchJson("""{"a":["x"],"b":["y",null]}""") } } @@ -170,19 +198,19 @@ class OptionLRTrySpec() extends AnyFunSpec with JsonMatchers: assert(caught.getMessage == "boom") } it("Seq and Tuple of Try must work for AS_NULL (Failure)") { - val inst = TryHolder2[Int]( List(Success(1),Failure(new Exception("boom")),Success(3)), (Failure(new Exception("boom")), Success(0))) + val inst = TryHolder2[Int](List(Success(1), Failure(new Exception("boom")), Success(3)), (Failure(new Exception("boom")), Success(0))) val js = sj[TryHolder2[Int]](JsonConfig.withTryFailureHandling(TryPolicy.AS_NULL)).toJson(inst) js should matchJson("""{"a":[1,null,3],"b":[null,0]}""") } it("Seq and Tuple of Try must work for NO_WRITE (Failure)") { - val inst = TryHolder2[Int]( List(Success(1),Failure(new Exception("boom")),Success(3)), (Failure(new Exception("boom")), Success(0))) + val inst = TryHolder2[Int](List(Success(1), Failure(new Exception("boom")), Success(3)), (Failure(new Exception("boom")), Success(0))) val js = sj[TryHolder2[Int]](JsonConfig.withTryFailureHandling(TryPolicy.NO_WRITE)).toJson(inst) js should matchJson("""{"a":[1,3],"b":[null,0]}""") } it("Seq and Tuple of Try of an Option must work for NO_WRITE (Failure)") { - val inst = TryHolder2[Option[Int]]( List(Success(None),Failure(new Exception("boom")),Success(Some(3))), (Failure(new Exception("boom")), Success(None)) ) + val inst = TryHolder2[Option[Int]](List(Success(None), Failure(new Exception("boom")), Success(Some(3))), (Failure(new Exception("boom")), Success(None))) val js = sj[TryHolder2[Option[Int]]](JsonConfig.withTryFailureHandling(TryPolicy.NO_WRITE)).toJson(inst) js should matchJson("""{"a":[3],"b":[null,null]}""") } } - } \ No newline at end of file + } From 38eb1bf5187d12b4de8ffc6a0b03d10a8fc67ae6 Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Thu, 7 Dec 2023 00:25:16 -0600 Subject: [PATCH 36/65] basic write tests complete --- TODO.txt | 16 +-- .../json/writing/JsonCodecMaker.scala | 50 ++++---- .../scala/co.blocke.scalajack/run/Play.scala | 10 +- .../co.blocke.scalajack/run/Record.scala | 7 +- .../co/blocke/scalajack/json/SampleClass.java | 47 ++++++++ .../json/classes/ClassSpec.scala | 109 ++++++++++++++++++ .../json/classes/Model.scala | 64 ++++++---- .../json/misc/AliasSpec.scala | 8 +- 8 files changed, 241 insertions(+), 70 deletions(-) create mode 100644 src/test/java/co/blocke/scalajack/json/SampleClass.java create mode 100644 src/test/scala/co.blocke.scalajack/json/classes/ClassSpec.scala diff --git a/TODO.txt b/TODO.txt index dd84a406..18564a90 100644 --- a/TODO.txt +++ b/TODO.txt @@ -12,12 +12,12 @@ Path to Performance [*] [W] - Java Map [*] [W] - Tuple - [*] [ ] - Scala case class - [*] [ ] - Scala non-case class - [*] [ ] - Java class - [*] [ ] - SelfRef - [*] [ ] - Sealed Trait - [*] [ ] - Sealed Abstract Class (handle like sealed trait....) + [*] [W] - Scala case class + [*] [W] - Scala non-case class + [*] [W] - Java class + [*] [W] - SelfRef + [*] [W] - Sealed Trait + [*] [W] - Sealed Abstract Class (handle like sealed trait....) [*] [W] - Option/Optional [*] [W] - Either @@ -34,8 +34,8 @@ Path to Performance [*] [X] - TypeSymbol (throw exception) [*] [W] - Value class (tested in MapSpec) - [*] [ ] - type hint label mapping - [*] [ ] - type hint value mapping + [*] [W] - type hint label mapping + [*] [W] - type hint value mapping [ ] -- Streaming JSON write support [ ] -- BigJSON support (eg. multi-gig file) \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala b/src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala index f3d5e0f6..74386609 100644 --- a/src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala +++ b/src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala @@ -256,26 +256,22 @@ object JsonCodecMaker: } case t: ScalaClassRef[?] if t.isSealed && t.isAbstractClass => // basically just like sealed trait... - makeFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => - if t.childrenAreObject then - // case object -> just write the simple name of the object - '{ - $out.value($in.getClass.getName.split('.').last.stripSuffix("$")) - } - else - val cases = t.sealedChildren.map { child => - child.refType match - case '[c] => - val subtype = TypeIdent(TypeRepr.of[c].typeSymbol) - val sym = Symbol.newBind(Symbol.spliceOwner, "t", Flags.EmptyFlags, subtype.tpe) - CaseDef(Bind(sym, Typed(Ref(sym), subtype)), None, genFnBody[c](child, Ref(sym).asExprOf[c], out, true).asTerm) - } :+ CaseDef(Literal(NullConstant()), None, '{ $out.burpNull() }.asTerm) - val matchExpr = Match(aE.asTerm, cases).asExprOf[Unit] - '{ - if $in == null then $out.burpNull() - else $matchExpr - } - } + if t.childrenAreObject then + val tin = aE.asExprOf[b] + // case object -> just write the simple name of the object + '{ + $out.value($tin.getClass.getName.split('.').last.stripSuffix("$")) + } + else + val cases = t.sealedChildren.map { child => + child.refType match + case '[c] => + val subtype = TypeIdent(TypeRepr.of[c].typeSymbol) + val sym = Symbol.newBind(Symbol.spliceOwner, "t", Flags.EmptyFlags, subtype.tpe) + CaseDef(Bind(sym, Typed(Ref(sym), subtype)), None, genFnBody[c](child, Ref(sym).asExprOf[c], out, true).asTerm) + } :+ CaseDef(Literal(NullConstant()), None, '{ $out.burpNull() }.asTerm) + val matchExpr = Match(aE.asTerm, cases).asExprOf[Unit] + matchExpr // We don't use makeFn here because a value class is basically just a "box" around a simple type case t: ScalaClassRef[?] if t.isValueClass => @@ -399,10 +395,9 @@ object JsonCodecMaker: t.refType match case '[p] => val rtype = t.expr.asExprOf[JavaClassRType[p]] - val tin = aE.asExprOf[b] + val tin = in.asExprOf[b] var fieldRefs = t.fields.asInstanceOf[List[NonConstructorFieldInfoRef]] - val fieldNames = t.fields.map(_.name) - var i = -1 + val sref = ReflectOnType[String](q)(TypeRepr.of[String])(using scala.collection.mutable.Map.empty[TypedName, Boolean]) '{ if $tin == null then $out.burpNull() else @@ -418,8 +413,7 @@ object JsonCodecMaker: fieldRefs = fieldRefs.tail ref.fieldRef.refType match case '[e] => - i += 1 - maybeWrite[e](fieldNames(i), '{ fieldValue }.asExprOf[e], ref.fieldRef.asInstanceOf[RTypeRef[e]], out, cfg) + maybeWriteMap[String, e]('{ field.name }, '{ fieldValue.asInstanceOf[e] }, sref, ref.fieldRef.asInstanceOf[RTypeRef[e]], out, cfg) } } $out.endObject() @@ -447,11 +441,7 @@ object JsonCodecMaker: CaseDef(Bind(sym, Typed(Ref(sym), subtype)), None, genFnBody[c](child, Ref(sym).asExprOf[c], out, true).asTerm) } :+ CaseDef(Literal(NullConstant()), None, '{ $out.burpNull() }.asTerm) val matchExpr = Match(aE.asTerm, cases).asExprOf[Unit] - // Generating a function for a single match might be overkill, but... there may be a lot of cases, and/or - // this sealed trait may be used a lot of times, so... its a trade-off - makeFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => - matchExpr - } + matchExpr // No makeFn here--Option is just a wrapper to the real thingy case t: OptionRef[?] => diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index 9ad4953b..55292c79 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -22,15 +22,17 @@ object RunMe extends App: import json.* import ScalaJack.* - val inst = Blah("wow", Some(111)) // Some(Some(None))) // Some(Some(3))) - val js = sj[Blah].toJson(inst) - println(js) + // val inst = Blah("wow", Some(111)) // Some(Some(None))) // Some(Some(3))) + // val js = sj[Blah].toJson(inst) + // println(js) // co.blocke.scalajack.internal.CodePrinter.code { // sj[Record] // } - // val y = Foo("You", Dog("Fido", 4), None) + val y = Foo("You", Dog("Fido", 4)) + val js2 = sj[Foo] + println(js2) // val v = Foo("Hey", Fish("Bloop", None), None, Color.Blue) // val v = Foo("Hey", "Boo") diff --git a/src/main/scala/co.blocke.scalajack/run/Record.scala b/src/main/scala/co.blocke.scalajack/run/Record.scala index b13c62ca..cb26b061 100644 --- a/src/main/scala/co.blocke.scalajack/run/Record.scala +++ b/src/main/scala/co.blocke.scalajack/run/Record.scala @@ -37,7 +37,7 @@ case class Record( ) case class Empl(name: String, age: Int) -case class Foo(a: Empl, b: List[Empl]) +// case class Foo(a: Empl, b: List[Empl]) case class Blah(a: String, b: String | Option[Int]) /* case class ArrayHolder[T](a: Array[T]) @@ -48,13 +48,14 @@ case class Foo(name: String, a: Animal, other: Option[Foo], color: Color, expect enum Color: case Red, Blue, Green + */ -sealed trait Animal +case class Foo(name: String, a: Animal) +sealed abstract class Animal @TypeHint(hintValue = "bow-wow") case class Dog(name: String, numLegs: Int) extends Animal @TypeHint(hintValue = "flippy") case class Fish(name: String, isFreshwater: Option[Boolean]) extends Animal - */ val jsData = """{ diff --git a/src/test/java/co/blocke/scalajack/json/SampleClass.java b/src/test/java/co/blocke/scalajack/json/SampleClass.java new file mode 100644 index 00000000..7f150e9a --- /dev/null +++ b/src/test/java/co/blocke/scalajack/json/SampleClass.java @@ -0,0 +1,47 @@ +package co.blocke.scalajack.json; + +import co.blocke.scala_reflection.Ignore; + +public class SampleClass { + // Fields + private String name; + private int age; + private String address; + + // Constructors + // public SampleClass(){} + + public SampleClass(String name, int age, String address) { + this.name = name; + this.age = age; + this.address = address; + } + + // Getter methods + public String getName() { + return name; + } + + @Ignore + public int getAge() { + return age; + } + + public String getAddress() { + return address; + } + + // Setter methods (optional, depending on whether you want to allow modification) + public void setName(String name) { + this.name = name; + } + + public void setAge(int age) { + this.age = age; + } + + public void setAddress(String address) { + this.address = address; + } + +} \ No newline at end of file diff --git a/src/test/scala/co.blocke.scalajack/json/classes/ClassSpec.scala b/src/test/scala/co.blocke.scalajack/json/classes/ClassSpec.scala new file mode 100644 index 00000000..265d9c10 --- /dev/null +++ b/src/test/scala/co.blocke.scalajack/json/classes/ClassSpec.scala @@ -0,0 +1,109 @@ +package co.blocke.scalajack +package json +package classes + +import ScalaJack.* +import co.blocke.scala_reflection.* +import org.scalatest.funspec.AnyFunSpec +import org.scalatest.matchers.should.Matchers.* +import org.scalatest.* +import scala.util.* +import TestUtil.* + +import java.util.UUID + +class ClassSpec() extends AnyFunSpec with JsonMatchers: + opaque type phone = String + + describe(colorString("-------------------------------\n: Class Tests :\n-------------------------------", Console.YELLOW)) { + describe(colorString("+++ Positive Tests +++")) { + it("Simple case class must work") { + val inst = Person("Bob",34) + val js = sj[Person].toJson(inst) + js should matchJson("""{"name":"Bob","age":34}""") + } + it("Inherited class must work") { + val inst = Child("Bob",34, 3) + val js = sj[Child].toJson(inst) + js should matchJson("""{"name":"Bob","age":34,"phase":3}""") + } + it("Non-constructor fields of class must work") { + val inst = Parent(99) + inst.hidden_=(true) + inst.nope_=(false) + val js = sj[Parent].toJson(inst) + js should matchJson("""{"phase":99,"foo":"ok","hidden":true}""") + } + it("Block non-constructor fields of class must work") { + val inst = Parent(99) + inst.hidden_=(true) + inst.nope_=(false) + val js = sj[Parent](JsonConfig.withWriteNonConstructorFields(false)).toJson(inst) + js should matchJson("""{"phase":99}""") + } + it("Parameterized class must work") { + val num: phone = "123-456-7890" + val inst = Params(List(Person("Bob",34),Person("Sarah",28)), Some(num)) + val js = sj[Params[Person,phone]].toJson(inst) + js should matchJson("""{"a":[{"name":"Bob","age":34},{"name":"Sarah","age":28}],"b":"123-456-7890"}""") + } + + it("Sealed trait with case objects and case classes must work") { + val inst = TraitHolder(Start, Fish("Beta",false), Miami(101.1)) + val js = sj[TraitHolder].toJson(inst) + js should matchJson("""{"a":"Start","b":{"_hint":"Fish","species":"Beta","freshwater":false},"c":{"_hint":"Miami","temp":101.1}}""") + } + it("Sealed trait with modified type hint label must work") { + val inst = TraitHolder(Start, Fish("Beta",false), Miami(101.1)) + val js = sj[TraitHolder](JsonConfig.withTypeHintLabel("ref")).toJson(inst) + js should matchJson("""{"a":"Start","b":{"ref":"Fish","species":"Beta","freshwater":false},"c":{"ref":"Miami","temp":101.1}}""") + } + it("Sealed trait with type hint policy SCRAMBLE_CLASSNAME label must work") { + val inst = TraitHolder(Start, Fish("Beta",false), Miami(101.1)) + val js = sj[TraitHolder](JsonConfig.withTypeHintPolicy(TypeHintPolicy.SCRAMBLE_CLASSNAME)).toJson(inst) + val diff = parseJValue(js).diff(parseJValue("""{"a":"Start","b":{"_hint":"82949-049-49A","species":"Beta","freshwater":false},"c":{"_hint":"53150-867-73B","temp":101.1}}""")) + val diffMap = diff.changed.values.asInstanceOf[Map[String,Map[String,_]]] + assert(diffMap("b").contains("_hint") && diffMap("c").contains("_hint") == true) + } + it("Sealed trait with type hint policy USE_ANNOTATION label must work") { + val inst = TraitHolder(Start, Fish("Beta",false), Miami(101.1)) + val js = sj[TraitHolder](JsonConfig.withTypeHintPolicy(TypeHintPolicy.USE_ANNOTATION)).toJson(inst) + js should matchJson("""{"a":"Start","b":{"_hint":"flipper","species":"Beta","freshwater":false},"c":{"_hint":"vice","temp":101.1}}""") + } + + it("Sealed abstract class with case objects and case classes must work") { + val inst = AbstractClassHolder(Start2, Fish2("Beta",false), Miami2(101.1)) + val js = sj[AbstractClassHolder].toJson(inst) + js should matchJson("""{"a":"Start2","b":{"_hint":"Fish2","species":"Beta","freshwater":false},"c":{"_hint":"Miami2","temp":101.1}}""") + } + it("Sealed abstract class with modified type hint label must work") { + val inst = AbstractClassHolder(Start2, Fish2("Beta",false), Miami2(101.1)) + val js = sj[AbstractClassHolder](JsonConfig.withTypeHintLabel("ref")).toJson(inst) + js should matchJson("""{"a":"Start2","b":{"ref":"Fish2","species":"Beta","freshwater":false},"c":{"ref":"Miami2","temp":101.1}}""") + } + it("Sealed abstract class with type hint policy SCRAMBLE_CLASSNAME label must work") { + val inst = AbstractClassHolder(Start2, Fish2("Beta",false), Miami2(101.1)) + val js = sj[AbstractClassHolder](JsonConfig.withTypeHintPolicy(TypeHintPolicy.SCRAMBLE_CLASSNAME)).toJson(inst) + val diff = parseJValue(js).diff(parseJValue("""{"a":"Start2","b":{"_hint":"82949-049-49A","species":"Beta","freshwater":false},"c":{"_hint":"53150-867-73B","temp":101.1}}""")) + val diffMap = diff.changed.values.asInstanceOf[Map[String,Map[String,_]]] + assert(diffMap("b").contains("_hint") && diffMap("c").contains("_hint") == true) + } + it("Sealed abstract class with type hint policy USE_ANNOTATION label must work") { + val inst = AbstractClassHolder(Start2, Fish2("Beta",false), Miami2(101.1)) + val js = sj[AbstractClassHolder](JsonConfig.withTypeHintPolicy(TypeHintPolicy.USE_ANNOTATION)).toJson(inst) + js should matchJson("""{"a":"Start2","b":{"_hint":"flipper","species":"Beta","freshwater":false},"c":{"_hint":"vice","temp":101.1}}""") + } + + it("Self-referencing class must work (bonus: parameterized self-referencing class)"){ + val inst = Empl("abc123", 5, Empl("xyz11", -1, null, Nil), List(Empl("tru777", 0, null, Nil), Empl("pop9", 9, null, Nil))) + val js = sj[Empl[Int]].toJson(inst) + js should matchJson("""{"id":"abc123","data":5,"boss":{"id":"xyz11","data":-1,"boss":null,"coworkers":[]},"coworkers":[{"id":"tru777","data":0,"boss":null,"coworkers":[]},{"id":"pop9","data":9,"boss":null,"coworkers":[]}]}""") + } + + it("Java classes must work") { + val inst = new SampleClass("John Doe", 45, "123 Main St") + val js = sj[SampleClass].toJson(inst) + js should matchJson("""{"address":"123 Main St","name":"John Doe"}""") + } + } + } diff --git a/src/test/scala/co.blocke.scalajack/json/classes/Model.scala b/src/test/scala/co.blocke.scalajack/json/classes/Model.scala index bae2594b..76dc8f6c 100644 --- a/src/test/scala/co.blocke.scalajack/json/classes/Model.scala +++ b/src/test/scala/co.blocke.scalajack/json/classes/Model.scala @@ -2,37 +2,59 @@ package co.blocke.scalajack package json package classes +import co.blocke.scala_reflection.Ignore +import dotty.tools.repl.Command + case class Person(name: String, age: Int) -class Parent(phase:Int): - private var _hidden: Boolean = false - def hidden: Boolean = _hidden - def hidden_=(h: Boolean) = _hidden = h +class Parent(val phase: Int): + private var _hidden: Boolean = false + def hidden: Boolean = _hidden + def hidden_=(h: Boolean) = _hidden = h + + private var _nope: Boolean = false // should not generate due to @Ignore + @Ignore def nope: Boolean = _nope + def nope_=(h: Boolean) = _nope = h + + var foo: String = "ok" + @Ignore var noFoo: String = "not ok" -case class Child(name: String, age: Int, phase: Int) extends Parent(phase) +case class Child(name: String, age: Int, override val phase: Int) extends Parent(phase) -case class Params[X,Y](a: List[X], b: Option[Y]) +case class Params[X, Y](a: List[X], b: Option[Y]) -// Simple case class serialization +sealed trait Command +case object Start extends Command +case object Stop extends Command -// Test inheritance with annotations +sealed trait Animal +@TypeHint(hintValue = "bowow") +case class Dog(name: String, numLegs: Int) extends Animal +@TypeHint(hintValue = "flipper") +case class Fish(species: String, freshwater: Boolean) extends Animal -// Test non-constructor fields of plain classes (Parent) +sealed trait City +class Dallas(val pop: Int) extends City +@TypeHint(hintValue = "vice") +class Miami(val temp: Double) extends City -// Parameterized classes +case class TraitHolder(a: Command, b: Animal, c: City) -// Sealed trait w/case classes -// Sealed trait w/non-case classes -// Sealed trait w/case objects +sealed abstract class Command2 +case object Start2 extends Command2 +case object Stop2 extends Command2 -// Sealed abstract class w/case classes -// Sealed abstract class w/non-case classes -// Sealed abstract class w/case objects +sealed abstract class Animal2 +@TypeHint(hintValue = "bowow") +case class Dog2(name: String, numLegs: Int) extends Animal2 +@TypeHint(hintValue = "flipper") +case class Fish2(species: String, freshwater: Boolean) extends Animal2 -// Java class +sealed abstract class City2 +class Dallas2(val pop: Int) extends City2 +@TypeHint(hintValue = "vice") +class Miami2(val temp: Double) extends City2 -// Self-referencing class -// Self-referencing class where self-ref has a parameter Thing[T](a:Int, b: Option[Thing[T]]) +case class AbstractClassHolder(a: Command2, b: Animal2, c: City2) -// [*] [ ] - type hint label mapping -// [*] [ ] - type hint value mapping \ No newline at end of file +case class Empl[T](id: String, data: T, boss: Empl[T], coworkers: List[Empl[T]]) diff --git a/src/test/scala/co.blocke.scalajack/json/misc/AliasSpec.scala b/src/test/scala/co.blocke.scalajack/json/misc/AliasSpec.scala index dddae13a..e26cf8ae 100644 --- a/src/test/scala/co.blocke.scalajack/json/misc/AliasSpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/misc/AliasSpec.scala @@ -21,22 +21,22 @@ class AliasSpec() extends AnyFunSpec with JsonMatchers: describe(colorString("-------------------------------\n: Alias Tests :\n-------------------------------", Console.YELLOW)) { describe(colorString("+++ Positive Tests +++")) { it("Type aliases (opaque types) must be dereferenced") { - val inst = AliasHolder[Count](5, List(1,2,3), Map(1->"wow"), Map("wow"->2)) + val inst = AliasHolder[Count](5, List(1, 2, 3), Map(1 -> "wow"), Map("wow" -> 2)) val js = sj[AliasHolder[Count]].toJson(inst) js should matchJson("""{"a":5,"b":[1,2,3],"c":{"1":"wow"},"d":{"wow":2}}""") } it("Type aliases (opaque types) must be dereferenced (with Option)") { - val inst = AliasHolder2[CountX](Some(5), List(Some(1),None,Some(3)), Map("wow"->None)) + val inst = AliasHolder2[CountX](Some(5), List(Some(1), None, Some(3)), Map("wow" -> None)) val js = sj[AliasHolder2[CountX]].toJson(inst) js should matchJson("""{"a":5,"b":[1,3],"c":{}}""") } it("Type aliases (non-opaque types) must be dereferenced") { - val inst = AliasHolder[CountY]("q", List("r","s","t"), Map("u"->"wow"), Map("wow"->"v")) + val inst = AliasHolder[CountY]("q", List("r", "s", "t"), Map("u" -> "wow"), Map("wow" -> "v")) val js = sj[AliasHolder[CountY]].toJson(inst) js should matchJson("""{"a":"q","b":["r","s","t"],"c":{"u":"wow"},"d":{"wow":"v"}}""") } it("Type aliases (non-opaque types) must be dereferenced (with Option)") { - val inst = AliasHolder2[CountZ](Some("q"), List(Some("r"),None,Some("t")), Map("wow"->None)) + val inst = AliasHolder2[CountZ](Some("q"), List(Some("r"), None, Some("t")), Map("wow" -> None)) val js = sj[AliasHolder2[CountZ]].toJson(inst) js should matchJson("""{"a":"q","b":["r","t"],"c":{}}""") } From bc11e675354b1cf97b2f6d8f4f7c34fdb3bd043b Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Sun, 10 Dec 2023 13:53:33 -0600 Subject: [PATCH 37/65] NeoType writes work --- TODO.txt | 5 + .../scalajack/AESEncryptionDecryption.java | 50 -- src/main/java/co/blocke/scalajack/Change.java | 9 + .../json/FieldKeyDecoder.scalax | 48 -- .../co.blocke.scalajack/json/JsonConfig.scala | 20 +- .../json/JsonParser.scalax | 367 ------------- .../json/writing/AnyWriter.scala | 130 +++++ .../json/writing/JsonCodecMaker.scala | 37 +- .../json/writing/JsonWriter_old.scalax | 500 ------------------ .../scala/co.blocke.scalajack/run/Play.scala | 13 +- .../co.blocke.scalajack/run/Record.scala | 32 +- .../json/classes/ClassSpec.scala | 34 +- .../json/classes/Model.scala | 2 +- .../json/misc/MiscTests.scala | 4 +- .../json/misc/OptionLRTrySpec.scala | 4 +- 15 files changed, 249 insertions(+), 1006 deletions(-) delete mode 100644 src/main/java/co/blocke/scalajack/AESEncryptionDecryption.java create mode 100644 src/main/java/co/blocke/scalajack/Change.java delete mode 100644 src/main/scala/co.blocke.scalajack/json/FieldKeyDecoder.scalax delete mode 100644 src/main/scala/co.blocke.scalajack/json/JsonParser.scalax create mode 100644 src/main/scala/co.blocke.scalajack/json/writing/AnyWriter.scala delete mode 100644 src/main/scala/co.blocke.scalajack/json/writing/JsonWriter_old.scalax diff --git a/TODO.txt b/TODO.txt index 18564a90..a1ed9a2c 100644 --- a/TODO.txt +++ b/TODO.txt @@ -34,8 +34,13 @@ Path to Performance [*] [X] - TypeSymbol (throw exception) [*] [W] - Value class (tested in MapSpec) + [*] [ ] - Any type -- No longer supported. Can't generate codec code for unknown types + [X] [X] - SJCapture support (optional by config) -- unnecessary! + [*] [ ] - NeoType integration + [*] [W] - type hint label mapping [*] [W] - type hint value mapping + [*] [W] - @Change - field name change [ ] -- Streaming JSON write support [ ] -- BigJSON support (eg. multi-gig file) \ No newline at end of file diff --git a/src/main/java/co/blocke/scalajack/AESEncryptionDecryption.java b/src/main/java/co/blocke/scalajack/AESEncryptionDecryption.java deleted file mode 100644 index b03ebddb..00000000 --- a/src/main/java/co/blocke/scalajack/AESEncryptionDecryption.java +++ /dev/null @@ -1,50 +0,0 @@ -package co.blocke.scalajack; - -import java.nio.charset.StandardCharsets; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.InvalidKeyException; -import java.io.UnsupportedEncodingException; -import javax.crypto.IllegalBlockSizeException; -import java.util.Arrays; -import java.util.Base64; - -import javax.crypto.Cipher; -import javax.crypto.spec.SecretKeySpec; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.BadPaddingException; - -/** - * Java String Encryption Decryption Example - * @author Ramesh Fadatare - * - */ -public class AESEncryptionDecryption { - private static SecretKeySpec secretKey; - private static byte[] key; - private static final String ALGORITHM = "AES"; - - public void prepareSecreteKey(String myKey) throws NoSuchAlgorithmException { - MessageDigest sha = null; - key = myKey.getBytes(StandardCharsets.UTF_8); - sha = MessageDigest.getInstance("SHA-1"); - key = sha.digest(key); - key = Arrays.copyOf(key, 16); - secretKey = new SecretKeySpec(key, ALGORITHM); - } - - public String encrypt(String strToEncrypt, String secret) - throws NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException, IllegalBlockSizeException, NoSuchPaddingException, BadPaddingException { - prepareSecreteKey(secret); - Cipher cipher = Cipher.getInstance(ALGORITHM); - cipher.init(Cipher.ENCRYPT_MODE, secretKey); - return Base64.getEncoder().encodeToString(cipher.doFinal(strToEncrypt.getBytes("UTF-8"))); - } - - public String decrypt(String strToDecrypt, String secret) throws NoSuchAlgorithmException, NoSuchPaddingException, BadPaddingException, InvalidKeyException, IllegalBlockSizeException { - prepareSecreteKey(secret); - Cipher cipher = Cipher.getInstance(ALGORITHM); - cipher.init(Cipher.DECRYPT_MODE, secretKey); - return new String(cipher.doFinal(Base64.getDecoder().decode(strToDecrypt))); - } -} \ No newline at end of file diff --git a/src/main/java/co/blocke/scalajack/Change.java b/src/main/java/co/blocke/scalajack/Change.java new file mode 100644 index 00000000..5222fc66 --- /dev/null +++ b/src/main/java/co/blocke/scalajack/Change.java @@ -0,0 +1,9 @@ +package co.blocke.scalajack; + +import java.lang.annotation.*; + +@Target({ElementType.PARAMETER, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface Change { + String name(); +} diff --git a/src/main/scala/co.blocke.scalajack/json/FieldKeyDecoder.scalax b/src/main/scala/co.blocke.scalajack/json/FieldKeyDecoder.scalax deleted file mode 100644 index c3addcee..00000000 --- a/src/main/scala/co.blocke.scalajack/json/FieldKeyDecoder.scalax +++ /dev/null @@ -1,48 +0,0 @@ -package co.blocke.scalajack -package json - -trait FieldKeyDecoder[+A] { - self => - - final def map[B](f: A => B): FieldKeyDecoder[B] = - new FieldKeyDecoder[B] { - def unsafeDecodeField(in: String): B = f(self.unsafeDecodeField(in)) - } - - final def mapOrFail[B](f: A => Either[String, B]): FieldKeyDecoder[B] = - new FieldKeyDecoder[B] { - def unsafeDecodeField(in: String): B = - f(self.unsafeDecodeField(in)) match { - case Left(err) => throw JsonParseError(err) - case Right(b) => b - } - } - - def unsafeDecodeField(in: String): A -} - -object FieldKeyDecoder { - def apply[A](implicit a: FieldKeyDecoder[A]): FieldKeyDecoder[A] = a - - implicit val string: FieldKeyDecoder[String] = new FieldKeyDecoder[String] { - def unsafeDecodeField(in: String): String = in - } - - implicit val int: FieldKeyDecoder[Int] = - FieldKeyDecoder[String].mapOrFail { str => - try - Right(str.toInt) - catch { - case n: NumberFormatException => Left(s"Invalid Int: '$str': $n") - } - } - - implicit val long: FieldKeyDecoder[Long] = - FieldKeyDecoder[String].mapOrFail { str => - try - Right(str.toLong) - catch { - case n: NumberFormatException => Left(s"Invalid Long: '$str': $n") - } - } -} diff --git a/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala b/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala index 22943f1c..fb4cfe8c 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala @@ -19,16 +19,16 @@ class JsonConfig private[scalajack] ( val typeHintPolicy: TypeHintPolicy, // -------------------------- val enumsAsIds: Option[List[String]], // None=no enums as ids, Some(Nil)=all enums as ids, Some(List(...))=specified classes enums as ids - val escapeStrings: Boolean + val escapedStrings: Boolean ): - def withNoneAsNull(nan: Boolean): JsonConfig = copy(noneAsNull = nan) + def withNoneAsNull(): JsonConfig = copy(noneAsNull = true) def withTryFailureHandling(tryPolicy: TryPolicy): JsonConfig = copy(tryFailureHandling = tryPolicy) def withEitherLeftHandling(eitherPolicy: EitherLeftPolicy): JsonConfig = copy(eitherLeftHandling = eitherPolicy) def withWriteNonConstructorFields(nonConstFlds: Boolean): JsonConfig = copy(writeNonConstructorFields = nonConstFlds) def withTypeHintLabel(label: String): JsonConfig = copy(typeHintLabel = label) def withTypeHintPolicy(hintPolicy: TypeHintPolicy): JsonConfig = copy(typeHintPolicy = hintPolicy) def withEnumsAsIds(asIds: Option[List[String]]): JsonConfig = copy(enumsAsIds = asIds) - def withEscapeStrings(escStr: Boolean): JsonConfig = copy(escapeStrings = escStr) + def withoutEscapedStrings(): JsonConfig = copy(escapedStrings = true) private[this] def copy( noneAsNull: Boolean = noneAsNull, @@ -38,7 +38,7 @@ class JsonConfig private[scalajack] ( typeHintLabel: String = typeHintLabel, typeHintPolicy: TypeHintPolicy = typeHintPolicy, enumsAsIds: Option[List[String]] = enumsAsIds, - escapeStrings: Boolean = escapeStrings + escapedStrings: Boolean = escapedStrings ): JsonConfig = new JsonConfig( noneAsNull, tryFailureHandling, @@ -47,7 +47,7 @@ class JsonConfig private[scalajack] ( typeHintLabel, typeHintPolicy, enumsAsIds, - escapeStrings + escapedStrings ) enum TryPolicy: @@ -71,7 +71,7 @@ object JsonConfig typeHintLabel = "_hint", typeHintPolicy = TypeHintPolicy.SIMPLE_CLASSNAME, enumsAsIds = None, - escapeStrings = true + escapedStrings = true ): import scala.quoted.FromExpr.* @@ -97,7 +97,7 @@ object JsonConfig $typeHintLabelE, $typeHintPolicyE, $enumsAsIdsE, - $escapeStringsE + $escapedStringsE ) } => try @@ -114,7 +114,7 @@ object JsonConfig extract("typeHintLabel", typeHintLabelE), extract("typeHintPolicy", typeHintPolicyE), extract("enumsAsIds", enumsAsIdsE), - extract("escapeStrings", escapeStringsE) + extract("escapedStrings", escapedStringsE) ) ) catch { @@ -123,14 +123,14 @@ object JsonConfig None } case '{ JsonConfig } => Some(JsonConfig) - case '{ ($x: JsonConfig).withNoneAsNull($v) } => Some(x.valueOrAbort.withNoneAsNull(v.valueOrAbort)) + case '{ ($x: JsonConfig).withNoneAsNull() } => Some(x.valueOrAbort.withNoneAsNull()) case '{ ($x: JsonConfig).withTryFailureHandling($v) } => Some(x.valueOrAbort.withTryFailureHandling(v.valueOrAbort)) case '{ ($x: JsonConfig).withEitherLeftHandling($v) } => Some(x.valueOrAbort.withEitherLeftHandling(v.valueOrAbort)) case '{ ($x: JsonConfig).withWriteNonConstructorFields($v) } => Some(x.valueOrAbort.withWriteNonConstructorFields(v.valueOrAbort)) case '{ ($x: JsonConfig).withTypeHintLabel($v) } => Some(x.valueOrAbort.withTypeHintLabel(v.valueOrAbort)) case '{ ($x: JsonConfig).withTypeHintPolicy($v) } => Some(x.valueOrAbort.withTypeHintPolicy(v.valueOrAbort)) case '{ ($x: JsonConfig).withEnumsAsIds($v) } => Some(x.valueOrAbort.withEnumsAsIds(v.valueOrAbort)) - case '{ ($x: JsonConfig).withEscapeStrings($v) } => Some(x.valueOrAbort.withEscapeStrings(v.valueOrAbort)) + case '{ ($x: JsonConfig).withoutEscapedStrings() } => Some(x.valueOrAbort.withoutEscapedStrings()) } private[scalajack] given FromExpr[TryPolicy] with { diff --git a/src/main/scala/co.blocke.scalajack/json/JsonParser.scalax b/src/main/scala/co.blocke.scalajack/json/JsonParser.scalax deleted file mode 100644 index cfbdb80f..00000000 --- a/src/main/scala/co.blocke.scalajack/json/JsonParser.scalax +++ /dev/null @@ -1,367 +0,0 @@ -package co.blocke.scalajack -package json - -import parser.* -import scala.util.* -import co.blocke.scala_reflection.TypedName -import scala.collection.mutable.ListBuffer - -case class JsonParser(js: String, cfg: JsonConfig): // , cache: Map[TypedName, (JsonConfig, JsonParser) => Either[ParseError, ?]]): - - private val jsChars: Array[Char] = js.toCharArray - private var i = 0 - private val max: Int = jsChars.length - - def getPos = i - - // Housekeeping - // ------------------------------ - @inline def nullCheck: Boolean = - val res = i + 3 < max && jsChars(i) == 'n' && jsChars(i + 1) == 'u' && jsChars(i + 2) == 'l' && jsChars(i + 3) == 'l' - if res then i += 4 - res - @inline def eatWhitespace: Either[ParseError, Unit] = - while i < max && jsChars(i).isWhitespace do i += 1 - Right(()) - @inline def expectQuote: Either[ParseError, Unit] = - if jsChars(i) == '\"' then - i += 1 - Right(()) - else Left(JsonParseError(showError(s"Quote expected at position [$i]"))) - @inline def expectComma: Either[ParseError, Unit] = // Note: this consumes whitespace before/after the ',' - for { - _ <- eatWhitespace - r <- - if jsChars(i) == ',' then - i += 1 - eatWhitespace - Right(()) - else Left(CommaExpected(showError(s"Comma expected at position [$i]"))) - } yield r - @inline def expectColon: Either[ParseError, Unit] = // Note: this consumes whitespace before/after the ':' - for { - _ <- eatWhitespace - r <- - if jsChars(i) == ':' then - i += 1 - eatWhitespace - Right(()) - else Left(JsonParseError(showError(s"Expected colon at position [$i]"))) - } yield r - - // JSON label (i.e. object key, which is always a simple string, ie no escaped/special chars) - def expectLabel: Either[ParseError, String] = - jsChars(i) match - case '"' => - i += 1 - val mark = i - while i < max && jsChars(i) != '"' do i += 1 - i += 1 - Right(js.substring(mark, i - 1)) - case x => Left(JsonParseError(showError(s"Unexpected character '$x' where beginning of label expected at position [$i]"))) - - // Data Types - // ------------------------------ - def expectBoolean(isMapKey: Boolean = false): Either[ParseError, Boolean] = - jsChars(i) match - case 't' if i + 3 < max && jsChars(i + 1) == 'r' && jsChars(i + 2) == 'u' && jsChars(i + 3) == 'e' => - i += 4 - Right(true) - case 'f' if i + 4 < max && jsChars(i + 1) == 'a' && jsChars(i + 2) == 'l' && jsChars(i + 3) == 's' && jsChars(i + 4) == 'e' => - i += 5 - Right(false) - case x => - if (isMapKey || cfg.permissivePrimitives) && jsChars(i) == '"' then - for { - _ <- expectQuote - result <- expectBoolean() - _ <- expectQuote - } yield result - else Left(JsonParseError(showError(s"Unexpected character '$x' where beginning of boolean value expected at position [$i]"))) - - def expectLong(isMapKey: Boolean = false): Either[ParseError, Long] = - val mark = i - var done = false - while !done do - jsChars(i) match - case c if (c >= '0' && c <= '9') || c == '-' => i += 1 - case _ => done = true - Try(js.substring(mark, i).toLong) match - case Success(g) => Right(g) - case Failure(f) => - if (isMapKey || cfg.permissivePrimitives) && jsChars(i) == '"' then - for { - _ <- expectQuote - result <- expectLong() - _ <- expectQuote - } yield result - else - val msg = - if mark == i then s"""Int/Long expected but couldn't parse from "${jsChars(i)}" at position [$i]""" - else s"""Int/Long expected but couldn't parse from "${js.substring(mark, i)}" at position [$i]""" - i = mark - Left(JsonParseError(showError(msg))) - - def expectBigLong(isMapKey: Boolean = false): Either[ParseError, BigInt] = - nullCheck match - case true if cfg.forbidNullsInInput => Left(JsonParseError(showError(s"Forbidden 'null' value received at position [$i]"))) - case true => Right(null.asInstanceOf[BigInt]) - case false => - val mark = i - var done = false - while !done do - jsChars(i) match - case c if (c >= '0' && c <= '9') || c == '-' => i += 1 - case _ => done = true - Try(BigInt(js.substring(mark, i))) match - case Success(g) => Right(g) - case Failure(f) => - if (isMapKey || cfg.permissivePrimitives) && jsChars(i) == '"' then - for { - _ <- expectQuote - result <- expectBigLong() - _ <- expectQuote - } yield result - else - val msg = - if mark == i then s"""Int/Long expected but couldn't parse from "${jsChars(i)}" at position [$i]""" - else s"""Int/Long expected but couldn't parse from "${js.substring(mark, i)}" at position [$i]""" - i = mark - Left(JsonParseError(showError(msg))) - - def expectDouble(isMapKey: Boolean = false): Either[ParseError, Double] = - val mark = i - var done = false - while !done do - jsChars(i) match - case c if (c >= '0' && c <= '9') || c == '-' || c == '.' || c == 'e' || c == 'E' || c == '+' => i += 1 - case _ => done = true - Try(js.substring(mark, i).toDouble) match - case Success(g) => Right(g) - case Failure(_) => - if (isMapKey || cfg.permissivePrimitives) && jsChars(i) == '"' then - for { - _ <- expectQuote - result <- expectDouble() - _ <- expectQuote - } yield result - else - val msg = - if mark == i then s"Float/Double expected but couldn't parse from \"${jsChars(i)}\" at position [$i]" - else s"Float/Double expected but couldn't parse from \"${js.substring(mark, i)}\" at position [$i]" - i = mark - Left(JsonParseError(showError(msg))) - - def expectBigDouble(isMapKey: Boolean = false): Either[ParseError, BigDecimal] = - nullCheck match - case true if cfg.forbidNullsInInput => Left(JsonParseError(showError(s"Forbidden 'null' value received at position [$i]"))) - case true => Right(null.asInstanceOf[BigDecimal]) - case false => - val mark = i - var done = false - while !done do - jsChars(i) match - case c if (c >= '0' && c <= '9') || c == '-' || c == '.' || c == 'e' || c == 'E' || c == '+' => i += 1 - case _ => done = true - Try(BigDecimal(js.substring(mark, i))) match - case Success(g) => Right(g) - case Failure(_) => - if (isMapKey || cfg.permissivePrimitives) && jsChars(i) == '"' then - for { - _ <- expectQuote - result <- expectBigDouble() - _ <- expectQuote - } yield result - else - val msg = - if mark == i then s"Float/Double expected but couldn't parse from \"${jsChars(i)}\" at position [$i]" - else s"Float/Double expected but couldn't parse from \"${js.substring(mark, i)}\" at position [$i]" - i = mark - Left(JsonParseError(showError(msg))) - - def expectString(): Either[ParseError, String] = - nullCheck match - case true if cfg.forbidNullsInInput => Left(JsonParseError(showError(s"Forbidden 'null' value received at position [$i]"))) - case true => Right(null.asInstanceOf[String]) - case false => - jsChars(i) match - case '"' => - i += 1 - val mark = i // save position in case we need complex string parse - var captured: Option[String] = None - while i < max && jsChars(i) != '"' do - jsChars(i) match - case '\\' => // oops! special char found--do slow string parse - i = mark - captured = Some(_expectString) - case _ => i += 1 - i += 1 - Right(captured.getOrElse(js.substring(mark, i - 1))) - case x => Left(JsonParseError(showError(s"Unexpected character '$x' where beginning of a string expected at position [$i]"))) - - def expectList[T](expectElement: () => Either[ParseError, T]): Either[ParseError, List[T]] = - nullCheck match - case true if cfg.forbidNullsInInput => Left(JsonParseError(showError(s"Forbidden 'null' value received at position [$i]"))) - case true => Right(null.asInstanceOf[List[T]]) - case false => - if jsChars(i) != '[' then Left(JsonParseError(showError(s"Beginning of list expected at position [$i]"))) - else - i += 1 - eatWhitespace - val acc = ListBuffer.empty[T] - var done: Option[Either[ParseError, List[T]]] = None - while done.isEmpty do - (for { - el <- expectElement() - _ = acc.addOne(el) - _ <- expectComma - } yield el) match - case Left(CommaExpected(_)) if jsChars(i) == ']' => - i += 1 - eatWhitespace - done = Some(Right(acc.toList)) - case Left(e) => - done = Some(Left(e)) - case Right(_) => - done.get - - def expectTuple( - tupleFns: List[(JsonConfig, JsonParser) => Either[ParseError, ?]] - ): Either[ParseError, List[?]] = - if jsChars(i) != '[' then Left(JsonParseError(showError(s"Beginning of tuple expected at position [$i]"))) - else - i += 1 - eatWhitespace - val buf = ListBuffer.empty[Any] - tupleFns - .foldLeft(Right(buf).asInstanceOf[Either[ParseError, ListBuffer[Any]]]) { (acc, fn) => - acc.flatMap(accumulator => - for { - el <- fn(cfg, this) - newAcc = accumulator.addOne(el) - _ <- expectComma - } yield newAcc - ) - } match - case Left(CommaExpected(_)) if buf.size == tupleFns.size && jsChars(i) == ']' => - i += 1 - eatWhitespace - Right(buf.toList) - case Left(CommaExpected(_)) if jsChars(i) == ']' => Left(JsonParseError(showError(s"Missing required elements in tuple at position [$i]"))) - case Left(e: ParseError) => Left(e) - case Right(_) => Left(JsonParseError(showError(s"Extra/unexpected tuple fields at position [$i]"))) - - def expectObject[K, V]( - keyElement: (JsonConfig, JsonParser) => Either[ParseError, K], - valueElement: (JsonConfig, JsonParser) => Either[ParseError, V] - ): Either[ParseError, Map[K, V]] = - if jsChars(i) != '{' then Left(JsonParseError(showError(s"Beginning of object expected at position [$i]"))) - else - i += 1 - eatWhitespace - val acc = scala.collection.mutable.Map.empty[K, V] - var done: Option[Either[ParseError, Map[K, V]]] = None - while done.isEmpty do - (for { - keyLabel <- keyElement(cfg, this) - _ <- expectColon - mapVal <- valueElement(cfg, this) - _ = acc.put(keyLabel, mapVal) - _ <- expectComma - } yield (keyLabel, mapVal)) match - case Left(CommaExpected(_)) if jsChars(i) == '}' => - i += 1 - eatWhitespace - done = Some(Right(acc.toMap)) - case Left(e) => - done = Some(Left(e)) - case Right(_) => - done.get - - // Special case of JSON object where each entry is a field of a class - def expectClass( - fieldMap: Map[String, (JsonConfig, JsonParser) => Either[ParseError, ?]], - fieldValues: scala.collection.mutable.HashMap[String, Any] // pre-set values (Option:None, default values) - ): Either[ParseError, Map[String, ?]] = - if jsChars(i) != '{' then Left(JsonParseError(showError(s"Beginning of class object expected at position [$i]"))) - else - i += 1 - eatWhitespace - var done: Option[Either[ParseError, Map[String, ?]]] = None - while done.isEmpty do - (for { - fieldLabel <- expectLabel - _ <- expectColon - fieldValue <- fieldMap(fieldLabel)(cfg, this) - _ = fieldValues.put(fieldLabel, fieldValue) - _ <- expectComma - } yield fieldValue) match - case Left(CommaExpected(_)) if jsChars(i) == '}' => - i += 1 - eatWhitespace - done = Some(Right(fieldValues.toMap)) - case Left(e) => - done = Some(Left(e)) - case Right(_) => - done.get - - // Slower String parsing that handles special escaped chars - private def _expectString = - val builder = new java.lang.StringBuilder() - while i < max && jsChars(i) != '"' do - if jsChars(i) == '\\' then { - jsChars(i + 1) match { - case '"' => - builder.append('\"') - i += 2 - - case '\\' => - builder.append('\\') - i += 2 - - case 'b' => - builder.append('\b') - i += 2 - - case 'f' => - builder.append('\f') - i += 2 - - case 'n' => - builder.append('\n') - i += 2 - - case 'r' => - builder.append('\r') - i += 2 - - case 't' => - builder.append('\t') - i += 2 - - case 'u' => - val hexEncoded = js.substring(i + 2, i + 6) - val unicodeChar = Integer.parseInt(hexEncoded, 16).toChar - builder.append(unicodeChar.toString) - i += 6 - - case c => - builder.append(c) - i += 2 - } - } else { - builder.append(jsChars(i)) - i += 1 - } - builder.toString - - def showError(msg: String): String = { - val (clip, dashes) = i match { - case ep if ep <= 50 && max < 80 => (js, ep) - case ep if ep <= 50 => (js.substring(0, 77) + "...", ep) - case ep if ep > 50 && ep + 30 >= max => - ("..." + js.substring(i - 49), 52) - case ep => ("..." + js.substring(ep - 49, ep + 27) + "...", 52) - } - msg + s" at positio [$i]" + "\n" + clip.replaceAll("[\n\t]", "~") + "\n" + ("-" * dashes) + "^" - } diff --git a/src/main/scala/co.blocke.scalajack/json/writing/AnyWriter.scala b/src/main/scala/co.blocke.scalajack/json/writing/AnyWriter.scala new file mode 100644 index 00000000..6be73907 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/writing/AnyWriter.scala @@ -0,0 +1,130 @@ +package co.blocke.scalajack +package json +package writing + +import co.blocke.scala_reflection.{RType, TypedName} +import co.blocke.scala_reflection.rtypes.* +import scala.jdk.CollectionConverters.* +import org.apache.commons.text.StringEscapeUtils +import org.apache.commons.lang3.text.translate.CharSequenceTranslator +import scala.util.* + +/** + * Writing an Any-typed value is a hot mess. You don't know until runtime exactly what the target object will actually + * be, so all the compile-time macro magic will be useless. The sad part is that in many ways, most of the macro logic + * must be re-written here, purely as a runtime ability. + */ + +object AnyWriter: + + def writeAny( target: Any, out: JsonOutput, inTuple: Boolean = false ): Unit = + // val rt = RType.of(target.getClass) + target match + case null => out.burpNull() + case v: BigDecimal => out.value(v) + case v: BigInt => out.value(v) + case v: Boolean => out.value(v) + case v: Byte => out.value(v) + case v: Char => out.value(v) + case v: Double => out.value(v) + case v: Float => out.value(v) + case v: Int => out.value(v) + case v: Long => out.value(v) + case v: Short => out.value(v) + case v: String => out.value(StringEscapeUtils.escapeJson(v)) + case v: java.lang.Boolean => out.value(v) + case v: java.lang.Byte => out.value(v) + case v: java.lang.Character => out.value(v) + case v: java.lang.Double => out.value(v) + case v: java.lang.Float => out.value(v) + case v: java.lang.Integer => out.value(v) + case v: java.lang.Long => out.value(v) + case v: java.lang.Short => out.value(v) + case v: java.lang.Number => out.value(v) + case v: java.time.Duration => out.value(v) + case v: java.time.Instant => out.value(v) + case v: java.time.LocalDate => out.value(v) + case v: java.time.LocalDateTime => out.value(v) + case v: java.time.LocalTime => out.value(v) + case v: java.time.MonthDay => out.value(v) + case v: java.time.OffsetDateTime => out.value(v) + case v: java.time.OffsetTime => out.value(v) + case v: java.time.Period => out.value(v) + case v: java.time.Year => out.value(v) + case v: java.time.YearMonth => out.value(v) + case v: java.time.ZoneOffset => out.value(v) + case v: java.time.ZonedDateTime => out.value(v) + case v: java.time.ZoneId => out.value(v) + case v: java.util.UUID => out.value(v) + + case v: Array[_] => + out.startArray() + v.map( e => writeAny(e,out) ) + out.endArray() + + case v: Set[_] => + out.startArray() + v.map( e => writeAny(e,out) ) + out.endArray() + + case v: Seq[_] => + out.startArray() + v.map( e => writeAny(e,out) ) + out.endArray() + + case v: Map[_,_] => + out.startObject() + v.map{ case(k,v) => okToWrite(k.toString, v, out) } + out.endObject() + + case v: Option[_] => + v match + case None => + if inTuple then out.burpNull() + else () + case Some(v2) => writeAny(v2,out) + + case v: Either[_,_] => + v match + case Left(_) => + if inTuple then out.burpNull() + else () + case Right(v2) => writeAny(v2,out) + + case v: Try[_] => + v match + case Failure(_) => + if inTuple then out.burpNull() + else () + case Success(v2) => writeAny(v2,out) + + case v: Tuple => + val varr = v.toArray + out.startArray() + varr.foreach( v2 => writeAny(v2,out,true) ) + out.endArray() + + case v => + val rt = RType.of(v.getClass) + rt match + case t: ScalaClassRType[_] => + out.startObject() + t.fields.map(f => + val field = f.asInstanceOf[ScalaFieldInfo] + val m = v.getClass.getMethod(field.name) + m.setAccessible(true) + val fieldValue = m.invoke(v) + val fieldName = f.annotations.get("co.blocke.scalajack.Change").flatMap(_.get("name")).getOrElse(f.name) + okToWrite(fieldName, fieldValue, out) + ) + out.endObject() + case _ => throw new JsonUnsupportedType("Class "+v.getClass.getName+" not supported for Any type") + + def okToWrite(label:String, value: Any, out: JsonOutput): Unit = + value match + case None => () + case Left(_) => () + case Some(v2) => okToWrite(label, v2, out) + case _ => + out.label(label) + writeAny(value,out) diff --git a/src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala b/src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala index 74386609..9e7f2d2c 100644 --- a/src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala +++ b/src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala @@ -288,7 +288,8 @@ object JsonCodecMaker: f.fieldRef.refType match case '[z] => val fieldValue = Select.unique(in.asTerm, f.name).asExprOf[z] - maybeWrite[z](f.name, fieldValue, f.fieldRef.asInstanceOf[RTypeRef[z]], out, cfg) + val fieldName = f.annotations.get("co.blocke.scalajack.Change").flatMap(_.get("name")).getOrElse(f.name) + maybeWrite[z](fieldName, fieldValue, f.fieldRef.asInstanceOf[RTypeRef[z]], out, cfg) } if emitDiscriminator then val cname = cfg.typeHintPolicy match @@ -310,7 +311,8 @@ object JsonCodecMaker: f.fieldRef.refType match case '[e] => val fieldValue = Select.unique(in.asTerm, f.getterLabel).asExprOf[e] - maybeWrite[e](f.name, fieldValue, f.fieldRef.asInstanceOf[RTypeRef[e]], out, cfg) + val fieldName = f.annotations.get("co.blocke.scalajack.Change").flatMap(_.get("name")).getOrElse(f.name) + maybeWrite[e](fieldName, fieldValue, f.fieldRef.asInstanceOf[RTypeRef[e]], out, cfg) } val subBody = eachField.length match case 0 => '{} @@ -408,12 +410,13 @@ object JsonCodecMaker: val m = $tin.getClass.getMethod(field.getterLabel) m.setAccessible(true) val fieldValue = m.invoke($tin) + val fieldName = field.annotations.get("co.blocke.scalajack.Change").flatMap(_.get("name")).getOrElse(f.name) ${ val ref = fieldRefs.head fieldRefs = fieldRefs.tail ref.fieldRef.refType match case '[e] => - maybeWriteMap[String, e]('{ field.name }, '{ fieldValue.asInstanceOf[e] }, sref, ref.fieldRef.asInstanceOf[RTypeRef[e]], out, cfg) + maybeWriteMap[String, e]('{ fieldName }, '{ fieldValue.asInstanceOf[e] }, sref, ref.fieldRef.asInstanceOf[RTypeRef[e]], out, cfg) } } $out.endObject() @@ -640,7 +643,7 @@ object JsonCodecMaker: if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Short] }) } else '{ $out.value(${ aE.asExprOf[Short] }) } case t: StringRef => - if cfg.escapeStrings then '{ $out.value(StringEscapeUtils.escapeJson(${ aE.asExprOf[String] })) } + if cfg.escapedStrings then '{ $out.value(StringEscapeUtils.escapeJson(${ aE.asExprOf[String] })) } else '{ $out.value(${ aE.asExprOf[String] }) } case t: JBigDecimalRef => @@ -714,6 +717,32 @@ object JsonCodecMaker: else '{ $out.value($rtype.asInstanceOf[EnumRType[_]].ordinal($aE.toString).get) } else '{ $out.value($aE.toString) } + // NeoType is a bit of a puzzle-box. To get the correct underlying base type, I had to dig into + // the argument of method validate$retainedBody. It happened to have the correctly-typed parameter. + // With the correct type, we can correct write out the value. + case t: NeoTypeRef[?] => // in Quotes context + Symbol.requiredModule(t.name).methodMember("validate$retainedBody").head.paramSymss.head.head.tree match + case ValDef(_, tt, _) => + tt.tpe.asType match + case '[u] => + val baseTypeRef = ReflectOnType.apply(q)(tt.tpe)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) + genWriteVal[u]('{ $aE.asInstanceOf[u] }, baseTypeRef.asInstanceOf[RTypeRef[u]], out) + + /* This is how you call make(), which includes validate() + val myMake = module.methodMember("make").head + val tm = Ref(module) + val z = Apply( + Select.unique(tm, "make"), + List( + Expr(List(1, 2, 3)).asTerm + ) + ).asExprOf[Either[String, _]] + '{ + println("Hello...") + println($z) + } + */ + // Everything else... case _ if isStringified => throw new JsonIllegalKeyType("Non-primitive/non-simple types cannot be map keys") case _ => genFnBody(ref, aE, out, inTuple = inTuple) diff --git a/src/main/scala/co.blocke.scalajack/json/writing/JsonWriter_old.scalax b/src/main/scala/co.blocke.scalajack/json/writing/JsonWriter_old.scalax deleted file mode 100644 index 528c062e..00000000 --- a/src/main/scala/co.blocke.scalajack/json/writing/JsonWriter_old.scalax +++ /dev/null @@ -1,500 +0,0 @@ -package co.blocke.scalajack -package json - -import scala.quoted.* -import co.blocke.scala_reflection.* -import co.blocke.scala_reflection.rtypes.{EnumRType, NonConstructorFieldInfo, ScalaClassRType, TraitRType} -import co.blocke.scala_reflection.reflect.{ReflectOnType, TypeSymbolMapper} -import co.blocke.scala_reflection.reflect.rtypeRefs.* -import scala.jdk.CollectionConverters.* -import scala.util.{Failure, Success, Try} -import scala.quoted.staging.* - -/* - TODO: - Code Test - [*] [W] - Primitive types - [*] [W] - Simple Types - - [*] [W] - Seq/Set/Array - [*] [W] - Map - [*] [W] - Java Collections - [*] [W] - Java Map - [*] [W] - Tuple - - [*] [ ] - Scala case class - [*] [ ] - Scala non-case class - [*] [ ] - Java class - [*] [ ] - Sealed Trait - [*] [ ] - Sealed Abstract Class (handle like sealed trait....) - - [*] [ ] - Option/Optional - [*] [ ] - Either - [*] [ ] - Intersection - [*] [ ] - Union - [*] [ ] - Enum - [*] [ ] - Enumeration - [*] [ ] - Java Enum - [*] [ ] - Alias type - [*] [ ] - Object - [*] [ ] - SelfRef - [ ] [ ] - TryRef - [*] [X] - Unknown (throw exception) - [*] [X] - Scala 2 (throw exception) - [*] [X] - TypeSymbol (throw exception) - [*] [W] - Value class (tested in MapSpec) - - [*] [ ] - type hint label mapping - [*] [ ] - type hint value mapping - - [ ] -- Streaming JSON write support - [ ] -- BigJSON support (eg. multi-gig file) - */ - -object JsonWriter_old: - - final inline def lastPart(n: String) = n.split('.').last.stripSuffix("$") - - // Tests whether we should write something or not--mainly in the case of Option, or wrapped Option - // Affected types: Option, java.util.Optional, Left/Right, Try/Failure - def isOkToWrite(a: Any, cfg: JsonConfig) = - a match - case None if !cfg.noneAsNull => false - case o: java.util.Optional[?] if o.isEmpty && !cfg.noneAsNull => false - case Left(None) if !cfg.noneAsNull => false - case Right(None) if !cfg.noneAsNull => false - case Failure(_) if cfg.tryFailureHandling == TryOption.NO_WRITE => false - case _ => true - - def refWrite[T]( - cfgE: Expr[JsonConfig], - ref: RTypeRef[T], - aE: Expr[T], - sbE: Expr[StringBuilder], - isMapKey: Boolean = false - )(using classesSeen: scala.collection.mutable.Map[TypedName, RTypeRef[?]])(using Quotes, Type[T]): Expr[StringBuilder] = - import quotes.reflect.* - - ref match - case t: PrimitiveRef[?] if t.family == PrimFamily.Stringish => '{ if $aE == null then $sbE.append("null") else $sbE.append("\"" + $aE.toString + "\"") } - case t: PrimitiveRef[?] => - val isNullable = Expr(t.isNullable) - if isMapKey then - '{ - if $isNullable && $aE == null then $sbE.append("\"null\"") - else - $sbE.append('"') - $sbE.append($aE.toString) - $sbE.append('"') - } - else '{ if $isNullable && $aE == null then $sbE.append("null") else $sbE.append($aE.toString) } - - case t: SeqRef[?] => - if isMapKey then throw new JsonError("Seq instances cannot be map keys") - - t.elementRef.refType match - case '[e] => - '{ - val sb = $sbE - if $aE == null then sb.append("null") - else - sb.append('[') - val sbLen = sb.length - - $aE.asInstanceOf[Seq[e]].foldLeft(sb) { (acc, one) => - if isOkToWrite(one, $cfgE) then - ${ refWrite[e](cfgE, t.elementRef.asInstanceOf[RTypeRef[e]], '{ one }, '{ acc }) } - sb.append(',') - else sb - } - - if sbLen == sb.length then sb.append(']') - else sb.setCharAt(sb.length() - 1, ']') - } - case t: ArrayRef[?] => - if isMapKey then throw new JsonError("Arrays cannot be map keys") - - t.elementRef.refType match - case '[e] => - '{ - val sb = $sbE - if $aE == null then sb.append("null") - else - sb.append('[') - val sbLen = sb.length - - $aE.asInstanceOf[Array[e]].foldLeft(sb) { (acc, one) => - if isOkToWrite(one, $cfgE) then - ${ refWrite[e](cfgE, t.elementRef.asInstanceOf[RTypeRef[e]], '{ one }, '{ acc }) } - sb.append(',') - else sb - } - - if sbLen == sb.length then sb.append(']') - else sb.setCharAt(sb.length() - 1, ']') - } - - case t: ScalaClassRef[?] if t.isSealed && t.isAbstractClass => - classesSeen.put(t.typedName, t) - if t.childrenAreObject then - // case object -> just write the simple name of the object - '{ - if $aE == null then $sbE.append("null") - else - $sbE.append('"') - $sbE.append(lastPart($aE.getClass.getName)) - $sbE.append('"') - } - else - // Wow! If this is a sealed trait, all the children already have correctly-typed parameters--no need for expensive inTermsOf() - val rt = t.expr.asInstanceOf[Expr[ScalaClassRType[T]]] - '{ - if $aE == null then $sbE.append("null") - else - val className = $aE.getClass.getName - $rt.sealedChildren - .find(_.name == className) - .map(foundKid => - val augmented = foundKid.asInstanceOf[ScalaClassRType[foundKid.T]].copy(renderTrait = Some($rt.name)).asInstanceOf[RType[foundKid.T]] - JsonWriterRT.refWriteRT[foundKid.T]($cfgE, augmented, $aE.asInstanceOf[foundKid.T], $sbE)(using scala.collection.mutable.Map.empty[TypedName, RType[?]]) - ) - .getOrElse(throw new JsonError(s"Unrecognized child $className of seald trait " + $rt.name)) - } - - case t: ScalaClassRef[?] if t.isValueClass => - val theField = t.fields.head.fieldRef - theField.refType match - case '[e] => - val fieldValue = Select.unique(aE.asTerm, t.fields.head.name).asExprOf[e] - refWrite[e](cfgE, theField.asInstanceOf[RTypeRef[e]], fieldValue, sbE) - - case t: ScalaClassRef[?] => - if t.isAbstractClass then throw new JsonError("Cannot serialize an abstract class") - classesSeen.put(t.typedName, t) - val isCase = Expr(t.isCaseClass) - '{ - val sb = $sbE - if $aE == null then sb.append("null") - else - sb.append('{') - val sbLen = sb.length - ${ - t.fields.foldLeft('{ sb }) { (accE, f) => - f.fieldRef.refType match - case '[e] => - val fieldValue = Select.unique(aE.asTerm, f.name).asExprOf[e] - val name = Expr(f.name) - '{ - val acc = $accE - if isOkToWrite($fieldValue, $cfgE) then - acc.append('"') - acc.append($name) - acc.append('"') - acc.append(':') - ${ refWrite[e](cfgE, f.fieldRef.asInstanceOf[RTypeRef[e]], fieldValue, '{ acc }) } - acc.append(',') - else acc - } - } - } - if ! $isCase && $cfgE.writeNonConstructorFields then - ${ - t.nonConstructorFields.foldLeft('{ sb }) { (accE, f) => - f.fieldRef.refType match - case '[e] => - val fieldValue = Select.unique(aE.asTerm, f.getterLabel).asExprOf[e] - val name = Expr(f.name) - '{ - val acc = $accE - if isOkToWrite($fieldValue, $cfgE) then - acc.append('"') - acc.append($name) - acc.append('"') - acc.append(':') - ${ refWrite[e](cfgE, f.fieldRef.asInstanceOf[RTypeRef[e]], fieldValue, '{ acc }) } - acc.append(',') - else acc - } - } - } - if sbLen == sb.length then sb.append('}') - else sb.setCharAt(sb.length() - 1, '}') - } - - case t: TraitRef[?] => - classesSeen.put(t.typedName, t) - val rt = t.expr.asInstanceOf[Expr[TraitRType[T]]] - if t.childrenAreObject then - // case object -> just write the simple name of the object - '{ - if $aE == null then $sbE.append("null") - else - $sbE.append('"') - $sbE.append(lastPart($aE.getClass.getName)) - $sbE.append('"') - } - else if t.isSealed then - // Wow! If this is a sealed trait, all the children already have correctly-typed parameters--no need for expensive inTermsOf() - '{ - if $aE == null then $sbE.append("null") - else - val className = $aE.getClass.getName - $rt.sealedChildren - .find(_.name == className) - .map(foundKid => - val augmented = foundKid.asInstanceOf[ScalaClassRType[foundKid.T]].copy(renderTrait = Some($rt.name)).asInstanceOf[RType[foundKid.T]] - JsonWriterRT.refWriteRT[foundKid.T]($cfgE, augmented, $aE.asInstanceOf[foundKid.T], $sbE)(using scala.collection.mutable.Map.empty[TypedName, RType[?]]) - ) - .getOrElse(throw new JsonError(s"Unrecognized child $className of seald trait " + $rt.name)) - } - else throw new JsonError("non-sealed traits are not supported") - // '{ - // if $aE == null then $sbE.append("null") - // else - // given Compiler = Compiler.make($aE.getClass.getClassLoader) - // val fn = (q: Quotes) ?=> { - // import q.reflect.* - // val sb = $sbE - // val classRType = RType.inTermsOf[T]($aE.getClass).asInstanceOf[ScalaClassRType[T]].copy(renderTrait = Some($rt.name)).asInstanceOf[RType[T]] - // JsonWriterRT.refWriteRT[classRType.T]($cfgE, classRType, $aE.asInstanceOf[classRType.T], $sbE)(using scala.collection.mutable.Map.empty[TypedName, RType[?]]) - // Expr(1) // do-nothing... '{} requires Expr(something) be returned, so... - // } - // quoted.staging.run(fn) - // $sbE - // } - - case t: OptionRef[?] => - if isMapKey then throw new JsonError("Option valuess cannot be map keys") - t.optionParamType.refType match - case '[e] => - '{ - if $aE == null then $sbE.append("null") - else - $aE match - case None => $sbE.append("null") - case Some(v) => - ${ refWrite[e](cfgE, t.optionParamType.asInstanceOf[RTypeRef[e]], '{ v }.asInstanceOf[Expr[e]], sbE) } - } - - case t: MapRef[?] => - if isMapKey then throw new JsonError("Map values cannot be map keys") - t.elementRef.refType match - case '[k] => - t.elementRef2.refType match - case '[v] => - '{ - val sb = $sbE - if $aE == null then sb.append("null") - else - sb.append('{') - val sbLen = sb.length - $aE.asInstanceOf[Map[?, ?]].foreach { case (key, value) => - if isOkToWrite(value, $cfgE) then - val b = ${ refWrite[k](cfgE, t.elementRef.asInstanceOf[RTypeRef[k]], '{ key }.asInstanceOf[Expr[k]], sbE, true) } - b.append(':') - val b2 = ${ refWrite[v](cfgE, t.elementRef2.asInstanceOf[RTypeRef[v]], '{ value }.asInstanceOf[Expr[v]], sbE) } - b2.append(',') - } - if sbLen == sb.length then sb.append('}') - else sb.setCharAt(sb.length() - 1, '}') - } - - case t: LeftRightRef[?] => - if isMapKey then throw new JsonError("Union, Intersection, or Either-typed values cannot be map keys.") - t.leftRef.refType match - case '[lt] => - t.rightRef.refType match - case '[rt] => - val isEither = Expr(t.lrkind == LRKind.EITHER) - '{ - if $isEither then - $aE match - case Left(v) => - val vv = v.asInstanceOf[lt] - ${ refWrite[lt](cfgE, t.leftRef.asInstanceOf[RTypeRef[lt]], '{ vv }.asInstanceOf[Expr[lt]], sbE) } - case Right(v) => - val vv = v.asInstanceOf[rt] - ${ refWrite[rt](cfgE, t.rightRef.asInstanceOf[RTypeRef[rt]], '{ vv }.asInstanceOf[Expr[rt]], sbE) } - else - // Intersection/Union types....take your best shot! It's all we've got. No definitive info here. - val trial = new StringBuilder() - val lrSb = scala.util.Try { - val vv = $aE.asInstanceOf[rt] - ${ - refWrite[rt](cfgE, t.rightRef.asInstanceOf[RTypeRef[rt]], '{ vv }, '{ trial }) - } - } match - case Success(trialSb) => trialSb - case Failure(_) => - trial.clear - val vv = $aE.asInstanceOf[lt] - ${ - refWrite[lt](cfgE, t.leftRef.asInstanceOf[RTypeRef[lt]], '{ vv }, '{ trial }) - } - $sbE ++= lrSb - } - - case t: TryRef[?] => - if isMapKey then throw new JsonError("Try values (Succeed/Fail) cannot be map keys") - t.tryRef.refType match - case '[e] => - '{ - if $aE == null then $sbE.append("null") - else - $aE match - case Success(v) => - ${ refWrite[e](cfgE, t.tryRef.asInstanceOf[RTypeRef[e]], '{ v }.asInstanceOf[Expr[e]], sbE) } - case Failure(_) if $cfgE.tryFailureHandling == TryOption.AS_NULL => $sbE.append("null") - case Failure(_) if $cfgE.tryFailureHandling == TryOption.AS_NULL => $sbE.append("null") - case Failure(f) if $cfgE.tryFailureHandling == TryOption.THROW_EXCEPTION => - throw new JsonError("A try value was Failure with message: " + f.getMessage()) - case Failure(v) => - $sbE.append("\"Failure(") - $sbE.append(v.getMessage) - $sbE.append(")\"") - } - - case t: TupleRef[?] => - if isMapKey then throw new JsonError("Tuples cannot be map keys") - '{ - val sb = $sbE - if $aE == null then sb.append("null") - else - sb.append('[') - val sbLen = sb.length - ${ - val tupleBuf = t.tupleRefs.zipWithIndex.foldLeft(sbE) { case (accE, (ref, i)) => - ref.refType match - case '[e] => - val fieldValue = Select.unique(aE.asTerm, "_" + (i + 1)).asExprOf[e] - '{ - val acc = $accE - ${ refWrite[e](cfgE, ref.asInstanceOf[RTypeRef[e]], fieldValue, '{ acc }) } - acc.append(',') - } - } - tupleBuf - } - if sbLen == sb.length then sb.append(']') - else sb.setCharAt(sb.length() - 1, ']') - } - - case t: JavaCollectionRef[?] => - if isMapKey then throw new JsonError("Collections cannot be map keys.") - t.elementRef.refType match - case '[e] => - '{ - val sb = $sbE - if $aE == null then sb.append("null") - sb.append('[') - val sbLen = sb.length - $aE.asInstanceOf[java.util.Collection[?]].toArray.foreach { elem => - if isOkToWrite(elem, $cfgE) then - ${ refWrite[e](cfgE, t.elementRef.asInstanceOf[RTypeRef[e]], '{ elem }.asInstanceOf[Expr[e]], sbE) } - sb.append(',') - } - if sbLen == sb.length then sb.append(']') - else sb.setCharAt(sb.length() - 1, ']') - } - - case t: JavaMapRef[?] => - if isMapKey then throw new JsonError("Maps cannot be map keys.") - t.elementRef.refType match - case '[k] => - t.elementRef2.refType match - case '[v] => - '{ - val sb = $sbE - if $aE == null then sb.append("null") - sb.append('{') - val sbLen = sb.length - $aE.asInstanceOf[java.util.Map[?, ?]].asScala.foreach { case (key, value) => - if isOkToWrite(value, $cfgE) then - ${ refWrite[k](cfgE, t.elementRef.asInstanceOf[RTypeRef[k]], '{ key }.asInstanceOf[Expr[k]], sbE, true) } - sb.append(':') - ${ refWrite[v](cfgE, t.elementRef2.asInstanceOf[RTypeRef[v]], '{ value }.asInstanceOf[Expr[v]], sbE) } - sb.append(',') - } - if sbLen == sb.length then sb.append('}') - else sb.setCharAt(sb.length() - 1, '}') - } - - case t: AliasRef[?] => - t.unwrappedType.refType match - case '[e] => - refWrite[e](cfgE, t.unwrappedType.asInstanceOf[RTypeRef[e]], aE.asInstanceOf[Expr[e]], sbE) - - case t: SelfRefRef[?] => - if isMapKey then throw new JsonError("Classes or traits cannot be map keys.") - import quotes.reflect.* - val againE = classesSeen.getOrElse(t.typedName, throw new JsonError("Dangling self-reference: " + t.name)).asInstanceOf[RTypeRef[T]].expr - '{ - val again = $againE.asInstanceOf[RType[T]] - JsonWriterRT.refWriteRT[T]($cfgE, again, $aE.asInstanceOf[T], $sbE)(using scala.collection.mutable.Map.empty[TypedName, RType[?]]) - $sbE - } - - case t: EnumRef[?] => - val enumE = t.expr - val isMapKeyE = Expr(isMapKey) - '{ - if $aE == null then $sbE.append("null") - else - val enumRT = $enumE.asInstanceOf[EnumRType[T]] - val enumAsId = $cfgE.enumsAsIds match - case '*' => true - case aList: List[String] if aList.contains(enumRT.name) => true - case _ => false - if enumAsId then - val enumVal = enumRT.ordinal($aE.toString).getOrElse(throw new JsonError("Value " + $aE.toString + s" is not a valid enum value for ${enumRT.name}")) - if $isMapKeyE then - $sbE.append('"') - $sbE.append(enumVal.toString) - $sbE.append('"') - else $sbE.append(enumVal.toString) - else - $sbE.append('"') - $sbE.append($aE.toString) - $sbE.append('"') - } - - // Just handle Java classes 100% runtime since we need to leverage Java reflection entirely anyway - case t: JavaClassRef[?] => - t.refType match - case '[e] => - '{ - JsonWriterRT.refWriteRT[e]($cfgE, ${ t.expr }.asInstanceOf[RType[e]], $aE.asInstanceOf[e], $sbE)(using scala.collection.mutable.Map.empty[TypedName, RType[?]]) - } - - case t: ObjectRef => - val tname = Expr(t.name) - '{ - $sbE.append("\"" + $tname + "\"") - } - - case t: Scala2Ref[?] => - val tname = Expr(t.name) - '{ - $cfgE.undefinedFieldHandling match - case UndefinedValueOption.AS_NULL => $sbE.append("null") - case UndefinedValueOption.AS_SYMBOL => $sbE.append("\"" + $tname + "\"") - case UndefinedValueOption.THROW_EXCEPTION => throw new JsonError("Unknown/unsupported Scala 2 type " + $tname) - } - - case t: UnknownRef[?] => - val tname = Expr(t.name) - '{ - $cfgE.undefinedFieldHandling match - case UndefinedValueOption.AS_NULL => $sbE.append("null") - case UndefinedValueOption.AS_SYMBOL => $sbE.append("\"" + $tname + "\"") - case UndefinedValueOption.THROW_EXCEPTION => throw new JsonError("Unknown/unsupported type " + $tname) - } - - case t: TypeSymbolRef => - val tname = Expr(t.name) - '{ - $cfgE.undefinedFieldHandling match - case UndefinedValueOption.AS_NULL => $sbE.append("null") - case UndefinedValueOption.AS_SYMBOL => $sbE.append("\"" + $tname + "\"") - case UndefinedValueOption.THROW_EXCEPTION => throw new JsonError("Unknown/unsupported type " + $tname + ". (Class didn't fully define all its type parameters.)") - } diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index 55292c79..156b20b0 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -22,6 +22,16 @@ object RunMe extends App: import json.* import ScalaJack.* + val o = json.writing.JsonOutput() + val a: Any = Foo("Hey", Fish("Bloop", Some(true)), ("ok", Seq(true, false))) + json.writing.AnyWriter.writeAny(a, o) + println(o.result) + + val p = Person2(XList(List("x", "y"))) + println(RType.of[Person2].pretty) + val js = sj[Person2].toJson(p) + println(js) + // val inst = Blah("wow", Some(111)) // Some(Some(None))) // Some(Some(3))) // val js = sj[Blah].toJson(inst) // println(js) @@ -30,9 +40,6 @@ object RunMe extends App: // sj[Record] // } - val y = Foo("You", Dog("Fido", 4)) - val js2 = sj[Foo] - println(js2) // val v = Foo("Hey", Fish("Bloop", None), None, Color.Blue) // val v = Foo("Hey", "Boo") diff --git a/src/main/scala/co.blocke.scalajack/run/Record.scala b/src/main/scala/co.blocke.scalajack/run/Record.scala index cb26b061..425b2779 100644 --- a/src/main/scala/co.blocke.scalajack/run/Record.scala +++ b/src/main/scala/co.blocke.scalajack/run/Record.scala @@ -1,6 +1,8 @@ package co.blocke.scalajack package run +import neotype.* + case class Person( name: String, age: Int, @@ -50,12 +52,38 @@ enum Color: case Red, Blue, Green */ -case class Foo(name: String, a: Animal) +case class Foo(name: String, a: Animal, x: (String, Seq[Boolean])) sealed abstract class Animal @TypeHint(hintValue = "bow-wow") case class Dog(name: String, numLegs: Int) extends Animal @TypeHint(hintValue = "flippy") -case class Fish(name: String, isFreshwater: Option[Boolean]) extends Animal +case class Fish(name: String, @Change(name = "fresh") isFreshwater: Option[Boolean]) extends Animal + +type NonEmptyString = NonEmptyString.Type +given NonEmptyString: Newtype[String] with + inline def validate(input: String): Boolean = + input.nonEmpty + +type NonEmptyList = NonEmptyList.Type +given NonEmptyList: Newtype[List[Int]] with + inline def validate(input: List[Int]): Boolean = + input.nonEmpty + +type XList = XList.Type +given XList: Newtype[List[String]] with + inline def validate(input: List[String]): Boolean = + input(0) == "x" + +case class Tag[X](a: X) +given [A, B](using newType: Newtype.WithType[A, B], tag: Tag[A]): Tag[B] = + newType.unsafeWrapF(tag) + +type EmptyString = EmptyString.Type +given EmptyString: Newtype[String] with + inline def validate(input: String): Boolean = + input.isEmpty + +case class Person2(age: XList) val jsData = """{ diff --git a/src/test/scala/co.blocke.scalajack/json/classes/ClassSpec.scala b/src/test/scala/co.blocke.scalajack/json/classes/ClassSpec.scala index 265d9c10..45e3598d 100644 --- a/src/test/scala/co.blocke.scalajack/json/classes/ClassSpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/classes/ClassSpec.scala @@ -18,12 +18,12 @@ class ClassSpec() extends AnyFunSpec with JsonMatchers: describe(colorString("-------------------------------\n: Class Tests :\n-------------------------------", Console.YELLOW)) { describe(colorString("+++ Positive Tests +++")) { it("Simple case class must work") { - val inst = Person("Bob",34) + val inst = Person("Bob", 34) val js = sj[Person].toJson(inst) - js should matchJson("""{"name":"Bob","age":34}""") + js should matchJson("""{"name":"Bob","duration":34}""") } it("Inherited class must work") { - val inst = Child("Bob",34, 3) + val inst = Child("Bob", 34, 3) val js = sj[Child].toJson(inst) js should matchJson("""{"name":"Bob","age":34,"phase":3}""") } @@ -43,58 +43,58 @@ class ClassSpec() extends AnyFunSpec with JsonMatchers: } it("Parameterized class must work") { val num: phone = "123-456-7890" - val inst = Params(List(Person("Bob",34),Person("Sarah",28)), Some(num)) - val js = sj[Params[Person,phone]].toJson(inst) - js should matchJson("""{"a":[{"name":"Bob","age":34},{"name":"Sarah","age":28}],"b":"123-456-7890"}""") + val inst = Params(List(Person("Bob", 34), Person("Sarah", 28)), Some(num)) + val js = sj[Params[Person, phone]].toJson(inst) + js should matchJson("""{"a":[{"name":"Bob","duration":34},{"name":"Sarah","duration":28}],"b":"123-456-7890"}""") } it("Sealed trait with case objects and case classes must work") { - val inst = TraitHolder(Start, Fish("Beta",false), Miami(101.1)) + val inst = TraitHolder(Start, Fish("Beta", false), Miami(101.1)) val js = sj[TraitHolder].toJson(inst) js should matchJson("""{"a":"Start","b":{"_hint":"Fish","species":"Beta","freshwater":false},"c":{"_hint":"Miami","temp":101.1}}""") } it("Sealed trait with modified type hint label must work") { - val inst = TraitHolder(Start, Fish("Beta",false), Miami(101.1)) + val inst = TraitHolder(Start, Fish("Beta", false), Miami(101.1)) val js = sj[TraitHolder](JsonConfig.withTypeHintLabel("ref")).toJson(inst) js should matchJson("""{"a":"Start","b":{"ref":"Fish","species":"Beta","freshwater":false},"c":{"ref":"Miami","temp":101.1}}""") } it("Sealed trait with type hint policy SCRAMBLE_CLASSNAME label must work") { - val inst = TraitHolder(Start, Fish("Beta",false), Miami(101.1)) + val inst = TraitHolder(Start, Fish("Beta", false), Miami(101.1)) val js = sj[TraitHolder](JsonConfig.withTypeHintPolicy(TypeHintPolicy.SCRAMBLE_CLASSNAME)).toJson(inst) val diff = parseJValue(js).diff(parseJValue("""{"a":"Start","b":{"_hint":"82949-049-49A","species":"Beta","freshwater":false},"c":{"_hint":"53150-867-73B","temp":101.1}}""")) - val diffMap = diff.changed.values.asInstanceOf[Map[String,Map[String,_]]] + val diffMap = diff.changed.values.asInstanceOf[Map[String, Map[String, _]]] assert(diffMap("b").contains("_hint") && diffMap("c").contains("_hint") == true) } it("Sealed trait with type hint policy USE_ANNOTATION label must work") { - val inst = TraitHolder(Start, Fish("Beta",false), Miami(101.1)) + val inst = TraitHolder(Start, Fish("Beta", false), Miami(101.1)) val js = sj[TraitHolder](JsonConfig.withTypeHintPolicy(TypeHintPolicy.USE_ANNOTATION)).toJson(inst) js should matchJson("""{"a":"Start","b":{"_hint":"flipper","species":"Beta","freshwater":false},"c":{"_hint":"vice","temp":101.1}}""") } it("Sealed abstract class with case objects and case classes must work") { - val inst = AbstractClassHolder(Start2, Fish2("Beta",false), Miami2(101.1)) + val inst = AbstractClassHolder(Start2, Fish2("Beta", false), Miami2(101.1)) val js = sj[AbstractClassHolder].toJson(inst) js should matchJson("""{"a":"Start2","b":{"_hint":"Fish2","species":"Beta","freshwater":false},"c":{"_hint":"Miami2","temp":101.1}}""") } it("Sealed abstract class with modified type hint label must work") { - val inst = AbstractClassHolder(Start2, Fish2("Beta",false), Miami2(101.1)) + val inst = AbstractClassHolder(Start2, Fish2("Beta", false), Miami2(101.1)) val js = sj[AbstractClassHolder](JsonConfig.withTypeHintLabel("ref")).toJson(inst) js should matchJson("""{"a":"Start2","b":{"ref":"Fish2","species":"Beta","freshwater":false},"c":{"ref":"Miami2","temp":101.1}}""") } it("Sealed abstract class with type hint policy SCRAMBLE_CLASSNAME label must work") { - val inst = AbstractClassHolder(Start2, Fish2("Beta",false), Miami2(101.1)) + val inst = AbstractClassHolder(Start2, Fish2("Beta", false), Miami2(101.1)) val js = sj[AbstractClassHolder](JsonConfig.withTypeHintPolicy(TypeHintPolicy.SCRAMBLE_CLASSNAME)).toJson(inst) val diff = parseJValue(js).diff(parseJValue("""{"a":"Start2","b":{"_hint":"82949-049-49A","species":"Beta","freshwater":false},"c":{"_hint":"53150-867-73B","temp":101.1}}""")) - val diffMap = diff.changed.values.asInstanceOf[Map[String,Map[String,_]]] + val diffMap = diff.changed.values.asInstanceOf[Map[String, Map[String, _]]] assert(diffMap("b").contains("_hint") && diffMap("c").contains("_hint") == true) } it("Sealed abstract class with type hint policy USE_ANNOTATION label must work") { - val inst = AbstractClassHolder(Start2, Fish2("Beta",false), Miami2(101.1)) + val inst = AbstractClassHolder(Start2, Fish2("Beta", false), Miami2(101.1)) val js = sj[AbstractClassHolder](JsonConfig.withTypeHintPolicy(TypeHintPolicy.USE_ANNOTATION)).toJson(inst) js should matchJson("""{"a":"Start2","b":{"_hint":"flipper","species":"Beta","freshwater":false},"c":{"_hint":"vice","temp":101.1}}""") } - it("Self-referencing class must work (bonus: parameterized self-referencing class)"){ + it("Self-referencing class must work (bonus: parameterized self-referencing class)") { val inst = Empl("abc123", 5, Empl("xyz11", -1, null, Nil), List(Empl("tru777", 0, null, Nil), Empl("pop9", 9, null, Nil))) val js = sj[Empl[Int]].toJson(inst) js should matchJson("""{"id":"abc123","data":5,"boss":{"id":"xyz11","data":-1,"boss":null,"coworkers":[]},"coworkers":[{"id":"tru777","data":0,"boss":null,"coworkers":[]},{"id":"pop9","data":9,"boss":null,"coworkers":[]}]}""") diff --git a/src/test/scala/co.blocke.scalajack/json/classes/Model.scala b/src/test/scala/co.blocke.scalajack/json/classes/Model.scala index 76dc8f6c..3e3c8ef3 100644 --- a/src/test/scala/co.blocke.scalajack/json/classes/Model.scala +++ b/src/test/scala/co.blocke.scalajack/json/classes/Model.scala @@ -5,7 +5,7 @@ package classes import co.blocke.scala_reflection.Ignore import dotty.tools.repl.Command -case class Person(name: String, age: Int) +case class Person(name: String, @Change(name = "duration") age: Int) class Parent(val phase: Int): private var _hidden: Boolean = false diff --git a/src/test/scala/co.blocke.scalajack/json/misc/MiscTests.scala b/src/test/scala/co.blocke.scalajack/json/misc/MiscTests.scala index d5f0f4e1..0aa74acf 100644 --- a/src/test/scala/co.blocke.scalajack/json/misc/MiscTests.scala +++ b/src/test/scala/co.blocke.scalajack/json/misc/MiscTests.scala @@ -27,8 +27,8 @@ class MiscSpec() extends AnyFunSpec with JsonMatchers: } it("String without escaping must work (bad JSON, but proves escape can be turned off)") { val inst = StringHolder("""This is a "strange" test\non another level.""") - val js = sj[StringHolder](JsonConfig.withEscapeStrings(false)).toJson(inst) - js should equal("""{"a":"This is a "strange" test\non another level."}""") + val js = sj[StringHolder](JsonConfig.withoutEscapedStrings()).toJson(inst) + js should equal("""{"a":"This is a \"strange\" test\\non another level."}""") } } } diff --git a/src/test/scala/co.blocke.scalajack/json/misc/OptionLRTrySpec.scala b/src/test/scala/co.blocke.scalajack/json/misc/OptionLRTrySpec.scala index da009751..27793051 100644 --- a/src/test/scala/co.blocke.scalajack/json/misc/OptionLRTrySpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/misc/OptionLRTrySpec.scala @@ -62,7 +62,7 @@ class OptionLRTrySpec() extends AnyFunSpec with JsonMatchers: Left(None) // Either of Option (L) ) val js = sj[OptionHolder[Int]]( - JsonConfig.withNoneAsNull(true).withEitherLeftHandling(EitherLeftPolicy.AS_VALUE) + JsonConfig.withNoneAsNull().withEitherLeftHandling(EitherLeftPolicy.AS_VALUE) ).toJson(inst) js should matchJson("""{"a":null,"b":[null,"ok"],"c":[null,null,null],"d":{"1":null,"3":null},"e":null,"f":null,"g":null,"h":null,"i":null,"j":null}""") } @@ -83,7 +83,7 @@ class OptionLRTrySpec() extends AnyFunSpec with JsonMatchers: } it("Complex Either/Option must work (NoneAsNull)") { val inst = ComplexEither[Int](Some(Right(None))) - val js = sj[ComplexEither[Int]](JsonConfig.withNoneAsNull(true)).toJson(inst) + val js = sj[ComplexEither[Int]](JsonConfig.withNoneAsNull()).toJson(inst) js should matchJson("""{"a":null}""") } it("Complex Either/Option must work (Left-NO_WRITE)") { From 3a3001aabe06226cbfe9da3bcb5fb605f91d63fb Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Sun, 10 Dec 2023 22:30:39 -0600 Subject: [PATCH 38/65] writes done --- TODO.txt | 4 +- .../co.blocke.scalajack/json/JsonConfig.scala | 51 +++- .../json/writing/AnyWriter.scala | 259 ++++++++++-------- .../json/writing/JsonCodecMaker.scala | 7 + .../scala/co.blocke.scalajack/run/Play.scala | 8 +- .../json/misc/MiscTests.scala | 42 ++- .../co.blocke.scalajack/json/misc/Model.scala | 30 ++ 7 files changed, 278 insertions(+), 123 deletions(-) diff --git a/TODO.txt b/TODO.txt index a1ed9a2c..0eb3ad77 100644 --- a/TODO.txt +++ b/TODO.txt @@ -34,9 +34,9 @@ Path to Performance [*] [X] - TypeSymbol (throw exception) [*] [W] - Value class (tested in MapSpec) - [*] [ ] - Any type -- No longer supported. Can't generate codec code for unknown types + [*] [W] - Any type -- No longer supported. Can't generate codec code for unknown types [X] [X] - SJCapture support (optional by config) -- unnecessary! - [*] [ ] - NeoType integration + [*] [W] - NeoType integration [*] [W] - type hint label mapping [*] [W] - type hint value mapping diff --git a/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala b/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala index fb4cfe8c..82fc158c 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala @@ -28,7 +28,7 @@ class JsonConfig private[scalajack] ( def withTypeHintLabel(label: String): JsonConfig = copy(typeHintLabel = label) def withTypeHintPolicy(hintPolicy: TypeHintPolicy): JsonConfig = copy(typeHintPolicy = hintPolicy) def withEnumsAsIds(asIds: Option[List[String]]): JsonConfig = copy(enumsAsIds = asIds) - def withoutEscapedStrings(): JsonConfig = copy(escapedStrings = true) + def withoutEscapedStrings(): JsonConfig = copy(escapedStrings = false) private[this] def copy( noneAsNull: Boolean = noneAsNull, @@ -75,6 +75,28 @@ object JsonConfig ): import scala.quoted.FromExpr.* + private[scalajack] given ToExpr[JsonConfig] with { + def apply(x: JsonConfig)(using Quotes): Expr[JsonConfig] = + '{ + val jc = JsonConfig + .withTryFailureHandling(${ Expr(x.tryFailureHandling) }) + .withEitherLeftHandling(${ Expr(x.eitherLeftHandling) }) + .withWriteNonConstructorFields(${ Expr(x.writeNonConstructorFields) }) + .withTypeHintLabel(${ Expr(x.typeHintLabel) }) + .withTypeHintPolicy(${ Expr(x.typeHintPolicy) }) + .withEnumsAsIds(${ Expr(x.enumsAsIds) }) + val jc2 = ${ + if x.noneAsNull then '{ jc.withNoneAsNull() } + else '{ jc } + } + val jc3 = ${ + if !x.escapedStrings then '{ jc2.withoutEscapedStrings() } + else '{ jc2 } + } + jc3 + } + } + private[scalajack] given FromExpr[JsonConfig] with { def extract[X: FromExpr](name: String, x: Expr[X])(using Quotes): X = @@ -133,6 +155,33 @@ object JsonConfig case '{ ($x: JsonConfig).withoutEscapedStrings() } => Some(x.valueOrAbort.withoutEscapedStrings()) } + private[scalajack] given ToExpr[TryPolicy] with { + def apply(x: TryPolicy)(using Quotes): Expr[TryPolicy] = + x match + case TryPolicy.AS_NULL => '{ TryPolicy.AS_NULL } + case TryPolicy.ERR_MSG_STRING => '{ TryPolicy.ERR_MSG_STRING } + case TryPolicy.NO_WRITE => '{ TryPolicy.NO_WRITE } + case TryPolicy.THROW_EXCEPTION => '{ TryPolicy.THROW_EXCEPTION } + } + + private[scalajack] given ToExpr[EitherLeftPolicy] with { + def apply(x: EitherLeftPolicy)(using Quotes): Expr[EitherLeftPolicy] = + x match + case EitherLeftPolicy.AS_VALUE => '{ EitherLeftPolicy.AS_VALUE } + case EitherLeftPolicy.AS_NULL => '{ EitherLeftPolicy.AS_NULL } + case EitherLeftPolicy.ERR_MSG_STRING => '{ EitherLeftPolicy.ERR_MSG_STRING } + case EitherLeftPolicy.NO_WRITE => '{ EitherLeftPolicy.NO_WRITE } + case EitherLeftPolicy.THROW_EXCEPTION => '{ EitherLeftPolicy.THROW_EXCEPTION } + } + + private[scalajack] given ToExpr[TypeHintPolicy] with { + def apply(x: TypeHintPolicy)(using Quotes): Expr[TypeHintPolicy] = + x match + case TypeHintPolicy.SIMPLE_CLASSNAME => '{ TypeHintPolicy.SIMPLE_CLASSNAME } + case TypeHintPolicy.SCRAMBLE_CLASSNAME => '{ TypeHintPolicy.SCRAMBLE_CLASSNAME } + case TypeHintPolicy.USE_ANNOTATION => '{ TypeHintPolicy.USE_ANNOTATION } + } + private[scalajack] given FromExpr[TryPolicy] with { def unapply(x: Expr[TryPolicy])(using Quotes): Option[TryPolicy] = import quotes.reflect.* diff --git a/src/main/scala/co.blocke.scalajack/json/writing/AnyWriter.scala b/src/main/scala/co.blocke.scalajack/json/writing/AnyWriter.scala index 6be73907..45303ead 100644 --- a/src/main/scala/co.blocke.scalajack/json/writing/AnyWriter.scala +++ b/src/main/scala/co.blocke.scalajack/json/writing/AnyWriter.scala @@ -8,123 +8,152 @@ import scala.jdk.CollectionConverters.* import org.apache.commons.text.StringEscapeUtils import org.apache.commons.lang3.text.translate.CharSequenceTranslator import scala.util.* +import scala.quoted.* -/** - * Writing an Any-typed value is a hot mess. You don't know until runtime exactly what the target object will actually - * be, so all the compile-time macro magic will be useless. The sad part is that in many ways, most of the macro logic - * must be re-written here, purely as a runtime ability. +/** Writing an Any-typed value is a hot mess. You don't know until runtime exactly what the target object will actually + * be, so all the compile-time macro magic will be useless. It is 100% runtime, so it will be much slower as well. + * Basically straightforward things (which is 80-90% of what people do) should work fine. If you have a case that + * misbehaves, my best advice is to use a more specific type. As a rule, it's a much better design choice anyway! */ object AnyWriter: - def writeAny( target: Any, out: JsonOutput, inTuple: Boolean = false ): Unit = - // val rt = RType.of(target.getClass) - target match - case null => out.burpNull() - case v: BigDecimal => out.value(v) - case v: BigInt => out.value(v) - case v: Boolean => out.value(v) - case v: Byte => out.value(v) - case v: Char => out.value(v) - case v: Double => out.value(v) - case v: Float => out.value(v) - case v: Int => out.value(v) - case v: Long => out.value(v) - case v: Short => out.value(v) - case v: String => out.value(StringEscapeUtils.escapeJson(v)) - case v: java.lang.Boolean => out.value(v) - case v: java.lang.Byte => out.value(v) - case v: java.lang.Character => out.value(v) - case v: java.lang.Double => out.value(v) - case v: java.lang.Float => out.value(v) - case v: java.lang.Integer => out.value(v) - case v: java.lang.Long => out.value(v) - case v: java.lang.Short => out.value(v) - case v: java.lang.Number => out.value(v) - case v: java.time.Duration => out.value(v) - case v: java.time.Instant => out.value(v) - case v: java.time.LocalDate => out.value(v) - case v: java.time.LocalDateTime => out.value(v) - case v: java.time.LocalTime => out.value(v) - case v: java.time.MonthDay => out.value(v) - case v: java.time.OffsetDateTime => out.value(v) - case v: java.time.OffsetTime => out.value(v) - case v: java.time.Period => out.value(v) - case v: java.time.Year => out.value(v) - case v: java.time.YearMonth => out.value(v) - case v: java.time.ZoneOffset => out.value(v) - case v: java.time.ZonedDateTime => out.value(v) - case v: java.time.ZoneId => out.value(v) - case v: java.util.UUID => out.value(v) - - case v: Array[_] => - out.startArray() - v.map( e => writeAny(e,out) ) - out.endArray() - - case v: Set[_] => - out.startArray() - v.map( e => writeAny(e,out) ) - out.endArray() - - case v: Seq[_] => - out.startArray() - v.map( e => writeAny(e,out) ) - out.endArray() - - case v: Map[_,_] => - out.startObject() - v.map{ case(k,v) => okToWrite(k.toString, v, out) } - out.endObject() - - case v: Option[_] => - v match - case None => - if inTuple then out.burpNull() - else () - case Some(v2) => writeAny(v2,out) - - case v: Either[_,_] => - v match - case Left(_) => - if inTuple then out.burpNull() - else () - case Right(v2) => writeAny(v2,out) - - case v: Try[_] => - v match - case Failure(_) => - if inTuple then out.burpNull() - else () - case Success(v2) => writeAny(v2,out) - - case v: Tuple => - val varr = v.toArray - out.startArray() - varr.foreach( v2 => writeAny(v2,out,true) ) - out.endArray() - - case v => - val rt = RType.of(v.getClass) - rt match - case t: ScalaClassRType[_] => - out.startObject() - t.fields.map(f => - val field = f.asInstanceOf[ScalaFieldInfo] - val m = v.getClass.getMethod(field.name) - m.setAccessible(true) - val fieldValue = m.invoke(v) - val fieldName = f.annotations.get("co.blocke.scalajack.Change").flatMap(_.get("name")).getOrElse(f.name) - okToWrite(fieldName, fieldValue, out) - ) - out.endObject() - case _ => throw new JsonUnsupportedType("Class "+v.getClass.getName+" not supported for Any type") - - def okToWrite(label:String, value: Any, out: JsonOutput): Unit = - value match - case None => () - case Left(_) => () - case Some(v2) => okToWrite(label, v2, out) - case _ => - out.label(label) - writeAny(value,out) + def writeAny(target: Any, out: JsonOutput, cfg: JsonConfig, inTuple: Boolean = false): Unit = + // val rt = RType.of(target.getClass) + target match + case null => out.burpNull() + case v: BigDecimal => out.value(v) + case v: BigInt => out.value(v) + case v: Boolean => out.value(v) + case v: Byte => out.value(v) + case v: Char => out.value(v) + case v: Double => out.value(v) + case v: Float => out.value(v) + case v: Int => out.value(v) + case v: Long => out.value(v) + case v: Short => out.value(v) + case v: String => out.value(StringEscapeUtils.escapeJson(v)) + case v: java.lang.Boolean => out.value(v) + case v: java.lang.Byte => out.value(v) + case v: java.lang.Character => out.value(v) + case v: java.lang.Double => out.value(v) + case v: java.lang.Float => out.value(v) + case v: java.lang.Integer => out.value(v) + case v: java.lang.Long => out.value(v) + case v: java.lang.Short => out.value(v) + case v: java.lang.Number => out.value(v) + case v: java.time.Duration => out.value(v) + case v: java.time.Instant => out.value(v) + case v: java.time.LocalDate => out.value(v) + case v: java.time.LocalDateTime => out.value(v) + case v: java.time.LocalTime => out.value(v) + case v: java.time.MonthDay => out.value(v) + case v: java.time.OffsetDateTime => out.value(v) + case v: java.time.OffsetTime => out.value(v) + case v: java.time.Period => out.value(v) + case v: java.time.Year => out.value(v) + case v: java.time.YearMonth => out.value(v) + case v: java.time.ZoneOffset => out.value(v) + case v: java.time.ZonedDateTime => out.value(v) + case v: java.time.ZoneId => out.value(v) + case v: java.util.UUID => out.value(v) + + case v: Array[?] => + out.startArray() + v.map(e => writeAny(e, out, cfg)) + out.endArray() + + case v: Set[?] => + out.startArray() + v.map(e => writeAny(e, out, cfg)) + out.endArray() + + case v: Seq[?] => + out.startArray() + v.map(e => writeAny(e, out, cfg)) + out.endArray() + + case v: Map[?, ?] => + out.startObject() + v.map { case (k, v) => okToWrite(k.toString, v, out, cfg) } + out.endObject() + + case v: Option[?] => + v match + case None => + if inTuple then out.burpNull() + else () + case Some(v2) => writeAny(v2, out, cfg) + + case v: Either[?, ?] => + v match + case Left(_) => + if inTuple then out.burpNull() + else () + case Right(v2) => writeAny(v2, out, cfg) + + case v: Try[?] => + v match + case Failure(_) => + if inTuple then out.burpNull() + else () + case Success(v2) => writeAny(v2, out, cfg) + + case v: Tuple => + val varr = v.toArray + out.startArray() + varr.foreach(v2 => writeAny(v2, out, cfg, true)) + out.endArray() + + case v => + val rt = RType.of(v.getClass) + rt match + case t: ScalaClassRType[?] => + out.startObject() + t.fields.map(f => + val field = f.asInstanceOf[ScalaFieldInfo] + val m = v.getClass.getMethod(field.name) + m.setAccessible(true) + val fieldValue = m.invoke(v) + val fieldName = f.annotations.get("co.blocke.scalajack.Change").flatMap(_.get("name")).getOrElse(f.name) + okToWrite(fieldName, fieldValue, out, cfg) + ) + out.endObject() + case _ => throw new JsonUnsupportedType("Class " + v.getClass.getName + " not supported for Any type") + + // Called for Any-typed classes + def okToWrite(label: String, value: Any, out: JsonOutput, cfg: JsonConfig): Unit = + isOkToWrite(value, cfg).map { v => + out.label(label) + writeAny(v, out, cfg) + } + + // Called by non-Any classes (in JsonCodecMaker) that have Any-typed fields + def okToWrite2(prefix: Expr[Unit], value: Expr[Any], out: Expr[JsonOutput], cfg: JsonConfig)(using Quotes): Expr[Unit] = + import quotes.reflect.* + '{ + isOkToWrite($value, ${ Expr(cfg) }).map { v => + $prefix + AnyWriter.writeAny(v, $out, ${ Expr(cfg) }) + } + } + + def isOkToWrite(value: Any, cfg: JsonConfig): Option[Any] = + value match + case None => if cfg.noneAsNull then Some("null") else None + case Failure(e) => + cfg.tryFailureHandling match + case TryPolicy.AS_NULL => Some("null") + case TryPolicy.NO_WRITE => None + case TryPolicy.ERR_MSG_STRING => Some("Try Failure with msg: " + e.getMessage()) + case TryPolicy.THROW_EXCEPTION => throw e + + case Left(v) => + cfg.eitherLeftHandling match + case EitherLeftPolicy.AS_VALUE => Some(v) + case EitherLeftPolicy.AS_NULL => Some("null") + case EitherLeftPolicy.NO_WRITE => None + case EitherLeftPolicy.ERR_MSG_STRING => Some("Left Error: " + v.toString) + case EitherLeftPolicy.THROW_EXCEPTION => throw new JsonEitherLeftError("Left Error: " + v.toString) + case Some(v) => isOkToWrite(v, cfg) + case _ => Some(value) diff --git a/src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala b/src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala index 9e7f2d2c..7385b467 100644 --- a/src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala +++ b/src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala @@ -193,6 +193,8 @@ object JsonCodecMaker: $out.revert() ${ _maybeWrite[lt](prefix, '{ $tin.asInstanceOf[lt] }, t.leftRef.asInstanceOf[RTypeRef[lt]], out, cfg) } } + case t: AnyRef => + AnyWriter.okToWrite2(prefix, aE, out, cfg) case _ => ref.refType match case '[u] => @@ -743,6 +745,11 @@ object JsonCodecMaker: } */ + case t: AnyRef => + '{ + AnyWriter.writeAny($aE, $out, ${ Expr(cfg) }) + } + // Everything else... case _ if isStringified => throw new JsonIllegalKeyType("Non-primitive/non-simple types cannot be map keys") case _ => genFnBody(ref, aE, out, inTuple = inTuple) diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index 156b20b0..ec8aee08 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -22,10 +22,10 @@ object RunMe extends App: import json.* import ScalaJack.* - val o = json.writing.JsonOutput() - val a: Any = Foo("Hey", Fish("Bloop", Some(true)), ("ok", Seq(true, false))) - json.writing.AnyWriter.writeAny(a, o) - println(o.result) + // val o = json.writing.JsonOutput() + // val a: Any = Foo("Hey", Fish("Bloop", Some(true)), ("ok", Seq(true, false))) + // json.writing.AnyWriter.writeAny(a, o) + // println(o.result) val p = Person2(XList(List("x", "y"))) println(RType.of[Person2].pretty) diff --git a/src/test/scala/co.blocke.scalajack/json/misc/MiscTests.scala b/src/test/scala/co.blocke.scalajack/json/misc/MiscTests.scala index 0aa74acf..3b605ad8 100644 --- a/src/test/scala/co.blocke.scalajack/json/misc/MiscTests.scala +++ b/src/test/scala/co.blocke.scalajack/json/misc/MiscTests.scala @@ -28,7 +28,47 @@ class MiscSpec() extends AnyFunSpec with JsonMatchers: it("String without escaping must work (bad JSON, but proves escape can be turned off)") { val inst = StringHolder("""This is a "strange" test\non another level.""") val js = sj[StringHolder](JsonConfig.withoutEscapedStrings()).toJson(inst) - js should equal("""{"a":"This is a \"strange\" test\\non another level."}""") + js should equal("""{"a":"This is a "strange" test\non another level."}""") + } + it("NeoType integration must work") { + val inst = Validated(NonEmptyString("Mike"), XList(List("x", "y", "z")), List(EmptyString(""), EmptyString(""), EmptyString(""))) + val js = sj[Validated].toJson(inst) + js should equal("""{"name":"Mike","xspot":["x","y","z"],"nada":["","",""]}""") + } + it("Any type must work (non-exhaustive test)") { + val inst = AnyHolder( + Some(List(1, 2, 3)), + None, + TryHolder(Success(-5)), + Success(99), + Failure(new Exception("oops")), + Map("a" -> 1, "b" -> 2), + Right(3), + Left("nope"), + (Some('a'), None, Some('b')) + ) + val js = sj[AnyHolder].toJson(inst) + js should equal("""{"maybe":[1,2,3],"itried":{"a":-5},"itried2":99,"ifailed":"anymap":{"a":1,"b":2},"whichOneR":3,"bunch":["a",null,"b"]}""") + } + it("Any type must work (none as null)") { + val inst = AnyHolder( + Some(List(1, 2, 3)), + None, + TryHolder(Success(-5)), + Success(99), + Failure(new Exception("oops")), + Map("a" -> 1, "b" -> 2), + Right(3), + Left("nope"), + (Some('a'), None, Some('b')) + ) + val js = sj[AnyHolder]( + JsonConfig + .withNoneAsNull() + .withEitherLeftHandling(EitherLeftPolicy.ERR_MSG_STRING) + .withTryFailureHandling(TryPolicy.AS_NULL) + ).toJson(inst) + js should equal("""{"maybe":[1,2,3],"maybeNot":"null","itried":{"a":-5},"itried2":99,"ifailed":"null","anymap":{"a":1,"b":2},"whichOneR":3,"whichOneL":"Left Error: nope","bunch":["a",null,"b"]}""") } } } diff --git a/src/test/scala/co.blocke.scalajack/json/misc/Model.scala b/src/test/scala/co.blocke.scalajack/json/misc/Model.scala index 78c3e8ca..aae61c3b 100644 --- a/src/test/scala/co.blocke.scalajack/json/misc/Model.scala +++ b/src/test/scala/co.blocke.scalajack/json/misc/Model.scala @@ -4,6 +4,7 @@ package misc import java.util.Optional import scala.util.* +import neotype.* case class Person(name: String, age: Int) @@ -32,3 +33,32 @@ case class AliasHolder[T](a: T, b: List[T], c: Map[T, String], d: Map[String, T] case class AliasHolder2[T](a: T, b: List[T], c: Map[String, T]) case class StringHolder(a: String) + +type NonEmptyString = NonEmptyString.Type +given NonEmptyString: Newtype[String] with + inline def validate(input: String): Boolean = + input.nonEmpty + +type XList = XList.Type +given XList: Newtype[List[String]] with + inline def validate(input: List[String]): Boolean = + input.nonEmpty && input(0) == "x" + +type EmptyString = EmptyString.Type +given EmptyString: Newtype[String] with + inline def validate(input: String): Boolean = + input.isEmpty + +case class Validated(name: NonEmptyString, xspot: XList, nada: List[EmptyString]) + +case class AnyHolder( + maybe: Any, // Option[List[String]] <- Some + maybeNot: Any, // None + itried: Any, // TryHolder[Int] <- class test + itried2: Any, // Try[Int] (Success) + ifailed: Any, // Try[Int] (Failure) + anymap: Any, // Map[String,Int] + whichOneR: Any, // Either[String,Int] <- right + whichOneL: Any, // Either[String,Int] <- left + bunch: Any // (Some('a'),None,Some('b')) +) From facd89ea68bb55d95395b9f6c289f7fc32b8d105 Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Thu, 25 Jan 2024 23:37:55 -0600 Subject: [PATCH 39/65] First tentative steps into reading --- benchmark/README.md | 68 +-- benchmark/build.sbt | 10 +- .../src/main/scala/co.blocke/Benchmark.scala | 12 +- benchmark/src/main/scala/co.blocke/Run.scala | 24 +- .../json/{ => exp}/ByteArrayAccess.java | 2 +- .../scala/co.blocke.scalajack/ScalaJack.scala | 23 +- .../co.blocke.scalajack/json/JsonCodec.scala | 9 +- .../co.blocke.scalajack/json/JsonError.scala | 18 +- .../json/exp/JsonReader.scala | 440 ++++++++++++++++++ .../json/exp/JsonReaderException.scala | 6 + .../json/exp/package.scala | 15 + .../json/reading/ClassDecoder.scala | 15 + .../{JsonReader.scala => JsonReader.scalax} | 9 +- .../json/reading/JsonReader.scalax2 | 114 +++++ .../json/reading/JsonSource.scala | 128 ++++- .../json/writing/JsonCodecMaker.scala | 166 +++++-- .../scala/co.blocke.scalajack/run/Play.scala | 97 ++-- 17 files changed, 1006 insertions(+), 150 deletions(-) rename src/main/java/co/blocke/scalajack/json/{ => exp}/ByteArrayAccess.java (97%) create mode 100644 src/main/scala/co.blocke.scalajack/json/exp/JsonReader.scala create mode 100644 src/main/scala/co.blocke.scalajack/json/exp/JsonReaderException.scala create mode 100644 src/main/scala/co.blocke.scalajack/json/exp/package.scala rename src/main/scala/co.blocke.scalajack/json/reading/{JsonReader.scala => JsonReader.scalax} (90%) create mode 100644 src/main/scala/co.blocke.scalajack/json/reading/JsonReader.scalax2 diff --git a/benchmark/README.md b/benchmark/README.md index 5405fb5f..a7420b02 100644 --- a/benchmark/README.md +++ b/benchmark/README.md @@ -1,12 +1,12 @@ # Performance -JSON serialization benchmarks I found in various repos often measured (IMO) silly things like how fast -a parser could handle a small list of Int. For this benchmark I used a more substantial model + JSON. +JSON serialization benchmarks I found in various project repos often measured (IMO) silly things like how fast +a parser could handle a small list of Int. For this benchmark I used a slightly more substantial model. It's still a small model, but it does have some nested objects and collections that make it a more -realistic test. +interesting test. -The test is run via jmh, a common and accepted benchmarking tool. The JVM is **stock**--not tuned to -within an inch of its life, again to be a more realistic use case. +The test is run via jmh. The JVM is **stock**--not tuned to within an inch of its life, to be a more realistic +use case. Run benchmark from the ScalaJack/benchmark directory (not the main ScalaJack project directory): ``` @@ -40,60 +40,72 @@ sbt "jmh:run -i 10 -wi 10 -f 2 -t 1 co.blocke.*" | Argonaut | thrpt | 20 | 690269.697 | ± 6348.882 | ops/s | | Play JSON | thrpt | 20 | 438650.022 | ± 23800.221 | ops/s | -**Note:** Exact numbers aren't terribly important--they will vary depending on the platform +**Note:** Exact numbers aren't terribly important--they may vary widely depending on the platform used. The important thing is the relative relationship between libraries given all tests were performed on the same platform. ### Interpretation -Performance for ScalaJack has been a journey. ScalaJack is a mature product, and while it -was once (a long time ago) quite fast vs its competition, its performance has lagged -considerably. ScalaJack 8 changes that! +Performance for ScalaJack has been a journey. ScalaJack is a mature product--over 10 yrs old, +can you believe it?. Long ago it was quite fast vs its competition. Over the years though, its +performance has lagged considerably, to the point that it was one of the slower serialization +libraries. ScalaJack 8 changes that! I was sampling and testing against a collection of popular serializers for Scala util something quite unexpected happend. When I tested Jsoniter, its performance was through -the roof! Even faster than hand-tooled code. This was a shock. I had to learn how this -worked. +the roof! It far outpaced all competitors for raw speed. This was a shock. I had to +learn how this worked. So full credit where credit is due: ScalaJack 8's reading/writing codec architecture -is heavily derived from Jsoniter. +is heavily informed from Jsoniter, so I'll post their licence here: [Jsoniter's License](https://github.com/plokhotnyuk/jsoniter-scala/blob/af23cf65a70d48834b8fecb792cc333b23409c6f/LICENSE) There are a number of optimizations and design choices I elected not to bring over from -Jsoniter, and of course ScalaJack utilizes our own scala-reflection library to great effect. +Jsoniter, in many cases because ScalaJack doesn't need them for its intended feature set. +Of course ScalaJack utilizes our own macro-driven scala-reflection library to great effect, +which Jsoniter does not. -Jsoniter, it turns out, achieves its neck-breaking speed by going deep--very deep. They -use a lot of low level byte arrays and bitwise operators, much as you'd expect to see in -a C program, to improve on the standard library functions everyone else uses. It works. +Jsoniter achieves its neck-breaking speed by going deep--very deep into macro code +generation. They also use a lot of low level byte arrays and bitwise operators, much as you'd +expect to see in a C program, to improve on the standard library functions everyone else uses. +It works. ScalaJack's focus is first and foremost to be frictionless--no drama to the user. ScalaJack requires zero boilerplate--you can throw any Scala object (or even a Java object) at it with no pre-preparation -and it will serialize it. For its intended use-cases, ScalaJack offers excellent performance, equal -to or exceeding a number of widely-used alternative choices. +and it will serialize it. For its intended use-cases, out-of-the-box ScalaJack 8 offers excellent +performance, equal to or exceeding a number of widely-used alternative choices. + +If you're willing to suffer just 1 single line of boilerplate, ScalaJack 8 will reward you with +speed that's in the top one or two of its class ("fast mode" in the results). ### Technical Notes -Achieving extreme speed for ScalaJack was weeks of learning, trial, error, -and re-writes. I studied Jsoniter, Circe, and ZIO Json, and others to learn optimizations. +Achieving extreme speed for ScalaJack 8 was several weeks of learning, trial, error, +and re-writes. I studied Jsoniter, Circe, ZIO Json, and others to learn optimizations. The tough news for anyone wanting to duplicate this kind of performance in your own code is that there isn't one magic trick to achieve maximum performance. It's a basket -of techniques, each achieving marginal gains that add up, and you must decide when enough -is enough. Here's a partial list of learnings incorporated into ScalaJack: +of techniques, each achieving small marginal gains that add up, and you must decide when +enough is enough for you. Here's a partial list of learnings incorporated into ScalaJack 8: * Being careful when using .asInstanceOf[]... in fact try to avoid it wherever possible - as it messes up CPU cache harming performance. This means a lot of very careful type + as it messes up CPU cache, harming performance. This means a lot of very careful type management, and its why you see the RTypeRefs from scala-reflection are now all typed in the latest version -* Lots of specific typing. Don't make the computer think--provide detailed types wherever +* Lots of specific typing. Don't make the compiler think--provide detailed types wherever you can * For macro-based software like this--find every opportunity to do hard work at compile-time * Be mindful of what code your macros generate! You can paint by the numbers with quotes and - splices, like the documentaion and blogs suggest, and you'll get something working. When you - examine the code this produces, you may be disappointed. If it looks kludgy it will be slow--rework - your macros until the code is smooth. For fastest performance you'll actually have to generate - custom functions as shown in ScalaJack's code (look at JsonCodecMaker.scala) + splices, like the documentaion and blogs suggest, and you will get something working. + When you examine the code a "stock" macro use produces, you may be disappointed + if ultimate runtime speed is your goal. Then generated code might look a litle kludgy, and + it will not necessarily be speed optimized. Rework your macros carefully until the generated code + is as smooth as you might write by hand. Remember: your macro code doesn't have to win awards for + style or beauty--your generated code does! For the fastest performance you'll actually have + to generate custom functions as shown in ScalaJack's code (look at JsonCodecMaker.scala) This + isn't for the faint of heart. If it all looks like Greek, step back and layer yourself into + macros slowly a piece at a time. diff --git a/benchmark/build.sbt b/benchmark/build.sbt index c5af1411..45d16189 100644 --- a/benchmark/build.sbt +++ b/benchmark/build.sbt @@ -36,15 +36,17 @@ lazy val benchmark = project libraryDependencies ++= Seq( "org.playframework" %% "play-json" % "3.0.1", "io.argonaut" %% "argonaut" % "6.3.9", - "co.blocke" %% "scalajack" % "fc0b25_unknown", - "co.blocke" %% "scala-reflection" % "sj_fixes_edbef8", + "co.blocke" %% "scalajack" % "3a3001_unknown", + "co.blocke" %% "scala-reflection" % "sj_fixes_f43af7", "dev.zio" %% "zio-json" % "0.6.1", "org.typelevel" %% "fabric-core" % "1.12.6", "org.typelevel" %% "fabric-io" % "1.12.6", "org.typelevel" %% "jawn-parser" % "1.3.2", "org.typelevel" %% "jawn-ast" % "1.3.2", - "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core" % "2.24.4", - "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-macros" % "2.24.4" % "compile-internal", + "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core" % "2.24.5-SNAPSHOT", + "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-macros" % "2.24.5-SNAPSHOT" % "compile-internal", + // "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core" % "2.24.4", + // "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-macros" % "2.24.4" % "compile-internal", // "io.circe" %% "circe-derivation" % "0.15.0-M1", // "io.circe" %% "circe-jackson29" % "0.14.0", // "org.json4s" %% "json4s-jackson" % "4.0.4", diff --git a/benchmark/src/main/scala/co.blocke/Benchmark.scala b/benchmark/src/main/scala/co.blocke/Benchmark.scala index 045faa89..5beb8197 100644 --- a/benchmark/src/main/scala/co.blocke/Benchmark.scala +++ b/benchmark/src/main/scala/co.blocke/Benchmark.scala @@ -43,12 +43,12 @@ trait HandTooledWritingBenchmark { @BenchmarkMode(Array(Mode.Throughput)) @OutputTimeUnit(TimeUnit.SECONDS) class ReadingBenchmark - // extends CirceZ.CirceReadingBenchmark - extends ScalaJackZ.ScalaJackReadingBenchmark - // with JsoniterZ.JsoniterReadingBenchmark - // with ZIOZ.ZIOJsonReadingBenchmark - // with PlayZ.PlayReadingBenchmark - // with ArgonautZ.ArgonautReadingBenchmark + extends CirceZ.CirceReadingBenchmark + with ScalaJackZ.ScalaJackReadingBenchmark + with JsoniterZ.JsoniterReadingBenchmark + with ZIOZ.ZIOJsonReadingBenchmark + with PlayZ.PlayReadingBenchmark + with ArgonautZ.ArgonautReadingBenchmark @State(Scope.Thread) @BenchmarkMode(Array(Mode.Throughput)) diff --git a/benchmark/src/main/scala/co.blocke/Run.scala b/benchmark/src/main/scala/co.blocke/Run.scala index 33451f47..f359887a 100644 --- a/benchmark/src/main/scala/co.blocke/Run.scala +++ b/benchmark/src/main/scala/co.blocke/Run.scala @@ -15,12 +15,30 @@ object RunMe extends App: // deriveEncoder[Record] // } - co.blocke.scalajack.internal.CodePrinter.code { - given codec: JsonValueCodec[Record] = JsonCodecMaker.make - } + // import co.blocke.scalajack.* + // import ScalaJack.* + // implicit val blah: ScalaJack[Record] = sj[Record] + // println(ScalaJack[Record].fromJson(jsData)) + + + // co.blocke.scalajack.internal.CodePrinter.code { + // given codec: JsonValueCodec[Record] = JsonCodecMaker.make + // } // given codec: JsonValueCodec[Record] = JsonCodecMaker.make // println(readFromString[Record](jsData)) // println(writeToString(record)) + import com.github.plokhotnyuk.jsoniter_scala.core._ + import com.github.plokhotnyuk.jsoniter_scala.macros._ + + given codec: JsonValueCodec[Record] = JsonCodecMaker.make + println(readFromString[Record](jsData)) + // } + + // trait JsoniterWritingBenchmark{ + // @Benchmark + // def writeRecordJsoniter = writeToString(record) + // } + println("\nDone") \ No newline at end of file diff --git a/src/main/java/co/blocke/scalajack/json/ByteArrayAccess.java b/src/main/java/co/blocke/scalajack/json/exp/ByteArrayAccess.java similarity index 97% rename from src/main/java/co/blocke/scalajack/json/ByteArrayAccess.java rename to src/main/java/co/blocke/scalajack/json/exp/ByteArrayAccess.java index 99771032..d85cc1ad 100644 --- a/src/main/java/co/blocke/scalajack/json/ByteArrayAccess.java +++ b/src/main/java/co/blocke/scalajack/json/exp/ByteArrayAccess.java @@ -1,4 +1,4 @@ -package co.blocke.scalajack.json; +package co.blocke.scalajack.json.exp; import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; diff --git a/src/main/scala/co.blocke.scalajack/ScalaJack.scala b/src/main/scala/co.blocke.scalajack/ScalaJack.scala index cebe8c02..e8258682 100644 --- a/src/main/scala/co.blocke.scalajack/ScalaJack.scala +++ b/src/main/scala/co.blocke.scalajack/ScalaJack.scala @@ -7,6 +7,15 @@ import scala.quoted.* import quoted.Quotes import json.* +case class ScalaJack[T](jsonCodec: JsonCodec[T]): // extends JsonCodec[T] //with YamlCodec with MsgPackCodec + def fromJson(js: String): T = // Either[JsonParseError, T] = + jsonCodec.decodeValue(reading.JsonSource(js)) + + val out = writing.JsonOutput() // let's clear & re-use JsonOutput--avoid re-allocating all the internal buffer space + def toJson(a: T): String = + jsonCodec.encodeValue(a, out.clear()) + out.result +/* case class ScalaJack[T](jsonDecoder: reading.JsonDecoder[T], jsonEncoder: JsonCodec[T]): // extends JsonCodec[T] //with YamlCodec with MsgPackCodec def fromJson(js: String): Either[JsonParseError, T] = jsonDecoder.decodeJson(js) @@ -15,6 +24,7 @@ case class ScalaJack[T](jsonDecoder: reading.JsonDecoder[T], jsonEncoder: JsonCo def toJson(a: T): String = jsonEncoder.encodeValue(a, out.clear()) out.result + */ // --------------------------------------- @@ -27,10 +37,11 @@ object ScalaJack: def sjImpl[T: Type](using Quotes): Expr[ScalaJack[T]] = import quotes.reflect.* val classRef = ReflectOnType[T](quotes)(TypeRepr.of[T], true)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) - val jsonDecoder = reading.JsonReader.refRead(classRef) - val jsonEncoder = writing.JsonCodecMaker.generateCodecFor(classRef, JsonConfig) + // val jsonDecoder = reading.JsonReader.refRead2(classRef) + // println(s"Decoder: ${jsonDecoder.show}") + val jsonCodec = writing.JsonCodecMaker.generateCodecFor(classRef, JsonConfig) - '{ ScalaJack($jsonDecoder, $jsonEncoder) } + '{ ScalaJack($jsonCodec) } // ----- Use given JsonConfig inline def sj[T](inline cfg: JsonConfig): ScalaJack[T] = ${ sjImplWithConfig[T]('cfg) } @@ -38,9 +49,9 @@ object ScalaJack: import quotes.reflect.* val cfg = summon[FromExpr[JsonConfig]].unapply(cfgE) val classRef = ReflectOnType[T](quotes)(TypeRepr.of[T], true)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) - val jsonDecoder = reading.JsonReader.refRead(classRef) - val jsonEncoder = writing.JsonCodecMaker.generateCodecFor(classRef, cfg.getOrElse(JsonConfig)) - '{ ScalaJack($jsonDecoder, $jsonEncoder) } + // val jsonDecoder = reading.JsonReader.refRead2(classRef) + val jsonCodec = writing.JsonCodecMaker.generateCodecFor(classRef, cfg.getOrElse(JsonConfig)) + '{ ScalaJack($jsonCodec) } // refRead[T](classRef) diff --git a/src/main/scala/co.blocke.scalajack/json/JsonCodec.scala b/src/main/scala/co.blocke.scalajack/json/JsonCodec.scala index a8d07bce..200ccc45 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonCodec.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonCodec.scala @@ -2,14 +2,13 @@ package co.blocke.scalajack package json import writing.* +import reading.* trait JsonCodec[A] { - // TBD... when we're ready to tackle reading! - // def decodeValue(in: JsonReader, default: A): A = ${ - // if (cfg.encodingOnly) '{ ??? } - // else genReadVal(rootTpe :: Nil, 'default, cfg.isStringified, false, 'in) - // } + // def decodeValue(in: JsonReader, default: A): A = + // ${ genReadVal(rootTpe :: Nil, 'default, cfg.isStringified, false, 'in) } def encodeValue(in: A, out: JsonOutput): Unit + def decodeValue(in: JsonSource): A } diff --git a/src/main/scala/co.blocke.scalajack/json/JsonError.scala b/src/main/scala/co.blocke.scalajack/json/JsonError.scala index d10e0a59..f790f1d0 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonError.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonError.scala @@ -1,21 +1,23 @@ package co.blocke.scalajack package json -class JsonIllegalKeyType(msg: String) extends Throwable(msg) -class JsonNullKeyValue(msg: String) extends Throwable(msg) -class JsonUnsupportedType(msg: String) extends Throwable(msg) -class JsonConfigError(msg: String) extends Throwable(msg) -class JsonEitherLeftError(msg: String) extends Throwable(msg) +import scala.util.control.NoStackTrace -class ParseError(val msg: String) extends Throwable(msg): +class JsonIllegalKeyType(msg: String) extends Throwable(msg) with NoStackTrace +class JsonNullKeyValue(msg: String) extends Throwable(msg) with NoStackTrace +class JsonUnsupportedType(msg: String) extends Throwable(msg) with NoStackTrace +class JsonConfigError(msg: String) extends Throwable(msg) with NoStackTrace +class JsonEitherLeftError(msg: String) extends Throwable(msg) with NoStackTrace + +class ParseError(val msg: String) extends Throwable(msg) with NoStackTrace: val show: String = "" // Thrown at compile-time only! -case class JsonTypeError(override val msg: String) extends ParseError(msg): +case class JsonTypeError(override val msg: String) extends ParseError(msg) with NoStackTrace: override val show: String = "" // Thrown at runtime only! -case class JsonParseError(override val msg: String, context: reading.JsonSource) extends ParseError(msg + " at position " + context.pos): +case class JsonParseError(override val msg: String, context: reading.JsonSource) extends ParseError(msg + " at position " + context.pos) with NoStackTrace: override val show: String = val js = context.js.toString val (clip, dashes) = context.pos match { diff --git a/src/main/scala/co.blocke.scalajack/json/exp/JsonReader.scala b/src/main/scala/co.blocke.scalajack/json/exp/JsonReader.scala new file mode 100644 index 00000000..bb5ad981 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/exp/JsonReader.scala @@ -0,0 +1,440 @@ +package co.blocke.scalajack +package json +package exp + +import scala.annotation.{switch, tailrec} +import java.nio.ByteBuffer +import java.io.InputStream +import java.nio.charset.StandardCharsets.UTF_8 +import scala.specialized + +/* +Bottom Line: + Fancy string reading, ByteBuffer handling, etc. did NOT have a material effect on speed. + The SJ way was either equal to, or faster than, the JsonReader approach for reading strings! +*/ + +class JsonReader private[json]( + private[this] var buf: Array[Byte] = new Array[Byte](32768), + private[this] var head: Int = 0, + private[this] var tail: Int = 0, + private[this] var mark: Int = -1, + private[this] var charBuf: Array[Char] = new Array[Char](4096), + private[this] var bbuf: ByteBuffer = null, + private[this] var in: InputStream = null, + private[this] var totalRead: Long = 0 +): + + private[json] def read(s: String): String = { + val currBuf = this.buf + try { + this.buf = s.getBytes(UTF_8) + head = 0 + val to = buf.length + tail = to + totalRead = 0 + mark = -1 + readString("") + } finally { + this.buf = currBuf + } + } + + def readString(default: String): String = + if (isNextToken('"', head)) { + val pos = head + val len = parseString(0, Math.min(tail - pos, charBuf.length), charBuf, pos) + new String(charBuf, 0, len) + } else readNullOrTokenError(default, '"') + + @tailrec + private[this] def isNextToken(t: Byte, pos: Int): Boolean = + if (pos < tail) { + val b = buf(pos) + head = pos + 1 + b == t || ((b == ' ' || b == '\n' || (b | 0x4) == '\r') && nextToken(pos + 1) == t) + } else isNextToken(t, loadMoreOrError(pos)) + + @tailrec + private[this] def nextToken(pos: Int): Byte = + if (pos < tail) { + val b = buf(pos) + if (b == ' ' || b == '\n' || (b | 0x4) == '\r') nextToken(pos + 1) + else { + head = pos + 1 + b + } + } else nextToken(loadMoreOrError(pos)) + + @tailrec + private[this] def readNullOrTokenError[@specialized A](default: A, t: Byte): A = + if (default != null) { + val pos = head + if (pos != 0) { + if (pos + 2 < tail) { + val bs = ByteArrayAccess.getInt(buf, pos - 1) + if (bs == 0x6C6C756E) { + head = pos + 3 + default + } else tokenOrNullError(t, bs, pos) + } else if (buf(pos - 1) == 'n') { + head = loadMoreOrError(pos - 1) + 1 + readNullOrTokenError(default, t) + } else tokenOrNullError(t) + } else illegalTokenOperation() + } else tokenError(t) + + @tailrec + private[this] def parseString(i: Int, minLim: Int, charBuf: Array[Char], pos: Int): Int = + if (i + 3 < minLim) { // Based on SWAR routine of JSON string parsing: https://github.com/sirthias/borer/blob/fde9d1ce674d151b0fee1dd0c2565020c3f6633a/core/src/main/scala/io/bullet/borer/json/JsonParser.scala#L456 + val bs = ByteArrayAccess.getInt(buf, pos) + val m = ((bs - 0x20202020 ^ 0x3C3C3C3C) - 0x1010101 | (bs ^ 0x5D5D5D5D) + 0x1010101) & 0x80808080 + charBuf(i) = (bs & 0xFF).toChar + charBuf(i + 1) = (bs >> 8 & 0xFF).toChar + charBuf(i + 2) = (bs >> 16 & 0xFF).toChar + charBuf(i + 3) = (bs >> 24).toChar + if (m != 0) { + val offset = java.lang.Integer.numberOfTrailingZeros(m) >> 3 + if ((bs >> (offset << 3)).toByte == '"') { + head = pos + offset + 1 + i + offset + } else parseEncodedString(i + offset, charBuf.length - 1, charBuf, pos + offset) + } else parseString(i + 4, minLim, charBuf, pos + 4) + } else if (i < minLim) { + val b = buf(pos) + charBuf(i) = b.toChar + if (b == '"') { + head = pos + 1 + i + } else if ((b - 0x20 ^ 0x3C) <= 0) parseEncodedString(i, charBuf.length - 1, charBuf, pos) + else parseString(i + 1, minLim, charBuf, pos + 1) + } else if (pos >= tail) { + val newPos = loadMoreOrError(pos) + parseString(i, Math.min(charBuf.length, i + tail - newPos), charBuf, newPos) + } else parseString(i, Math.min(growCharBuf(i + 1), i + tail - pos), this.charBuf, pos) + + @tailrec + private[this] def parseEncodedString(i: Int, lim: Int, charBuf: Array[Char], pos: Int): Int = { + val remaining = tail - pos + if (i < lim) { + if (remaining > 0) { + val b1 = buf(pos) + if (b1 >= 0) { + if (b1 == '"') { + head = pos + 1 + i + } else if (b1 != '\\') { // 0aaaaaaa (UTF-8 byte) -> 000000000aaaaaaa (UTF-16 char) + if (b1 < ' ') unescapedControlCharacterError(pos) + charBuf(i) = b1.toChar + parseEncodedString(i + 1, lim, charBuf, pos + 1) + } else if (remaining > 1) { + val b2 = buf(pos + 1) + if (b2 != 'u') { + charBuf(i) = (b2: @switch) match { + case '"' => '"' + case 'n' => '\n' + case 'r' => '\r' + case 't' => '\t' + case 'b' => '\b' + case 'f' => '\f' + case '\\' => '\\' + case '/' => '/' + case _ => illegalEscapeSequenceError(pos + 1) + } + parseEncodedString(i + 1, lim, charBuf, pos + 2) + } else if (remaining > 5) { + val ch1 = readEscapedUnicode(pos + 2, buf) + charBuf(i) = ch1 + if (ch1 < 0xD800 || ch1 > 0xDFFF) parseEncodedString(i + 1, lim, charBuf, pos + 6) + else if (remaining > 11) { + if (buf(pos + 6) != '\\') illegalEscapeSequenceError(pos + 6) + if (buf(pos + 7) != 'u') illegalEscapeSequenceError(pos + 7) + val ch2 = readEscapedUnicode(pos + 8, buf) + if (ch1 >= 0xDC00 || ch2 < 0xDC00 || ch2 > 0xDFFF) decodeError("illegal surrogate character pair", pos + 11) + charBuf(i + 1) = ch2 + parseEncodedString(i + 2, lim, charBuf, pos + 12) + } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos)) + } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos)) + } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos)) + } else if ((b1 >> 5) == -2) { // 110bbbbb 10aaaaaa (UTF-8 bytes) -> 00000bbbbbaaaaaa (UTF-16 char) + if (remaining > 1) { + val b2 = buf(pos + 1) + if ((b1 & 0x1E) == 0 || (b2 & 0xC0) != 0x80) malformedBytesError(b1, b2, pos) + charBuf(i) = (b1 << 6 ^ b2 ^ 0xF80).toChar // 0xF80 == 0xC0.toByte << 6 ^ 0x80.toByte + parseEncodedString(i + 1, lim, charBuf, pos + 2) + } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos)) + } else if ((b1 >> 4) == -2) { // 1110cccc 10bbbbbb 10aaaaaa (UTF-8 bytes) -> ccccbbbbbbaaaaaa (UTF-16 char) + if (remaining > 2) { + val b2 = buf(pos + 1) + val b3 = buf(pos + 2) + val ch = (b1 << 12 ^ b2 << 6 ^ b3 ^ 0xFFFE1F80).toChar // 0xFFFE1F80 == 0xE0.toByte << 12 ^ 0x80.toByte << 6 ^ 0x80.toByte + if ((b1 == -32 && (b2 & 0xE0) == 0x80) || (b2 & 0xC0) != 0x80 || (b3 & 0xC0) != 0x80 || + (ch >= 0xD800 && ch <= 0xDFFF)) malformedBytesError(b1, b2, b3, pos) + charBuf(i) = ch + parseEncodedString(i + 1, lim, charBuf, pos + 3) + } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos)) + } else if ((b1 >> 3) == -2) { // 11110ddd 10ddcccc 10bbbbbb 10aaaaaa (UTF-8 bytes) -> 110110uuuuccccbb 110111bbbbaaaaaa (UTF-16 chars), where uuuu = ddddd - 1 + if (remaining > 3) { + val b2 = buf(pos + 1) + val b3 = buf(pos + 2) + val b4 = buf(pos + 3) + val cp = b1 << 18 ^ b2 << 12 ^ b3 << 6 ^ b4 ^ 0x381F80 // 0x381F80 == 0xF0.toByte << 18 ^ 0x80.toByte << 12 ^ 0x80.toByte << 6 ^ 0x80.toByte + if ((b2 & 0xC0) != 0x80 || (b3 & 0xC0) != 0x80 || (b4 & 0xC0) != 0x80 || + cp < 0x10000 || cp > 0x10FFFF) malformedBytesError(b1, b2, b3, b4, pos) + charBuf(i) = ((cp >>> 10) + 0xD7C0).toChar // 0xD7C0 == 0xD800 - (0x10000 >>> 10) + charBuf(i + 1) = ((cp & 0x3FF) + 0xDC00).toChar + parseEncodedString(i + 2, lim, charBuf, pos + 4) + } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos)) + } else malformedBytesError(b1, pos) + } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos)) + } else parseEncodedString(i, growCharBuf(i + 2) - 1, this.charBuf, pos) // 2 is length of surrogate pair + } + + private[this] def malformedBytesError(b1: Byte, pos: Int): Nothing = { + var i = appendString("malformed byte(s): 0x", 0) + i = appendHexByte(b1, i, hexDigits) + decodeError(i, pos, null) + } + + private[this] def malformedBytesError(b1: Byte, b2: Byte, pos: Int): Nothing = { + val ds = hexDigits + var i = appendString("malformed byte(s): 0x", 0) + i = appendHexByte(b1, i, ds) + i = appendString(", 0x", i) + i = appendHexByte(b2, i, ds) + decodeError(i, pos + 1, null) + } + + private[this] def malformedBytesError(b1: Byte, b2: Byte, b3: Byte, pos: Int): Nothing = { + val ds = hexDigits + var i = appendString("malformed byte(s): 0x", 0) + i = appendHexByte(b1, i, ds) + i = appendString(", 0x", i) + i = appendHexByte(b2, i, ds) + i = appendString(", 0x", i) + i = appendHexByte(b3, i, ds) + decodeError(i, pos + 2, null) + } + + private[this] def malformedBytesError(b1: Byte, b2: Byte, b3: Byte, b4: Byte, pos: Int): Nothing = { + val ds = hexDigits + var i = appendString("malformed byte(s): 0x", 0) + i = appendHexByte(b1, i, ds) + i = appendString(", 0x", i) + i = appendHexByte(b2, i, ds) + i = appendString(", 0x", i) + i = appendHexByte(b3, i, ds) + i = appendString(", 0x", i) + i = appendHexByte(b4, i, ds) + decodeError(i, pos + 3, null) + } + + private[this] def appendHexByte(b: Byte, i: Int, ds: Array[Char]): Int = { + ensureCharBufCapacity(i + 2) + charBuf(i) = ds(b >> 4 & 0xF) + charBuf(i + 1) = ds(b & 0xF) + i + 2 + } + + private[this] def appendString(s: String, i: Int): Int = { + val len = s.length + val required = i + len + ensureCharBufCapacity(required) + s.getChars(0, len, charBuf, i) + required + } + + private[this] def ensureCharBufCapacity(required: Int): Unit = + if (charBuf.length < required) growCharBuf(required): Unit + + private final val hexDigits: Array[Char] = + Array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f') + + private[this] def decodeError(msg: String, pos: Int, cause: Throwable = null): Nothing = + decodeError(appendString(msg, 0), pos, cause) + + private[this] def decodeError(from: Int, pos: Int, cause: Throwable): Nothing = { + var i = appendString(", offset: 0x", from) + val offset = + if ((bbuf eq null) && (in eq null)) 0 + else totalRead - tail + i = appendHexOffset(offset + pos, i) + // if (config.appendHexDumpToParseException) { + // i = appendString(", buf:", i) + // i = appendHexDump(pos, offset.toInt, i) + // } + throw new JsonReaderException(new String(charBuf, 0, i), cause, true) //config.throwReaderExceptionWithStackTrace) + } + + private[this] def appendHexOffset(d: Long, i: Int): Int = { + ensureCharBufCapacity(i + 16) + val ds = hexDigits + var j = i + val dl = d.toInt + if (dl != d) { + val dh = (d >> 32).toInt + var shift = 32 - java.lang.Integer.numberOfLeadingZeros(dh) & 0x1C + while (shift >= 0) { + charBuf(j) = ds(dh >> shift & 0xF) + shift -= 4 + j += 1 + } + } + putHexInt(dl, j, charBuf, ds) + j + 8 + } + + private[this] def putHexInt(d: Int, i: Int, charBuf: Array[Char], ds: Array[Char]): Unit = { + charBuf(i) = ds(d >>> 28) + charBuf(i + 1) = ds(d >> 24 & 0xF) + charBuf(i + 2) = ds(d >> 20 & 0xF) + charBuf(i + 3) = ds(d >> 16 & 0xF) + charBuf(i + 4) = ds(d >> 12 & 0xF) + charBuf(i + 5) = ds(d >> 8 & 0xF) + charBuf(i + 6) = ds(d >> 4 & 0xF) + charBuf(i + 7) = ds(d & 0xF) + } + + private[this] def loadMoreOrError(pos: Int): Int = { + if ((bbuf eq null) && (in eq null)) endOfInputError() + loadMore(pos, throwOnEndOfInput = true) + } + + private[this] def loadMore(pos: Int): Int = + if ((bbuf eq null) && (in eq null)) pos + else loadMore(pos, throwOnEndOfInput = false) + + private[this] def loadMore(pos: Int, throwOnEndOfInput: Boolean): Int = { + var newPos = pos + val offset = + if (mark < 0) pos + else mark + if (offset > 0) { + newPos -= offset + val buf = this.buf + val remaining = tail - offset + var i = 0 + while (i < remaining) { + buf(i) = buf(i + offset) + i += 1 + } + if (mark > 0) mark = 0 + tail = remaining + head = newPos + } else growBuf() + var len = buf.length - tail + if (bbuf ne null) { + len = Math.min(bbuf.remaining, len) + bbuf.get(buf, tail, len) + } else len = Math.max(in.read(buf, tail, len), 0) + if (throwOnEndOfInput && len == 0) endOfInputError() + tail += len + totalRead += len + newPos + } + + private[json] def endOfInputOrError(): Unit = + if (skipWhitespaces()) decodeError("expected end of input", head) + + private[this] def endOfInputError(): Nothing = decodeError("unexpected end of input", tail) + private[this] def illegalEscapeSequenceError(pos: Int): Nothing = decodeError("illegal escape sequence", pos) + private[this] def unescapedControlCharacterError(pos: Int): Nothing = decodeError("unescaped control character", pos) + @tailrec + private[this] def hexDigitError(pos: Int): Nothing = { + if (nibbles(buf(pos) & 0xFF) < 0) decodeError("expected hex digit", pos) + hexDigitError(pos + 1) + } + private[this] def tokenError(t: Byte, pos: Int = head - 1): Nothing = { + var i = appendString("expected '", 0) + i = appendChar(t.toChar, i) + i = appendChar('\'', i) + decodeError(i, pos, null) + } + private[this] def tokenOrNullError(t: Byte, bs: Int, pos: Int): Nothing = tokenOrNullError(t, { + val b0 = bs.toByte + val b1 = (bs >> 8).toByte + val b2 = (bs >> 16).toByte + pos + + (if (b0 != 'n') -1 + else if (b1 != 'u') 0 + else if (b2 != 'l') 1 + else 2) + }) + private[this] def tokenOrNullError(t: Byte, pos: Int = head - 1): Nothing = { + var i = appendString("expected '", 0) + i = appendChar(t.toChar, i) + i = appendString("' or null", i) + decodeError(i, pos, null) + } + private[this] def illegalTokenOperation(): Nothing = + throw new IllegalStateException("expected preceding call of 'nextToken()' or 'isNextToken()'") + + private[this] def appendChar(ch: Char, i: Int): Int = { + ensureCharBufCapacity(i + 1) + charBuf(i) = ch + i + 1 + } + + private[json] def skipWhitespaces(): Boolean = { + var pos = head + var buf = this.buf + while ((pos < tail || { + pos = loadMore(pos) + buf = this.buf + pos < tail + }) && { + val b = buf(pos) + b == ' ' || b == '\n' || (b | 0x4) == '\r' + }) pos += 1 + head = pos + pos != tail + } + + private[this] def growBuf(): Unit = { + var bufLen = buf.length + // val maxBufSize = config.maxBufSize + // if (bufLen == maxBufSize) tooLongInputError() + bufLen <<= 1 + // if (bufLen > maxBufSize || bufLen < 0) bufLen = maxBufSize + buf = java.util.Arrays.copyOf(buf, bufLen) + } + + private[this] def growCharBuf(required: Int): Int = { + var charBufLen = charBuf.length + // val maxCharBufSize = config.maxCharBufSize + // if (charBufLen == maxCharBufSize) tooLongStringError() + charBufLen = (-1 >>> Integer.numberOfLeadingZeros(charBufLen | required)) + 1 + // if (charBufLen > maxCharBufSize || charBufLen < 0) charBufLen = maxCharBufSize + charBuf = java.util.Arrays.copyOf(charBuf, charBufLen) + charBufLen + } + + private[this] def readEscapedUnicode(pos: Int, buf: Array[Byte]): Char = { + val ns = nibbles + val x = + ns(buf(pos) & 0xFF) << 12 | + ns(buf(pos + 1) & 0xFF) << 8 | + ns(buf(pos + 2) & 0xFF) << 4 | + ns(buf(pos + 3) & 0xFF) + if (x < 0) hexDigitError(pos) + x.toChar + } + + private final val nibbles: Array[Byte] = Array( + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 + ) \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json/exp/JsonReaderException.scala b/src/main/scala/co.blocke.scalajack/json/exp/JsonReaderException.scala new file mode 100644 index 00000000..a910deb0 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/exp/JsonReaderException.scala @@ -0,0 +1,6 @@ +package co.blocke.scalajack +package json +package exp + +class JsonReaderException private[json](msg: String, cause: Throwable, withStackTrace: Boolean) + extends RuntimeException(msg, cause, true, withStackTrace) \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json/exp/package.scala b/src/main/scala/co.blocke.scalajack/json/exp/package.scala new file mode 100644 index 00000000..9717f4f1 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/exp/package.scala @@ -0,0 +1,15 @@ +package co.blocke.scalajack +package json + +import java.nio.ByteBuffer +import scala.{specialized => sp} + +package object exp { + + private[this] final val readerPool: ThreadLocal[JsonReader] = new ThreadLocal[JsonReader] { + override def initialValue(): JsonReader = new JsonReader + } + + def readFromString(s: String): String = + readerPool.get.read(s) +} diff --git a/src/main/scala/co.blocke.scalajack/json/reading/ClassDecoder.scala b/src/main/scala/co.blocke.scalajack/json/reading/ClassDecoder.scala index ff12cf56..00f10666 100644 --- a/src/main/scala/co.blocke.scalajack/json/reading/ClassDecoder.scala +++ b/src/main/scala/co.blocke.scalajack/json/reading/ClassDecoder.scala @@ -31,3 +31,18 @@ object ClassDecoder: // Construct the new object instantiator(fieldValues) } + + /* + +ClassDecoder.apply[Friend]( + Array[String]("name", "age", "email"), + List[JsonDecoder[_]]( + JsonDecoder.string, + JsonDecoder.int, + JsonDecoder.string + ).toArray, + ((fieldValues: scala.Array[_]) => new Friend(fieldValues.apply(0).asInstanceOf[java.lang.String], fieldValues.apply(1).asInstanceOf[scala.Int], fieldValues.apply(2).asInstanceOf[java.lang.String])), + scala.List.apply[scala.Any](0, 0, 0).toArray[scala.Any](scala.reflect.ClassTag.Any) + ) + + */ diff --git a/src/main/scala/co.blocke.scalajack/json/reading/JsonReader.scala b/src/main/scala/co.blocke.scalajack/json/reading/JsonReader.scalax similarity index 90% rename from src/main/scala/co.blocke.scalajack/json/reading/JsonReader.scala rename to src/main/scala/co.blocke.scalajack/json/reading/JsonReader.scalax index a8a12267..a3410384 100644 --- a/src/main/scala/co.blocke.scalajack/json/reading/JsonReader.scala +++ b/src/main/scala/co.blocke.scalajack/json/reading/JsonReader.scalax @@ -48,7 +48,7 @@ object JsonReader: case '[t] => r.elementRef.refType match case '[e] => - val elemDecoder = Expr.summon[JsonDecoder[e]].getOrElse(refRead[e](r.elementRef.asInstanceOf[RTypeRef[e]])) + val elemDecoder = Expr.summon[JsonDecoder[e]].getOrElse(refRead2[e](r.elementRef.asInstanceOf[RTypeRef[e]])) '{ JsonDecoder.seq[e]($elemDecoder) map (_.to(${ Expr.summon[Factory[e, T]].get })) } @@ -59,7 +59,7 @@ object JsonReader: r.fields.map(f => f.fieldRef.refType match case '[e] => - Expr.summon[JsonDecoder[e]].getOrElse(refRead[e](f.fieldRef.asInstanceOf[RTypeRef[e]])) + Expr.summon[JsonDecoder[e]].getOrElse(refRead2[e](f.fieldRef.asInstanceOf[RTypeRef[e]])) ) ) val instantiator = JsonReaderUtil.classInstantiator[T](r.asInstanceOf[ClassRef[T]]) @@ -88,4 +88,7 @@ object JsonReader: else Expr(null.asInstanceOf[Int]) }) - '{ ClassDecoder[T]($fieldNames, $fieldDecoders.toArray, $instantiator, $preloaded.toArray) } + val x = '{ ClassDecoder[T]($fieldNames, $fieldDecoders.toArray, $instantiator, $preloaded.toArray) } + println(s"Class Decoder: ${x.show}") + println("---------------------") + x diff --git a/src/main/scala/co.blocke.scalajack/json/reading/JsonReader.scalax2 b/src/main/scala/co.blocke.scalajack/json/reading/JsonReader.scalax2 new file mode 100644 index 00000000..e72bc0b1 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/reading/JsonReader.scalax2 @@ -0,0 +1,114 @@ +package co.blocke.scalajack +package json +package reading + +import scala.annotation.* + +object JsonReader: + protected val ull: Array[Char] = "ull".toCharArray + protected val alse: Array[Char] = "alse".toCharArray + protected val rue: Array[Char] = "rue".toCharArray + +case class JsonReader(src: JsonSource): + + private var expectFieldValue = false + + // returns false if 'null' found + def expectObjectStart(): Boolean = + src.readSkipWhitespace() match { + case '{' => true + case 'n' => + readChars(JsonReader.ull, "null") + false + case c => throw new JsonParseError(s"Expected object start '{' but found '$c'", src) + } + + def expectArrayStart(): Boolean = + src.readSkipWhitespace() match { + case '[' => true + case 'n' => + readChars(JsonReader.ull, "null") + false + case c => throw new JsonParseError(s"Expected array start '[' but found '$c'", src) + } + + // True if we got a comma, and False for ] + def nextArrayElement(): Boolean = + (src.readSkipWhitespace(): @switch) match + case ',' => true + case ']' => false + case c => throw JsonParseError(s"Expected ',' or ']' got '$c'", src) + + // True if we got a comma, and False for } + def nextField(): Boolean = + (src.readSkipWhitespace(): @switch) match { + case ',' => + expectFieldValue = false + true + case '}' if !expectFieldValue => false + case '}' => + throw JsonParseError("Expected field value but got '}' instead.", src) + case c => + throw JsonParseError(s"expected ',' or '}' got '$c'", src) + } + + inline def expectFieldName(): CharSequence = + val charseq = expectString() + expectFieldValue = true + charseq + + // need flavor of expectString that might be null for field values + + private def expectString(): CharSequence = + charWithWS(src, '"') + val sb = new FastStringBuilder(64) + while true do + val c = src.readEscapedString() + if c == END_OF_STRING then return sb.buffer // mutable thing escapes, but cannot be changed + sb.append(c.toChar) + throw JsonParseError("Invalid string value detected", src) + + inline def expectChar(): Char = + expectString() match { + case s if s.length == 1 => s.charAt(0) + case s => throw new JsonParseError(s"Expected a Char value but got '$s'", src) + } + + def expectBoolean(): Boolean = + (src.readSkipWhitespace(): @switch) match + case 't' => + readChars(JsonReader.rue, "true") + true + case 'f' => + readChars(JsonReader.alse, "false") + false + case c => throw JsonParseError(s"Expected 'true' or 'false' got '$c'", src) + + def expectInt(): Int = + checkNumber() + try { + val i = UnsafeNumbers.int_(src, false) + src.retract() + i + } catch { + case UnsafeNumbers.UnsafeNumber => throw JsonParseError("Expected an Int", src) + } + + private inline def readChars( + expect: Array[Char], + errMsg: String + ): Unit = + var i: Int = 0 + while i < expect.length do + if src.read() != expect(i) then throw JsonParseError(s"Expected $errMsg", src) + i += 1 + + private def checkNumber(): Unit = + (src.readSkipWhitespace(): @switch) match + case '-' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '.' => () + case c => throw JsonParseError(s"Expected a number, got $c", src) + src.retract() + + inline def charWithWS(in: JsonSource, c: Char): Unit = + val got = in.readSkipWhitespace() + if got != c then throw JsonParseError(s"Expected '$c' got '$got'", src) diff --git a/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala b/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala index fb90eac4..562aa20a 100644 --- a/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala +++ b/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala @@ -4,18 +4,27 @@ package reading import scala.annotation.* +object JsonSource: + protected val ull: Array[Char] = "ull".toCharArray + protected val alse: Array[Char] = "alse".toCharArray + protected val rue: Array[Char] = "rue".toCharArray + // ZIO-Json defines a series of different Readers. Not exactly sure why--maybe to support different // modes (streaming, ...)? At least for now we only need one, so merged key bits of Readers into one. case class JsonSource(js: CharSequence): private var i = 0 + private var expectFieldValue = false private[json] val max = js.length def pos = i + def here = js.charAt(i) + inline def read(): Char = if i < max then + val c = history(i) i += 1 - history(i - 1) + c else BUFFER_EXCEEDED inline def readSkipWhitespace(): Char = @@ -68,3 +77,120 @@ case class JsonSource(js: CharSequence): accum = accum * 16 + c i += 1 accum.toChar + +//------- + + // returns false if 'null' found + def expectObjectStart(): Boolean = + readSkipWhitespace() match { + case '{' => + true + case 'n' => + readChars(JsonSource.ull, "null") + false + case c => throw new JsonParseError(s"Expected object start '{' but found '$c'", this) + } + + def expectArrayStart(): Boolean = + readSkipWhitespace() match { + case '[' => + true + case 'n' => + readChars(JsonSource.ull, "null") + false + case c => throw new JsonParseError(s"Expected array start '[' but found '$c'", this) + } + + // True if we got a comma, and False for ] + def nextArrayElement(): Boolean = + (readSkipWhitespace(): @switch) match + case ',' => + true + case ']' => + false + case c => throw JsonParseError(s"Expected ',' or ']' got '$c'", this) + + // True if we got a comma, and False for } + def nextField(): Boolean = + (readSkipWhitespace(): @switch) match { + case ',' => + expectFieldValue = false + true + case '}' if !expectFieldValue => + false + case '}' => + throw JsonParseError("Expected field value but got '}' instead.", this) + case c => + throw JsonParseError(s"expected ',' or '}' got '$c'", this) + } + + inline def expectFieldName(): CharSequence = + val charseq = parseString() + expectFieldValue = true + charseq + + // Value might be null! + def expectString(): CharSequence = + readSkipWhitespace() match { + case '"' => + retract() + parseString() + case 'n' => + readChars(JsonSource.ull, "null") + null + case c => throw new JsonParseError(s"Expected a String value but got '$c'", this) + } + + private def parseString(): CharSequence = + charWithWS('"') + val sb = new FastStringBuilder(64) + while true do + val c = readEscapedString() + if c == END_OF_STRING then return sb.buffer // mutable thing escapes, but cannot be changed + sb.append(c.toChar) + throw JsonParseError("Invalid string value detected", this) + + inline def expectChar(): Char = + expectString() match { + case s if s.length == 1 => s.charAt(0) + case s => throw new JsonParseError(s"Expected a Char value but got '$s'", this) + } + + def expectBoolean(): Boolean = + (readSkipWhitespace(): @switch) match + case 't' => + readChars(JsonSource.rue, "true") + true + case 'f' => + readChars(JsonSource.alse, "false") + false + case c => throw JsonParseError(s"Expected 'true' or 'false' got '$c'", this) + + def expectInt(): Int = + checkNumber() + try { + val i = UnsafeNumbers.int_(this, false) + retract() + i + } catch { + case UnsafeNumbers.UnsafeNumber => throw JsonParseError("Expected an Int", this) + } + + private inline def readChars( + expect: Array[Char], + errMsg: String + ): Unit = + var i: Int = 0 + while i < expect.length do + if read() != expect(i) then throw JsonParseError(s"Expected $errMsg", this) + i += 1 + + private def checkNumber(): Unit = + (readSkipWhitespace(): @switch) match + case '-' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '.' => () + case c => throw JsonParseError(s"Expected a number, got $c", this) + retract() + + inline def charWithWS(c: Char): Unit = + val got = readSkipWhitespace() + if got != c then throw JsonParseError(s"Expected '$c' got '$got'", this) diff --git a/src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala b/src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala index 7385b467..d6ea82b5 100644 --- a/src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala +++ b/src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala @@ -6,8 +6,10 @@ import co.blocke.scala_reflection.{RTypeRef, TypedName} import co.blocke.scala_reflection.reflect.ReflectOnType import co.blocke.scala_reflection.reflect.rtypeRefs.* import co.blocke.scala_reflection.rtypes.{EnumRType, JavaClassRType, NonConstructorFieldInfo} +import reading.JsonSource import scala.jdk.CollectionConverters.* import scala.quoted.* +import scala.collection.Factory import dotty.tools.dotc.ast.Trees.EmptyTree import org.apache.commons.text.StringEscapeUtils import org.apache.commons.lang3.text.translate.CharSequenceTranslator @@ -19,25 +21,26 @@ object JsonCodecMaker: // Cache generated method Symbols + an array of the generated functions (DefDef) case class MethodKey(ref: RTypeRef[?], isStringified: Boolean) // <-- TODO: Not clear what isStringified does here... - val methodSyms = new scala.collection.mutable.HashMap[MethodKey, Symbol] - val methodDefs = new scala.collection.mutable.ArrayBuffer[DefDef] + + val writeMethodSyms = new scala.collection.mutable.HashMap[MethodKey, Symbol] + val writeMethodDefs = new scala.collection.mutable.ArrayBuffer[DefDef] // Fantastic Dark Magic here--lifted from Jasoniter. Props! This thing will create a DefDef, and a Symbol to it. // The Symbol will let you call the generated function later from other macro-generated code. The goal is to use // generated functions to create cleaner/faster macro code than what straight quotes/splices would create unaided. - def makeFn[U: Type](methodKey: MethodKey, arg: Expr[U], out: Expr[JsonOutput])(f: (Expr[U], Expr[JsonOutput]) => Expr[Unit]): Expr[Unit] = + def makeWriteFn[U: Type](methodKey: MethodKey, arg: Expr[U], out: Expr[JsonOutput])(f: (Expr[U], Expr[JsonOutput]) => Expr[Unit]): Expr[Unit] = // Get a symbol, if one already created for this key... else make one. Apply( Ref( - methodSyms.getOrElse( + writeMethodSyms.getOrElse( methodKey, { val sym = Symbol.newMethod( Symbol.spliceOwner, - "w" + methodSyms.size, // 'w' is for Writer! + "w" + writeMethodSyms.size, // 'w' is for Writer! MethodType(List("in", "out"))(_ => List(TypeRepr.of[U], TypeRepr.of[JsonOutput]), _ => TypeRepr.of[Unit]) ) - methodSyms.update(methodKey, sym) - methodDefs += DefDef( + writeMethodSyms.update(methodKey, sym) + writeMethodDefs += DefDef( sym, params => { val List(List(in, out)) = params @@ -51,6 +54,39 @@ object JsonCodecMaker: List(arg.asTerm, out.asTerm) ).asExprOf[Unit] + val readMethodSyms = new scala.collection.mutable.HashMap[MethodKey, Symbol] + val readMethodDefs = new scala.collection.mutable.ArrayBuffer[DefDef] + + class JsonReader // temporary to compile until we write the real JsonReader + + def makeReadFn[U: Type](methodKey: MethodKey, arg: Expr[U], in: Expr[JsonReader])(f: (Expr[JsonReader], Expr[U]) => Expr[U])(using Quotes)(using Type[JsonReader]): Expr[U] = + methodKey.ref.refType match + case '[tt] => + val typerepr = TypeRepr.of[tt] + Apply( + Ref( + readMethodSyms.getOrElse( + methodKey, { + val sym = Symbol.newMethod( + Symbol.spliceOwner, + "r" + readMethodSyms.size, + MethodType(List("in", "default"))(_ => List(TypeRepr.of[JsonReader], typerepr), _ => TypeRepr.of[U]) + ) + readMethodSyms.update(methodKey, sym) + readMethodDefs += DefDef( + sym, + params => { + val List(List(in, default)) = params + Some(f(in.asExprOf[JsonReader], default.asExprOf[U]).asTerm.changeOwner(sym)) + } + ) + sym + } + ) + ), + List(in.asTerm, arg.asTerm) + ).asExprOf[U] + // --------------------------------------------------------------------------------------------- def maybeWrite[T](label: String, aE: Expr[T], ref: RTypeRef[T], out: Expr[JsonOutput], cfg: JsonConfig): Expr[Unit] = @@ -205,12 +241,12 @@ object JsonCodecMaker: // --------------------------------------------------------------------------------------------- - def genFnBody[T](r: RTypeRef[?], aE: Expr[T], out: Expr[JsonOutput], emitDiscriminator: Boolean = false, inTuple: Boolean = false)(using Quotes): Expr[Unit] = + def genEncFnBody[T](r: RTypeRef[?], aE: Expr[T], out: Expr[JsonOutput], emitDiscriminator: Boolean = false, inTuple: Boolean = false)(using Quotes): Expr[Unit] = r.refType match case '[b] => r match case t: ArrayRef[?] => - makeFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => + makeWriteFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => t.elementRef.refType match case '[e] => val tin = in.asInstanceOf[Expr[Array[e]]] @@ -226,7 +262,7 @@ object JsonCodecMaker: } case t: SeqRef[?] => - makeFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => + makeWriteFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => t.elementRef.refType match case '[e] => val tin = if t.isMutable then in.asExprOf[scala.collection.mutable.Seq[e]] else in.asExprOf[Seq[e]] @@ -242,7 +278,7 @@ object JsonCodecMaker: } case t: SetRef[?] => - makeFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => + makeWriteFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => t.elementRef.refType match case '[e] => val tin = if t.isMutable then in.asExprOf[scala.collection.mutable.Set[e]] else in.asExprOf[Set[e]] @@ -270,12 +306,12 @@ object JsonCodecMaker: case '[c] => val subtype = TypeIdent(TypeRepr.of[c].typeSymbol) val sym = Symbol.newBind(Symbol.spliceOwner, "t", Flags.EmptyFlags, subtype.tpe) - CaseDef(Bind(sym, Typed(Ref(sym), subtype)), None, genFnBody[c](child, Ref(sym).asExprOf[c], out, true).asTerm) + CaseDef(Bind(sym, Typed(Ref(sym), subtype)), None, genEncFnBody[c](child, Ref(sym).asExprOf[c], out, true).asTerm) } :+ CaseDef(Literal(NullConstant()), None, '{ $out.burpNull() }.asTerm) val matchExpr = Match(aE.asTerm, cases).asExprOf[Unit] matchExpr - // We don't use makeFn here because a value class is basically just a "box" around a simple type + // We don't use makeWriteFn here because a value class is basically just a "box" around a simple type case t: ScalaClassRef[?] if t.isValueClass => val theField = t.fields.head.fieldRef theField.refType match @@ -284,7 +320,7 @@ object JsonCodecMaker: genWriteVal(fieldValue, theField.asInstanceOf[RTypeRef[e]], out) case t: ScalaClassRef[?] => - makeFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => + makeWriteFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => val body = { val eachField = t.fields.map { f => f.fieldRef.refType match @@ -339,7 +375,7 @@ object JsonCodecMaker: } case t: MapRef[?] => - makeFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => + makeWriteFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => t.elementRef.refType match case '[k] => t.elementRef2.refType match @@ -359,7 +395,7 @@ object JsonCodecMaker: } case t: JavaCollectionRef[?] => - makeFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => + makeWriteFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => t.elementRef.refType match case '[e] => val tin = in.asExprOf[java.util.Collection[_]] @@ -375,7 +411,7 @@ object JsonCodecMaker: } case t: JavaMapRef[?] => - makeFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => + makeWriteFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => t.elementRef.refType match case '[k] => t.elementRef2.refType match @@ -395,7 +431,7 @@ object JsonCodecMaker: } case t: JavaClassRef[?] => - makeFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => + makeWriteFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => t.refType match case '[p] => val rtype = t.expr.asExprOf[JavaClassRType[p]] @@ -443,12 +479,12 @@ object JsonCodecMaker: case '[c] => val subtype = TypeIdent(TypeRepr.of[c].typeSymbol) val sym = Symbol.newBind(Symbol.spliceOwner, "t", Flags.EmptyFlags, subtype.tpe) - CaseDef(Bind(sym, Typed(Ref(sym), subtype)), None, genFnBody[c](child, Ref(sym).asExprOf[c], out, true).asTerm) + CaseDef(Bind(sym, Typed(Ref(sym), subtype)), None, genEncFnBody[c](child, Ref(sym).asExprOf[c], out, true).asTerm) } :+ CaseDef(Literal(NullConstant()), None, '{ $out.burpNull() }.asTerm) val matchExpr = Match(aE.asTerm, cases).asExprOf[Unit] matchExpr - // No makeFn here--Option is just a wrapper to the real thingy + // No makeWriteFn here--Option is just a wrapper to the real thingy case t: OptionRef[?] => t.optionParamType.refType match case '[e] => @@ -467,20 +503,20 @@ object JsonCodecMaker: ${ genWriteVal[e]('{ vv }, t.optionParamType.asInstanceOf[RTypeRef[e]], out) } } - // No makeFn here... SelfRef is referring to something we've already seen before. There absolutely should already be a geneated + // No makeWriteFn here... SelfRef is referring to something we've already seen before. There absolutely should already be a geneated // and cached function for this thing that we can call. case t: SelfRefRef[?] => t.refType match case '[e] => val key = MethodKey(ReflectOnType[e](q)(TypeRepr.of[e])(using scala.collection.mutable.Map.empty[TypedName, Boolean]), false) - val sym = methodSyms(key) + val sym = writeMethodSyms(key) val tin = aE.asExprOf[b] '{ if $tin == null then $out.burpNull() else ${ Ref(sym).appliedTo(tin.asTerm, out.asTerm).asExprOf[Unit] } } - // No makeFn here. All LeftRight types (Either, Union, Intersection) are just type wrappers + // No makeWriteFn here. All LeftRight types (Either, Union, Intersection) are just type wrappers case t: LeftRightRef[?] => val tin = aE.asExprOf[b] t.leftRef.refType match @@ -550,7 +586,7 @@ object JsonCodecMaker: ${ genWriteVal[lt]('{ $tin.asInstanceOf[lt] }, t.leftRef.asInstanceOf[RTypeRef[lt]], out, inTuple = inTuple) } } - // No makeFn here. Try is just a wrapper + // No makeWriteFn here. Try is just a wrapper case t: TryRef[?] => t.tryRef.refType match case '[e] => @@ -573,7 +609,7 @@ object JsonCodecMaker: } case t: TupleRef[?] => - makeFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => + makeWriteFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => '{ if $in == null then $out.burpNull() else @@ -601,14 +637,12 @@ object JsonCodecMaker: def genWriteVal[T: Type]( aE: Expr[T], ref: RTypeRef[T], - // optWriteDiscriminator: Option[WriteDiscriminator], out: Expr[JsonOutput], - // cfgE: Expr[JsonConfig], isStringified: Boolean = false, // e.g. Map key values. Doesn't apply to stringish values, which are always quotes-wrapped inTuple: Boolean = false )(using Quotes): Expr[Unit] = val methodKey = MethodKey(ref, false) - methodSyms + writeMethodSyms .get(methodKey) .map { sym => // hit cache first... then match on Ref type Apply(Ref(sym), List(aE.asTerm, out.asTerm)).asExprOf[Unit] @@ -752,9 +786,80 @@ object JsonCodecMaker: // Everything else... case _ if isStringified => throw new JsonIllegalKeyType("Non-primitive/non-simple types cannot be map keys") - case _ => genFnBody(ref, aE, out, inTuple = inTuple) + case _ => genEncFnBody(ref, aE, out, inTuple = inTuple) ) + // --------------------------------------------------------------------------------------------- + + def genReadVal[T: Type]( + // default: Expr[T], // needed? This should already be in ref... + ref: RTypeRef[T], + in: Expr[JsonSource], + isStringified: Boolean = false, // e.g. Map key values. Doesn't apply to stringish values, which are always quotes-wrapped + inTuple: Boolean = false // not sure if needed... + )(using Quotes): Expr[T] = + val methodKey = MethodKey(ref, false) + // Stuff here to hit readMethodSyms first -- TBD + ref match + // First cover all primitive and simple types... + // case t: BigDecimalRef => + // if isStringified then '{ $out.valueStringified(${ aE.asExprOf[scala.math.BigDecimal] }) } + // else '{ $out.value(${ aE.asExprOf[scala.math.BigDecimal] }) } + // case t: BigIntRef => + // if isStringified then '{ $out.valueStringified(${ aE.asExprOf[scala.math.BigInt] }) } + // else '{ $out.value(${ aE.asExprOf[scala.math.BigInt] }) } + case t: BooleanRef => + '{ $in.expectBoolean() } + // if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Boolean] }) } + // else '{ $out.value(${ aE.asExprOf[Boolean] }) } + // case t: ByteRef => + // if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Byte] }) } + // else '{ $out.value(${ aE.asExprOf[Byte] }) } + // case t: CharRef => '{ $out.value(${ aE.asExprOf[Char] }) } + // case t: DoubleRef => + // if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Double] }) } + // else '{ $out.value(${ aE.asExprOf[Double] }) } + // case t: FloatRef => + // if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Float] }) } + // else '{ $out.value(${ aE.asExprOf[Float] }) } + case t: IntRef => + '{ $in.expectInt() } + // if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Int] }) } + // else '{ $out.value(${ aE.asExprOf[Int] }) } + // case t: LongRef => + // if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Long] }) } + // else '{ $out.value(${ aE.asExprOf[Long] }) } + // case t: ShortRef => + // if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Short] }) } + // else '{ $out.value(${ aE.asExprOf[Short] }) } + case t: StringRef => + '{ Option($in.expectString()).map(_.toString).getOrElse(null) } + // if cfg.escapedStrings then '{ $out.value(StringEscapeUtils.escapeJson(${ aE.asExprOf[String] })) } + // else '{ $out.value(${ aE.asExprOf[String] }) } + + case _ => + ref.refType match + case '[b] => + ref match + case t: SeqRef[?] => + t.elementRef.refType match + case '[e] => + '{ + if ! $in.expectArrayStart() then null.asInstanceOf[T] + else if $in.here == ']' then // empty Seq + $in.read() // skip the ']' + List.empty[e].to(${ Expr.summon[Factory[e, T]].get }) // create appropriate Seq[T] here + else + val acc = scala.collection.mutable.ListBuffer.empty[e] + acc.addOne(${ genReadVal[e](t.elementRef.asInstanceOf[RTypeRef[e]], in) }) + while $in.nextArrayElement() do acc.addOne(${ genReadVal[e](t.elementRef.asInstanceOf[RTypeRef[e]], in) }) + acc.to(${ Expr.summon[Factory[e, T]].get }) // create appropriate Seq[T] here + } + + // case _ => genDecFnBody(ref, in, inTuple = inTuple) + + // --------------------------------------------------------------------------------------------- + // ================================================================ // You've made it this far! Ok, now we sew everything together. // We generate a codec class and then kick off a deep traversal of @@ -771,11 +876,12 @@ object JsonCodecMaker: // } def encodeValue(in: T, out: JsonOutput): Unit = ${ genWriteVal('in, ref, 'out) } + def decodeValue(in: JsonSource): T = ${ genReadVal(ref, 'in) } } }.asTerm val neededDefs = // others here??? Refer to Jsoniter file JsonCodecMaker.scala - methodDefs + writeMethodDefs val codec = Block(neededDefs.toList, codecDef).asExprOf[JsonCodec[T]] // println(s"Codec: ${codec.show}") codec diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index ec8aee08..72e942a4 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -1,65 +1,52 @@ package co.blocke.scalajack +package json package run import co.blocke.scala_reflection.* import scala.jdk.CollectionConverters.* import scala.reflect.ClassTag import json.* +import scala.collection.immutable.Queue -object RunMe extends App: - - // import scala.util.Random - // val random = new Random() - - // def scramble(hash: Int): String = - // val last5 = f"$hash%05d".takeRight(5) - // val digits = (1 to 5).map(_ => random.nextInt(10)) - // if digits(0) % 2 == 0 then s"${last5(0)}${digits(0)}${last5(1)}${digits(1)}${last5(2)}-${digits(2)}${last5(3)}${digits(3)}-${last5(4)}${digits(4)}A" - // else s"${digits(0)}${last5(0)}${digits(1)}${last5(1)}${digits(2)}-${last5(2)}${digits(3)}${last5(3)}-${digits(4)}${last5(4)}B" - - try - - import json.* - import ScalaJack.* - - // val o = json.writing.JsonOutput() - // val a: Any = Foo("Hey", Fish("Bloop", Some(true)), ("ok", Seq(true, false))) - // json.writing.AnyWriter.writeAny(a, o) - // println(o.result) - - val p = Person2(XList(List("x", "y"))) - println(RType.of[Person2].pretty) - val js = sj[Person2].toJson(p) - println(js) +class Shape[T](polygon: T) +class Parallelogram() +class Rectangle() extends Parallelogram - // val inst = Blah("wow", Some(111)) // Some(Some(None))) // Some(Some(3))) - // val js = sj[Blah].toJson(inst) - // println(js) - - // co.blocke.scalajack.internal.CodePrinter.code { - // sj[Record] - // } - - // val v = Foo("Hey", Fish("Bloop", None), None, Color.Blue) - // val v = Foo("Hey", "Boo") - - // println(ScalaJack[Foo].toJson(v)) - // println(sj[Foo](JsonConfig.withTypeHintLabel("bogus")).toJson(v)) - - // println(sj[Record].toJson(record)) - - // println("------") - - // println(sj[Record].fromJson(jsData)) - catch { - case t: Throwable => - println(s"BOOM ($t): " + t.getMessage) - t.printStackTrace - } +object RunMe extends App: - // val s1 = scramble(15) - // val s2 = scramble(394857) - // println(s1) - // println(s2) - // println(descrambleTest(s1, 15)) - // println(descrambleTest(s2, 394857)) + val suite: Shape[Parallelogram] = new Shape[Parallelogram](new Parallelogram()) + + // UPDATE: I have no idea what these two cases actually test! They seem to do different things... + + // val s = "\"This is a test\"" + // val now = System.nanoTime() + // (1 to 1000000).map(_ => exp.readFromString(s)) + // val later = System.nanoTime() + // println("JsonReader: " + (later - now)) + + // println("==============================") + + // val s2 = "This is a test\"" + // val now2 = System.nanoTime() + // (1 to 1000000).map(_ => parseString(reading.JsonSource(s2))) + // val later2 = System.nanoTime() + // println("SJ : " + (later2 - now2)) + + // def parseString(in: reading.JsonSource): CharSequence = + // // charWithWS(in, '"') + // val sb = new reading.FastStringBuilder(64) + // while true do + // val c = in.readEscapedString() + // if c == END_OF_STRING then return sb.buffer // mutable thing escapes, but cannot be changed + // sb.append(c.toChar) + // throw JsonParseError("Invalid string value detected", in) + + import ScalaJack.* + import co.blocke.scalajack.run.Record + println("\n") + implicit val blah: ScalaJack[List[Queue[Int]]] = sj[List[Queue[Int]]] + println(ScalaJack[List[Queue[Int]]].fromJson("[[1,2,3],[4,5,6],[7,8,9]]")) + // implicit val blah: ScalaJack[Record] = sj[Record] + // println(ScalaJack[Record].fromJson(co.blocke.scalajack.run.jsData)) + + println("done.") From c8b0d6839658df27e97b8afaaf090304861851bd Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Wed, 14 Feb 2024 00:29:00 -0600 Subject: [PATCH 40/65] Got rudimentary class parsing working --- .../json/exp/JsonReader.scala | 260 ++++++++-------- .../json/exp/JsonReaderException.scala | 3 +- .../json/exp/package.scala | 12 +- .../json/reading/JsonSource.scala | 86 +++++- .../json/writing/JsonCodecMaker.scala | 281 ++++++++++++------ .../scala/co.blocke.scalajack/run/Play.scala | 11 +- .../co.blocke.scalajack/run/Record.scala | 3 + 7 files changed, 413 insertions(+), 243 deletions(-) diff --git a/src/main/scala/co.blocke.scalajack/json/exp/JsonReader.scala b/src/main/scala/co.blocke.scalajack/json/exp/JsonReader.scala index bb5ad981..e7fb3f6f 100644 --- a/src/main/scala/co.blocke.scalajack/json/exp/JsonReader.scala +++ b/src/main/scala/co.blocke.scalajack/json/exp/JsonReader.scala @@ -9,12 +9,12 @@ import java.nio.charset.StandardCharsets.UTF_8 import scala.specialized /* -Bottom Line: +Bottom Line: Fancy string reading, ByteBuffer handling, etc. did NOT have a material effect on speed. The SJ way was either equal to, or faster than, the JsonReader approach for reading strings! -*/ + */ -class JsonReader private[json]( +class JsonReader private[json] ( private[this] var buf: Array[Byte] = new Array[Byte](32768), private[this] var head: Int = 0, private[this] var tail: Int = 0, @@ -35,13 +35,11 @@ class JsonReader private[json]( totalRead = 0 mark = -1 readString("") - } finally { - this.buf = currBuf - } + } finally this.buf = currBuf } def readString(default: String): String = - if (isNextToken('"', head)) { + if isNextToken('"', head) then { val pos = head val len = parseString(0, Math.min(tail - pos, charBuf.length), charBuf, pos) new String(charBuf, 0, len) @@ -49,7 +47,7 @@ class JsonReader private[json]( @tailrec private[this] def isNextToken(t: Byte, pos: Int): Boolean = - if (pos < tail) { + if pos < tail then { val b = buf(pos) head = pos + 1 b == t || ((b == ' ' || b == '\n' || (b | 0x4) == '\r') && nextToken(pos + 1) == t) @@ -57,27 +55,27 @@ class JsonReader private[json]( @tailrec private[this] def nextToken(pos: Int): Byte = - if (pos < tail) { + if pos < tail then { val b = buf(pos) - if (b == ' ' || b == '\n' || (b | 0x4) == '\r') nextToken(pos + 1) + if b == ' ' || b == '\n' || (b | 0x4) == '\r' then nextToken(pos + 1) else { head = pos + 1 b } - } else nextToken(loadMoreOrError(pos)) + } else nextToken(loadMoreOrError(pos)) @tailrec private[this] def readNullOrTokenError[@specialized A](default: A, t: Byte): A = - if (default != null) { + if default != null then { val pos = head - if (pos != 0) { - if (pos + 2 < tail) { + if pos != 0 then { + if pos + 2 < tail then { val bs = ByteArrayAccess.getInt(buf, pos - 1) - if (bs == 0x6C6C756E) { + if bs == 0x6c6c756e then { head = pos + 3 default } else tokenOrNullError(t, bs, pos) - } else if (buf(pos - 1) == 'n') { + } else if buf(pos - 1) == 'n' then { head = loadMoreOrError(pos - 1) + 1 readNullOrTokenError(default, t) } else tokenOrNullError(t) @@ -86,29 +84,29 @@ class JsonReader private[json]( @tailrec private[this] def parseString(i: Int, minLim: Int, charBuf: Array[Char], pos: Int): Int = - if (i + 3 < minLim) { // Based on SWAR routine of JSON string parsing: https://github.com/sirthias/borer/blob/fde9d1ce674d151b0fee1dd0c2565020c3f6633a/core/src/main/scala/io/bullet/borer/json/JsonParser.scala#L456 + if i + 3 < minLim then { // Based on SWAR routine of JSON string parsing: https://github.com/sirthias/borer/blob/fde9d1ce674d151b0fee1dd0c2565020c3f6633a/core/src/main/scala/io/bullet/borer/json/JsonParser.scala#L456 val bs = ByteArrayAccess.getInt(buf, pos) - val m = ((bs - 0x20202020 ^ 0x3C3C3C3C) - 0x1010101 | (bs ^ 0x5D5D5D5D) + 0x1010101) & 0x80808080 - charBuf(i) = (bs & 0xFF).toChar - charBuf(i + 1) = (bs >> 8 & 0xFF).toChar - charBuf(i + 2) = (bs >> 16 & 0xFF).toChar + val m = ((bs - 0x20202020 ^ 0x3c3c3c3c) - 0x1010101 | (bs ^ 0x5d5d5d5d) + 0x1010101) & 0x80808080 + charBuf(i) = (bs & 0xff).toChar + charBuf(i + 1) = (bs >> 8 & 0xff).toChar + charBuf(i + 2) = (bs >> 16 & 0xff).toChar charBuf(i + 3) = (bs >> 24).toChar - if (m != 0) { + if m != 0 then { val offset = java.lang.Integer.numberOfTrailingZeros(m) >> 3 - if ((bs >> (offset << 3)).toByte == '"') { + if (bs >> (offset << 3)).toByte == '"' then { head = pos + offset + 1 i + offset } else parseEncodedString(i + offset, charBuf.length - 1, charBuf, pos + offset) } else parseString(i + 4, minLim, charBuf, pos + 4) - } else if (i < minLim) { + } else if i < minLim then { val b = buf(pos) charBuf(i) = b.toChar - if (b == '"') { + if b == '"' then { head = pos + 1 i - } else if ((b - 0x20 ^ 0x3C) <= 0) parseEncodedString(i, charBuf.length - 1, charBuf, pos) + } else if (b - 0x20 ^ 0x3c) <= 0 then parseEncodedString(i, charBuf.length - 1, charBuf, pos) else parseString(i + 1, minLim, charBuf, pos + 1) - } else if (pos >= tail) { + } else if pos >= tail then { val newPos = loadMoreOrError(pos) parseString(i, Math.min(charBuf.length, i + tail - newPos), charBuf, newPos) } else parseString(i, Math.min(growCharBuf(i + 1), i + tail - pos), this.charBuf, pos) @@ -116,73 +114,75 @@ class JsonReader private[json]( @tailrec private[this] def parseEncodedString(i: Int, lim: Int, charBuf: Array[Char], pos: Int): Int = { val remaining = tail - pos - if (i < lim) { - if (remaining > 0) { + if i < lim then { + if remaining > 0 then { val b1 = buf(pos) - if (b1 >= 0) { - if (b1 == '"') { + if b1 >= 0 then { + if b1 == '"' then { head = pos + 1 i - } else if (b1 != '\\') { // 0aaaaaaa (UTF-8 byte) -> 000000000aaaaaaa (UTF-16 char) - if (b1 < ' ') unescapedControlCharacterError(pos) + } else if b1 != '\\' then { // 0aaaaaaa (UTF-8 byte) -> 000000000aaaaaaa (UTF-16 char) + if b1 < ' ' then unescapedControlCharacterError(pos) charBuf(i) = b1.toChar parseEncodedString(i + 1, lim, charBuf, pos + 1) - } else if (remaining > 1) { + } else if remaining > 1 then { val b2 = buf(pos + 1) - if (b2 != 'u') { + if b2 != 'u' then { charBuf(i) = (b2: @switch) match { - case '"' => '"' - case 'n' => '\n' - case 'r' => '\r' - case 't' => '\t' - case 'b' => '\b' - case 'f' => '\f' + case '"' => '"' + case 'n' => '\n' + case 'r' => '\r' + case 't' => '\t' + case 'b' => '\b' + case 'f' => '\f' case '\\' => '\\' - case '/' => '/' - case _ => illegalEscapeSequenceError(pos + 1) + case '/' => '/' + case _ => illegalEscapeSequenceError(pos + 1) } parseEncodedString(i + 1, lim, charBuf, pos + 2) - } else if (remaining > 5) { + } else if remaining > 5 then { val ch1 = readEscapedUnicode(pos + 2, buf) charBuf(i) = ch1 - if (ch1 < 0xD800 || ch1 > 0xDFFF) parseEncodedString(i + 1, lim, charBuf, pos + 6) - else if (remaining > 11) { - if (buf(pos + 6) != '\\') illegalEscapeSequenceError(pos + 6) - if (buf(pos + 7) != 'u') illegalEscapeSequenceError(pos + 7) + if ch1 < 0xd800 || ch1 > 0xdfff then parseEncodedString(i + 1, lim, charBuf, pos + 6) + else if remaining > 11 then { + if buf(pos + 6) != '\\' then illegalEscapeSequenceError(pos + 6) + if buf(pos + 7) != 'u' then illegalEscapeSequenceError(pos + 7) val ch2 = readEscapedUnicode(pos + 8, buf) - if (ch1 >= 0xDC00 || ch2 < 0xDC00 || ch2 > 0xDFFF) decodeError("illegal surrogate character pair", pos + 11) + if ch1 >= 0xdc00 || ch2 < 0xdc00 || ch2 > 0xdfff then decodeError("illegal surrogate character pair", pos + 11) charBuf(i + 1) = ch2 parseEncodedString(i + 2, lim, charBuf, pos + 12) } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos)) } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos)) } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos)) - } else if ((b1 >> 5) == -2) { // 110bbbbb 10aaaaaa (UTF-8 bytes) -> 00000bbbbbaaaaaa (UTF-16 char) - if (remaining > 1) { + } else if (b1 >> 5) == -2 then { // 110bbbbb 10aaaaaa (UTF-8 bytes) -> 00000bbbbbaaaaaa (UTF-16 char) + if remaining > 1 then { val b2 = buf(pos + 1) - if ((b1 & 0x1E) == 0 || (b2 & 0xC0) != 0x80) malformedBytesError(b1, b2, pos) - charBuf(i) = (b1 << 6 ^ b2 ^ 0xF80).toChar // 0xF80 == 0xC0.toByte << 6 ^ 0x80.toByte + if (b1 & 0x1e) == 0 || (b2 & 0xc0) != 0x80 then malformedBytesError(b1, b2, pos) + charBuf(i) = (b1 << 6 ^ b2 ^ 0xf80).toChar // 0xF80 == 0xC0.toByte << 6 ^ 0x80.toByte parseEncodedString(i + 1, lim, charBuf, pos + 2) } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos)) - } else if ((b1 >> 4) == -2) { // 1110cccc 10bbbbbb 10aaaaaa (UTF-8 bytes) -> ccccbbbbbbaaaaaa (UTF-16 char) - if (remaining > 2) { + } else if (b1 >> 4) == -2 then { // 1110cccc 10bbbbbb 10aaaaaa (UTF-8 bytes) -> ccccbbbbbbaaaaaa (UTF-16 char) + if remaining > 2 then { val b2 = buf(pos + 1) val b3 = buf(pos + 2) - val ch = (b1 << 12 ^ b2 << 6 ^ b3 ^ 0xFFFE1F80).toChar // 0xFFFE1F80 == 0xE0.toByte << 12 ^ 0x80.toByte << 6 ^ 0x80.toByte - if ((b1 == -32 && (b2 & 0xE0) == 0x80) || (b2 & 0xC0) != 0x80 || (b3 & 0xC0) != 0x80 || - (ch >= 0xD800 && ch <= 0xDFFF)) malformedBytesError(b1, b2, b3, pos) + val ch = (b1 << 12 ^ b2 << 6 ^ b3 ^ 0xfffe1f80).toChar // 0xFFFE1F80 == 0xE0.toByte << 12 ^ 0x80.toByte << 6 ^ 0x80.toByte + if (b1 == -32 && (b2 & 0xe0) == 0x80) || (b2 & 0xc0) != 0x80 || (b3 & 0xc0) != 0x80 || + (ch >= 0xd800 && ch <= 0xdfff) + then malformedBytesError(b1, b2, b3, pos) charBuf(i) = ch parseEncodedString(i + 1, lim, charBuf, pos + 3) } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos)) - } else if ((b1 >> 3) == -2) { // 11110ddd 10ddcccc 10bbbbbb 10aaaaaa (UTF-8 bytes) -> 110110uuuuccccbb 110111bbbbaaaaaa (UTF-16 chars), where uuuu = ddddd - 1 - if (remaining > 3) { + } else if (b1 >> 3) == -2 then { // 11110ddd 10ddcccc 10bbbbbb 10aaaaaa (UTF-8 bytes) -> 110110uuuuccccbb 110111bbbbaaaaaa (UTF-16 chars), where uuuu = ddddd - 1 + if remaining > 3 then { val b2 = buf(pos + 1) val b3 = buf(pos + 2) val b4 = buf(pos + 3) - val cp = b1 << 18 ^ b2 << 12 ^ b3 << 6 ^ b4 ^ 0x381F80 // 0x381F80 == 0xF0.toByte << 18 ^ 0x80.toByte << 12 ^ 0x80.toByte << 6 ^ 0x80.toByte - if ((b2 & 0xC0) != 0x80 || (b3 & 0xC0) != 0x80 || (b4 & 0xC0) != 0x80 || - cp < 0x10000 || cp > 0x10FFFF) malformedBytesError(b1, b2, b3, b4, pos) - charBuf(i) = ((cp >>> 10) + 0xD7C0).toChar // 0xD7C0 == 0xD800 - (0x10000 >>> 10) - charBuf(i + 1) = ((cp & 0x3FF) + 0xDC00).toChar + val cp = b1 << 18 ^ b2 << 12 ^ b3 << 6 ^ b4 ^ 0x381f80 // 0x381F80 == 0xF0.toByte << 18 ^ 0x80.toByte << 12 ^ 0x80.toByte << 6 ^ 0x80.toByte + if (b2 & 0xc0) != 0x80 || (b3 & 0xc0) != 0x80 || (b4 & 0xc0) != 0x80 || + cp < 0x10000 || cp > 0x10ffff + then malformedBytesError(b1, b2, b3, b4, pos) + charBuf(i) = ((cp >>> 10) + 0xd7c0).toChar // 0xD7C0 == 0xD800 - (0x10000 >>> 10) + charBuf(i + 1) = ((cp & 0x3ff) + 0xdc00).toChar parseEncodedString(i + 2, lim, charBuf, pos + 4) } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos)) } else malformedBytesError(b1, pos) @@ -231,8 +231,8 @@ class JsonReader private[json]( private[this] def appendHexByte(b: Byte, i: Int, ds: Array[Char]): Int = { ensureCharBufCapacity(i + 2) - charBuf(i) = ds(b >> 4 & 0xF) - charBuf(i + 1) = ds(b & 0xF) + charBuf(i) = ds(b >> 4 & 0xf) + charBuf(i + 1) = ds(b & 0xf) i + 2 } @@ -245,25 +245,25 @@ class JsonReader private[json]( } private[this] def ensureCharBufCapacity(required: Int): Unit = - if (charBuf.length < required) growCharBuf(required): Unit + if charBuf.length < required then growCharBuf(required): Unit - private final val hexDigits: Array[Char] = + final private val hexDigits: Array[Char] = Array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f') private[this] def decodeError(msg: String, pos: Int, cause: Throwable = null): Nothing = decodeError(appendString(msg, 0), pos, cause) - + private[this] def decodeError(from: Int, pos: Int, cause: Throwable): Nothing = { var i = appendString(", offset: 0x", from) val offset = - if ((bbuf eq null) && (in eq null)) 0 + if (bbuf eq null) && (in eq null) then 0 else totalRead - tail i = appendHexOffset(offset + pos, i) // if (config.appendHexDumpToParseException) { // i = appendString(", buf:", i) // i = appendHexDump(pos, offset.toInt, i) // } - throw new JsonReaderException(new String(charBuf, 0, i), cause, true) //config.throwReaderExceptionWithStackTrace) + throw new JsonReaderException(new String(charBuf, 0, i), cause, true) // config.throwReaderExceptionWithStackTrace) } private[this] def appendHexOffset(d: Long, i: Int): Int = { @@ -271,11 +271,11 @@ class JsonReader private[json]( val ds = hexDigits var j = i val dl = d.toInt - if (dl != d) { + if dl != d then { val dh = (d >> 32).toInt - var shift = 32 - java.lang.Integer.numberOfLeadingZeros(dh) & 0x1C - while (shift >= 0) { - charBuf(j) = ds(dh >> shift & 0xF) + var shift = 32 - java.lang.Integer.numberOfLeadingZeros(dh) & 0x1c + while shift >= 0 do { + charBuf(j) = ds(dh >> shift & 0xf) shift -= 4 j += 1 } @@ -286,62 +286,62 @@ class JsonReader private[json]( private[this] def putHexInt(d: Int, i: Int, charBuf: Array[Char], ds: Array[Char]): Unit = { charBuf(i) = ds(d >>> 28) - charBuf(i + 1) = ds(d >> 24 & 0xF) - charBuf(i + 2) = ds(d >> 20 & 0xF) - charBuf(i + 3) = ds(d >> 16 & 0xF) - charBuf(i + 4) = ds(d >> 12 & 0xF) - charBuf(i + 5) = ds(d >> 8 & 0xF) - charBuf(i + 6) = ds(d >> 4 & 0xF) - charBuf(i + 7) = ds(d & 0xF) + charBuf(i + 1) = ds(d >> 24 & 0xf) + charBuf(i + 2) = ds(d >> 20 & 0xf) + charBuf(i + 3) = ds(d >> 16 & 0xf) + charBuf(i + 4) = ds(d >> 12 & 0xf) + charBuf(i + 5) = ds(d >> 8 & 0xf) + charBuf(i + 6) = ds(d >> 4 & 0xf) + charBuf(i + 7) = ds(d & 0xf) } private[this] def loadMoreOrError(pos: Int): Int = { - if ((bbuf eq null) && (in eq null)) endOfInputError() + if (bbuf eq null) && (in eq null) then endOfInputError() loadMore(pos, throwOnEndOfInput = true) } private[this] def loadMore(pos: Int): Int = - if ((bbuf eq null) && (in eq null)) pos + if (bbuf eq null) && (in eq null) then pos else loadMore(pos, throwOnEndOfInput = false) private[this] def loadMore(pos: Int, throwOnEndOfInput: Boolean): Int = { var newPos = pos val offset = - if (mark < 0) pos + if mark < 0 then pos else mark - if (offset > 0) { + if offset > 0 then { newPos -= offset val buf = this.buf val remaining = tail - offset var i = 0 - while (i < remaining) { + while i < remaining do { buf(i) = buf(i + offset) i += 1 } - if (mark > 0) mark = 0 + if mark > 0 then mark = 0 tail = remaining head = newPos } else growBuf() var len = buf.length - tail - if (bbuf ne null) { + if bbuf ne null then { len = Math.min(bbuf.remaining, len) bbuf.get(buf, tail, len) } else len = Math.max(in.read(buf, tail, len), 0) - if (throwOnEndOfInput && len == 0) endOfInputError() + if throwOnEndOfInput && len == 0 then endOfInputError() tail += len totalRead += len newPos } private[json] def endOfInputOrError(): Unit = - if (skipWhitespaces()) decodeError("expected end of input", head) + if skipWhitespaces() then decodeError("expected end of input", head) private[this] def endOfInputError(): Nothing = decodeError("unexpected end of input", tail) private[this] def illegalEscapeSequenceError(pos: Int): Nothing = decodeError("illegal escape sequence", pos) private[this] def unescapedControlCharacterError(pos: Int): Nothing = decodeError("unescaped control character", pos) @tailrec private[this] def hexDigitError(pos: Int): Nothing = { - if (nibbles(buf(pos) & 0xFF) < 0) decodeError("expected hex digit", pos) + if nibbles(buf(pos) & 0xff) < 0 then decodeError("expected hex digit", pos) hexDigitError(pos + 1) } private[this] def tokenError(t: Byte, pos: Int = head - 1): Nothing = { @@ -350,16 +350,18 @@ class JsonReader private[json]( i = appendChar('\'', i) decodeError(i, pos, null) } - private[this] def tokenOrNullError(t: Byte, bs: Int, pos: Int): Nothing = tokenOrNullError(t, { - val b0 = bs.toByte - val b1 = (bs >> 8).toByte - val b2 = (bs >> 16).toByte - pos + - (if (b0 != 'n') -1 - else if (b1 != 'u') 0 - else if (b2 != 'l') 1 - else 2) - }) + private[this] def tokenOrNullError(t: Byte, bs: Int, pos: Int): Nothing = tokenOrNullError( + t, { + val b0 = bs.toByte + val b1 = (bs >> 8).toByte + val b2 = (bs >> 16).toByte + pos + + (if b0 != 'n' then -1 + else if b1 != 'u' then 0 + else if b2 != 'l' then 1 + else 2) + } + ) private[this] def tokenOrNullError(t: Byte, pos: Int = head - 1): Nothing = { var i = appendString("expected '", 0) i = appendChar(t.toChar, i) @@ -378,14 +380,15 @@ class JsonReader private[json]( private[json] def skipWhitespaces(): Boolean = { var pos = head var buf = this.buf - while ((pos < tail || { - pos = loadMore(pos) - buf = this.buf - pos < tail - }) && { - val b = buf(pos) - b == ' ' || b == '\n' || (b | 0x4) == '\r' - }) pos += 1 + while (pos < tail || { + pos = loadMore(pos) + buf = this.buf + pos < tail + }) && { + val b = buf(pos) + b == ' ' || b == '\n' || (b | 0x4) == '\r' + } + do pos += 1 head = pos pos != tail } @@ -412,29 +415,18 @@ class JsonReader private[json]( private[this] def readEscapedUnicode(pos: Int, buf: Array[Byte]): Char = { val ns = nibbles val x = - ns(buf(pos) & 0xFF) << 12 | - ns(buf(pos + 1) & 0xFF) << 8 | - ns(buf(pos + 2) & 0xFF) << 4 | - ns(buf(pos + 3) & 0xFF) - if (x < 0) hexDigitError(pos) + ns(buf(pos) & 0xff) << 12 | + ns(buf(pos + 1) & 0xff) << 8 | + ns(buf(pos + 2) & 0xff) << 4 | + ns(buf(pos + 3) & 0xff) + if x < 0 then hexDigitError(pos) x.toChar } - private final val nibbles: Array[Byte] = Array( - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, - -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 - ) \ No newline at end of file + final private val nibbles: Array[Byte] = Array( + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, + 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1 + ) diff --git a/src/main/scala/co.blocke.scalajack/json/exp/JsonReaderException.scala b/src/main/scala/co.blocke.scalajack/json/exp/JsonReaderException.scala index a910deb0..1125402a 100644 --- a/src/main/scala/co.blocke.scalajack/json/exp/JsonReaderException.scala +++ b/src/main/scala/co.blocke.scalajack/json/exp/JsonReaderException.scala @@ -2,5 +2,4 @@ package co.blocke.scalajack package json package exp -class JsonReaderException private[json](msg: String, cause: Throwable, withStackTrace: Boolean) - extends RuntimeException(msg, cause, true, withStackTrace) \ No newline at end of file +class JsonReaderException private[json] (msg: String, cause: Throwable, withStackTrace: Boolean) extends RuntimeException(msg, cause, true, withStackTrace) diff --git a/src/main/scala/co.blocke.scalajack/json/exp/package.scala b/src/main/scala/co.blocke.scalajack/json/exp/package.scala index 9717f4f1..51b2a08b 100644 --- a/src/main/scala/co.blocke.scalajack/json/exp/package.scala +++ b/src/main/scala/co.blocke.scalajack/json/exp/package.scala @@ -2,14 +2,14 @@ package co.blocke.scalajack package json import java.nio.ByteBuffer -import scala.{specialized => sp} +import scala.specialized as sp package object exp { - private[this] final val readerPool: ThreadLocal[JsonReader] = new ThreadLocal[JsonReader] { - override def initialValue(): JsonReader = new JsonReader - } + final private[this] val readerPool: ThreadLocal[JsonReader] = new ThreadLocal[JsonReader] { + override def initialValue(): JsonReader = new JsonReader + } - def readFromString(s: String): String = - readerPool.get.read(s) + def readFromString(s: String): String = + readerPool.get.read(s) } diff --git a/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala b/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala index 562aa20a..7293f87c 100644 --- a/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala +++ b/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala @@ -18,7 +18,7 @@ case class JsonSource(js: CharSequence): def pos = i - def here = js.charAt(i) + inline def here = js.charAt(i) inline def read(): Char = if i < max then @@ -45,6 +45,11 @@ case class JsonSource(js: CharSequence): case _ => false } + @inline private[this] def isNumber(c: Char): Boolean = + (c: @switch) match + case '+' | '-' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '.' | 'e' | 'E' => true + case _ => false + // Read, transforming escaped chars and stopping when we hit '"' inline def readEscapedString(): Char = read() match @@ -101,6 +106,14 @@ case class JsonSource(js: CharSequence): case c => throw new JsonParseError(s"Expected array start '[' but found '$c'", this) } + // True if we got anything besides a ], False for ] + def firstArrayElement(): Boolean = + (readSkipWhitespace(): @switch) match + case ']' => false + case _ => + retract() + true + // True if we got a comma, and False for ] def nextArrayElement(): Boolean = (readSkipWhitespace(): @switch) match @@ -110,24 +123,43 @@ case class JsonSource(js: CharSequence): false case c => throw JsonParseError(s"Expected ',' or ']' got '$c'", this) + // True if we got a string (implies a retraction), False for } + def firstField(): Boolean = + (readSkipWhitespace(): @switch) match { + case '"' => true + case '}' => false + case c => + throw JsonParseError(s"expected string or '}' got '$c'", this) + } + // True if we got a comma, and False for } def nextField(): Boolean = (readSkipWhitespace(): @switch) match { case ',' => expectFieldValue = false true - case '}' if !expectFieldValue => - false + // case '}' if !expectFieldValue => + // false case '}' => - throw JsonParseError("Expected field value but got '}' instead.", this) + false + // throw JsonParseError("Expected field value but got '}' instead.", this) case c => throw JsonParseError(s"expected ',' or '}' got '$c'", this) } - inline def expectFieldName(): CharSequence = - val charseq = parseString() + inline def expectFieldName(fieldNameMatrix: StringMatrix): Int = + charWithWS('"') expectFieldValue = true - charseq + var fi: Int = 0 + var bs: Long = fieldNameMatrix.initial + var c: Int = -1 + while { c = readEscapedString(); c != END_OF_STRING } do { + bs = fieldNameMatrix.update(bs, fi, c) + fi += 1 + } + charWithWS(':') + bs = fieldNameMatrix.exact(bs, fi) + fieldNameMatrix.first(bs) // Value might be null! def expectString(): CharSequence = @@ -169,9 +201,9 @@ case class JsonSource(js: CharSequence): def expectInt(): Int = checkNumber() try { - val i = UnsafeNumbers.int_(this, false) + val intRead = UnsafeNumbers.int_(this, false) retract() - i + intRead } catch { case UnsafeNumbers.UnsafeNumber => throw JsonParseError("Expected an Int", this) } @@ -194,3 +226,39 @@ case class JsonSource(js: CharSequence): inline def charWithWS(c: Char): Unit = val got = readSkipWhitespace() if got != c then throw JsonParseError(s"Expected '$c' got '$got'", this) + + def skipValue(): Unit = + (readSkipWhitespace(): @switch) match { + case 'n' => readChars(JsonSource.ull, "null") + case 'f' => readChars(JsonSource.alse, "false") + case 't' => readChars(JsonSource.rue, "true") + case '{' => + if firstField() then { + while { + { + charWithWS('"') + skipString() + charWithWS(':') + skipValue() + }; nextField() + } do () + } + case '[' => + if firstArrayElement() then { + while { skipValue(); nextArrayElement() } do () + } + case '"' => + skipString() + case '-' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '.' => + skipNumber() + case c => throw JsonParseError(s"Unexpected '$c'", this) + } + + def skipNumber(): Unit = { + while isNumber(read()) do {} + retract() + } + + def skipString(): Unit = + var i: Int = 0 + while { i = readEscapedString(); i != -1 } do () diff --git a/src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala b/src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala index d6ea82b5..5d8fc79f 100644 --- a/src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala +++ b/src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala @@ -6,13 +6,14 @@ import co.blocke.scala_reflection.{RTypeRef, TypedName} import co.blocke.scala_reflection.reflect.ReflectOnType import co.blocke.scala_reflection.reflect.rtypeRefs.* import co.blocke.scala_reflection.rtypes.{EnumRType, JavaClassRType, NonConstructorFieldInfo} -import reading.JsonSource +import reading.{JsonReaderUtil, JsonSource, StringMatrix} import scala.jdk.CollectionConverters.* import scala.quoted.* import scala.collection.Factory import dotty.tools.dotc.ast.Trees.EmptyTree import org.apache.commons.text.StringEscapeUtils import org.apache.commons.lang3.text.translate.CharSequenceTranslator +import dotty.tools.dotc.core.TypeComparer.AnyConstantType object JsonCodecMaker: @@ -57,35 +58,51 @@ object JsonCodecMaker: val readMethodSyms = new scala.collection.mutable.HashMap[MethodKey, Symbol] val readMethodDefs = new scala.collection.mutable.ArrayBuffer[DefDef] - class JsonReader // temporary to compile until we write the real JsonReader - - def makeReadFn[U: Type](methodKey: MethodKey, arg: Expr[U], in: Expr[JsonReader])(f: (Expr[JsonReader], Expr[U]) => Expr[U])(using Quotes)(using Type[JsonReader]): Expr[U] = - methodKey.ref.refType match - case '[tt] => - val typerepr = TypeRepr.of[tt] - Apply( - Ref( - readMethodSyms.getOrElse( - methodKey, { - val sym = Symbol.newMethod( - Symbol.spliceOwner, - "r" + readMethodSyms.size, - MethodType(List("in", "default"))(_ => List(TypeRepr.of[JsonReader], typerepr), _ => TypeRepr.of[U]) - ) - readMethodSyms.update(methodKey, sym) - readMethodDefs += DefDef( - sym, - params => { - val List(List(in, default)) = params - Some(f(in.asExprOf[JsonReader], default.asExprOf[U]).asTerm.changeOwner(sym)) - } - ) - sym - } - ) - ), - List(in.asTerm, arg.asTerm) - ).asExprOf[U] + def makeReadFn[U: Type](methodKey: MethodKey, in: Expr[JsonSource])(f: Expr[JsonSource] => Expr[U])(using Quotes)(using Type[JsonSource]): Expr[Unit] = + readMethodSyms.getOrElse( + methodKey, { + val sym = Symbol.newMethod( + Symbol.spliceOwner, + "r" + readMethodSyms.size, + MethodType(List("in"))(_ => List(TypeRepr.of[JsonSource]), _ => TypeRepr.of[U]) + // (_ => List(input_params,...), _ => resultType) + ) + readMethodSyms.update(methodKey, sym) + readMethodDefs += DefDef( + sym, + params => { + val List(List(in)) = params + Some(f(in.asExprOf[JsonSource]).asTerm.changeOwner(sym)) + } + ) + } + ) + '{} + + // Apply( + // Ref( + // readMethodSyms.getOrElse( + // methodKey, { + // val sym = Symbol.newMethod( + // Symbol.spliceOwner, + // "r" + readMethodSyms.size, + // MethodType(List("in"))(_ => List(TypeRepr.of[JsonSource]), _ => TypeRepr.of[U]) + // // (_ => List(input_params,...), _ => resultType) + // ) + // readMethodSyms.update(methodKey, sym) + // readMethodDefs += DefDef( + // sym, + // params => { + // val List(List(in)) = params + // Some(f(in.asExprOf[JsonSource]).asTerm.changeOwner(sym)) + // } + // ) + // sym + // } + // ) + // ), + // List(in.asTerm) + // ).asExprOf[Unit] // --------------------------------------------------------------------------------------------- @@ -791,6 +808,82 @@ object JsonCodecMaker: // --------------------------------------------------------------------------------------------- + // Little rif on Expr.ofList. I need an Expr[Array] here because in the runtime code we need to update the + // array (which is pre-loaded with default values) with the parsed field values for ultimate class instantiation. + import scala.reflect.ClassTag + def ofArray[T](xs: Seq[Expr[T]])(using Type[T])(using Quotes): Expr[Array[T]] = + '{ scala.Array.apply[T](${ quoted.Varargs(xs) }*)(${ Expr.summon[ClassTag[T]].get }) } + + def genDecFnBody[T: Type](r: RTypeRef[?], in: Expr[JsonSource])(using Quotes): Expr[Unit] = + r.refType match // refType is Type[r.R] + case '[b] => + r match + case t: ScalaClassRef[?] => + makeReadFn[T](MethodKey(t, false), in)(in => + val fieldNames = Expr(t.fields.map(_.name).toArray) + val instantiator = JsonReaderUtil.classInstantiator[T](t.asInstanceOf[ClassRef[T]]) + // Constructor argument list, preloaded with optional 'None' values and any default values specified + val argListArray = + ofArray(t.fields.map { f => + val scalaF = f.asInstanceOf[ScalaFieldInfoRef] + if scalaF.defaultValueAccessorName.isDefined then + r.refType match + case '[g] => + val tpe = TypeRepr.of[g].widen + val sym = tpe.typeSymbol + val companionBody = sym.companionClass.tree.asInstanceOf[ClassDef].body + val companion = Ref(sym.companionModule) + companionBody + .collect { + case defaultMethod @ DefDef(name, _, _, _) if name.startsWith("$lessinit$greater$default$" + (f.index + 1)) => + companion.select(defaultMethod.symbol).appliedToTypes(tpe.typeArgs).asExpr + } + .headOption + .getOrElse(Expr(null.asInstanceOf[Boolean])) + else if scalaF.fieldRef.isInstanceOf[OptionRef[_]] then Expr(None) + else Expr(null.asInstanceOf[Int]) + }) + + /* HOWTO: Wildcard case clause: (from https://github.com/arainko/ducktape/blob/bc65ab2a259b85ce51dc6c762b76904d735b7be6/ducktape/src/main/scala/io/github/arainko/ducktape/internal/modules/ZippedProduct.scala#L66) + (ducktape/ducktape/src/main/scala/io/github/arainko/ducktape/internal/modules/ZippedProduct.scala) + + val matchErrorCase = CaseDef(Bind(matchErrorBind, Wildcard()), None, '{ throw new MatchError($wronglyMatchedReference) }.asTerm) + + */ + + '{ + val fieldMatrix = new StringMatrix($fieldNames) + val args = $argListArray + if ! $in.expectObjectStart() then null.asInstanceOf[T] + else + if $in.here != '}' then + while + ${ + val fieldCases = t.fields.zipWithIndex.map { (f, fidex) => + f.fieldRef.refType match + case '[c] => + val fref: RTypeRef[c] = f.fieldRef.asInstanceOf[RTypeRef[c]] + CaseDef( + Literal(IntConstant(fidex)), + None, + '{ + args.update(${ Expr(fidex) }, ${ genReadVal[c](fref, in) }) + }.asTerm + ) + } :+ CaseDef(Literal(IntConstant(-1)), None, '{ $in.skipValue() }.asTerm) + Match('{ $in.expectFieldName(fieldMatrix) }.asTerm, fieldCases).asExprOf[Any] + } + $in.nextField() + do () + else $in.read() // skip the '}' + $instantiator(args) // instantiate class + } + ) + + case _ => ??? + + // --------------------------------------------------------------------------------------------- + def genReadVal[T: Type]( // default: Expr[T], // needed? This should already be in ref... ref: RTypeRef[T], @@ -799,64 +892,74 @@ object JsonCodecMaker: inTuple: Boolean = false // not sure if needed... )(using Quotes): Expr[T] = val methodKey = MethodKey(ref, false) - // Stuff here to hit readMethodSyms first -- TBD - ref match - // First cover all primitive and simple types... - // case t: BigDecimalRef => - // if isStringified then '{ $out.valueStringified(${ aE.asExprOf[scala.math.BigDecimal] }) } - // else '{ $out.value(${ aE.asExprOf[scala.math.BigDecimal] }) } - // case t: BigIntRef => - // if isStringified then '{ $out.valueStringified(${ aE.asExprOf[scala.math.BigInt] }) } - // else '{ $out.value(${ aE.asExprOf[scala.math.BigInt] }) } - case t: BooleanRef => - '{ $in.expectBoolean() } - // if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Boolean] }) } - // else '{ $out.value(${ aE.asExprOf[Boolean] }) } - // case t: ByteRef => - // if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Byte] }) } - // else '{ $out.value(${ aE.asExprOf[Byte] }) } - // case t: CharRef => '{ $out.value(${ aE.asExprOf[Char] }) } - // case t: DoubleRef => - // if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Double] }) } - // else '{ $out.value(${ aE.asExprOf[Double] }) } - // case t: FloatRef => - // if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Float] }) } - // else '{ $out.value(${ aE.asExprOf[Float] }) } - case t: IntRef => - '{ $in.expectInt() } - // if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Int] }) } - // else '{ $out.value(${ aE.asExprOf[Int] }) } - // case t: LongRef => - // if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Long] }) } - // else '{ $out.value(${ aE.asExprOf[Long] }) } - // case t: ShortRef => - // if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Short] }) } - // else '{ $out.value(${ aE.asExprOf[Short] }) } - case t: StringRef => - '{ Option($in.expectString()).map(_.toString).getOrElse(null) } - // if cfg.escapedStrings then '{ $out.value(StringEscapeUtils.escapeJson(${ aE.asExprOf[String] })) } - // else '{ $out.value(${ aE.asExprOf[String] }) } - - case _ => - ref.refType match - case '[b] => - ref match - case t: SeqRef[?] => - t.elementRef.refType match - case '[e] => - '{ - if ! $in.expectArrayStart() then null.asInstanceOf[T] - else if $in.here == ']' then // empty Seq - $in.read() // skip the ']' - List.empty[e].to(${ Expr.summon[Factory[e, T]].get }) // create appropriate Seq[T] here - else - val acc = scala.collection.mutable.ListBuffer.empty[e] - acc.addOne(${ genReadVal[e](t.elementRef.asInstanceOf[RTypeRef[e]], in) }) - while $in.nextArrayElement() do acc.addOne(${ genReadVal[e](t.elementRef.asInstanceOf[RTypeRef[e]], in) }) - acc.to(${ Expr.summon[Factory[e, T]].get }) // create appropriate Seq[T] here - } + readMethodSyms + .get(methodKey) + .map { sym => // hit cache first... then match on Ref type + println("Calling generated method " + sym) + Apply(Ref(sym), List(in.asTerm)).asExprOf[T] + } + .getOrElse( + ref match + // First cover all primitive and simple types... + // case t: BigDecimalRef => + // if isStringified then '{ $out.valueStringified(${ aE.asExprOf[scala.math.BigDecimal] }) } + // else '{ $out.value(${ aE.asExprOf[scala.math.BigDecimal] }) } + // case t: BigIntRef => + // if isStringified then '{ $out.valueStringified(${ aE.asExprOf[scala.math.BigInt] }) } + // else '{ $out.value(${ aE.asExprOf[scala.math.BigInt] }) } + case t: BooleanRef => + '{ $in.expectBoolean() }.asExprOf[T] + // if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Boolean] }) } + // else '{ $out.value(${ aE.asExprOf[Boolean] }) } + // case t: ByteRef => + // if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Byte] }) } + // else '{ $out.value(${ aE.asExprOf[Byte] }) } + // case t: CharRef => '{ $out.value(${ aE.asExprOf[Char] }) } + // case t: DoubleRef => + // if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Double] }) } + // else '{ $out.value(${ aE.asExprOf[Double] }) } + // case t: FloatRef => + // if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Float] }) } + // else '{ $out.value(${ aE.asExprOf[Float] }) } + case t: IntRef => + '{ $in.expectInt() }.asExprOf[T] + // if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Int] }) } + // else '{ $out.value(${ aE.asExprOf[Int] }) } + // case t: LongRef => + // if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Long] }) } + // else '{ $out.value(${ aE.asExprOf[Long] }) } + // case t: ShortRef => + // if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Short] }) } + // else '{ $out.value(${ aE.asExprOf[Short] }) } + case t: StringRef => + '{ Option($in.expectString()).map(_.toString).getOrElse(null) }.asExprOf[T] + // if cfg.escapedStrings then '{ $out.value(StringEscapeUtils.escapeJson(${ aE.asExprOf[String] })) } + // else '{ $out.value(${ aE.asExprOf[String] }) } + + case _ => + ref.refType match + case '[b] => + ref match + case t: SeqRef[?] => + t.elementRef.refType match + case '[e] => + '{ + if ! $in.expectArrayStart() then null.asInstanceOf[T] + else if $in.here == ']' then // empty Seq + $in.read() // skip the ']' + List.empty[e].to(${ Expr.summon[Factory[e, T]].get }) // create appropriate Seq[T] here + else + val acc = scala.collection.mutable.ListBuffer.empty[e] + acc.addOne(${ genReadVal[e](t.elementRef.asInstanceOf[RTypeRef[e]], in) }) + while $in.nextArrayElement() do acc.addOne(${ genReadVal[e](t.elementRef.asInstanceOf[RTypeRef[e]], in) }) + acc.to(${ Expr.summon[Factory[e, T]].get }) // create appropriate Seq[T] here + } - // case _ => genDecFnBody(ref, in, inTuple = inTuple) + case _ => + genDecFnBody[T](ref, in) // Create a new decoder function (presumably for class, trait, etc) + genReadVal(ref, in) + // Just-created function is present now and will be called + ) // --------------------------------------------------------------------------------------------- @@ -881,7 +984,7 @@ object JsonCodecMaker: }.asTerm val neededDefs = // others here??? Refer to Jsoniter file JsonCodecMaker.scala - writeMethodDefs + writeMethodDefs ++ readMethodDefs val codec = Block(neededDefs.toList, codecDef).asExprOf[JsonCodec[T]] - // println(s"Codec: ${codec.show}") + println(s"Codec: ${codec.show}") codec diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index 72e942a4..7e2ca062 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -42,10 +42,15 @@ object RunMe extends App: // throw JsonParseError("Invalid string value detected", in) import ScalaJack.* - import co.blocke.scalajack.run.Record + import co.blocke.scalajack.json.run.Record println("\n") - implicit val blah: ScalaJack[List[Queue[Int]]] = sj[List[Queue[Int]]] - println(ScalaJack[List[Queue[Int]]].fromJson("[[1,2,3],[4,5,6],[7,8,9]]")) + + implicit val blah: ScalaJack[Foom] = sj[Foom] + println(ScalaJack[Foom].fromJson("""{"a": -12, "b":"Greg Z"}""")) + + // implicit val blah: ScalaJack[List[Queue[Int]]] = sj[List[Queue[Int]]] + // println(ScalaJack[List[Queue[Int]]].fromJson("null")) // "[[1,2,3],[4,5,6],[7,8,9]]")) + // implicit val blah: ScalaJack[Record] = sj[Record] // println(ScalaJack[Record].fromJson(co.blocke.scalajack.run.jsData)) diff --git a/src/main/scala/co.blocke.scalajack/run/Record.scala b/src/main/scala/co.blocke.scalajack/run/Record.scala index 425b2779..94322c55 100644 --- a/src/main/scala/co.blocke.scalajack/run/Record.scala +++ b/src/main/scala/co.blocke.scalajack/run/Record.scala @@ -1,4 +1,5 @@ package co.blocke.scalajack +package json package run import neotype.* @@ -85,6 +86,8 @@ given EmptyString: Newtype[String] with case class Person2(age: XList) +case class Foom(a: Int = 5, b: String = "Wow") + val jsData = """{ "person": { From 241661d2e574d004ece7a680356e0e8c7ca14171 Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Wed, 21 Feb 2024 22:30:57 -0600 Subject: [PATCH 41/65] reworked read codegen --- benchmark/build.sbt | 2 +- .../src/main/scala/co.blocke/Benchmark.scala | 26 +- .../src/main/scala/co.blocke/ScalaJack.scala | 4 +- .../scala/co.blocke.scalajack/ScalaJack.scala | 4 +- .../json/{writing => }/JsonCodecMaker.scala | 116 +++--- .../co.blocke.scalajack/json/JsonConfig.scala | 8 +- .../co.blocke.scalajack/json/JsonError.scala | 13 + ...ClassDecoder.scala => ClassDecoder.scalax} | 0 .../{JsonDecoder.scala => JsonDecoder.scalax} | 0 .../{JsonParser.scala => JsonParser.scalax} | 0 .../json/reading/JsonReaderUtil.scala | 31 +- .../json/reading/JsonSource.scala | 109 ++++-- .../json/reading/JsonSource2.scala | 150 ++++++++ .../json/reading/Numbers.scala | 42 +-- .../json/reading/StringMatrix.scala | 4 - .../scala/co.blocke.scalajack/run/Foo.scalax | 351 ++++++++++++++++++ .../scala/co.blocke.scalajack/run/Play.scala | 33 +- .../co.blocke.scalajack/run/Record.scala | 2 +- .../json/misc/MiscTests.scala | 8 +- 19 files changed, 750 insertions(+), 153 deletions(-) rename src/main/scala/co.blocke.scalajack/json/{writing => }/JsonCodecMaker.scala (93%) rename src/main/scala/co.blocke.scalajack/json/reading/{ClassDecoder.scala => ClassDecoder.scalax} (100%) rename src/main/scala/co.blocke.scalajack/json/reading/{JsonDecoder.scala => JsonDecoder.scalax} (100%) rename src/main/scala/co.blocke.scalajack/json/reading/{JsonParser.scala => JsonParser.scalax} (100%) create mode 100644 src/main/scala/co.blocke.scalajack/json/reading/JsonSource2.scala create mode 100644 src/main/scala/co.blocke.scalajack/run/Foo.scalax diff --git a/benchmark/build.sbt b/benchmark/build.sbt index 45d16189..64615583 100644 --- a/benchmark/build.sbt +++ b/benchmark/build.sbt @@ -36,7 +36,7 @@ lazy val benchmark = project libraryDependencies ++= Seq( "org.playframework" %% "play-json" % "3.0.1", "io.argonaut" %% "argonaut" % "6.3.9", - "co.blocke" %% "scalajack" % "3a3001_unknown", + "co.blocke" %% "scalajack" % "c8b0d6_unknown", "co.blocke" %% "scala-reflection" % "sj_fixes_f43af7", "dev.zio" %% "zio-json" % "0.6.1", "org.typelevel" %% "fabric-core" % "1.12.6", diff --git a/benchmark/src/main/scala/co.blocke/Benchmark.scala b/benchmark/src/main/scala/co.blocke/Benchmark.scala index 5beb8197..7531bac8 100644 --- a/benchmark/src/main/scala/co.blocke/Benchmark.scala +++ b/benchmark/src/main/scala/co.blocke/Benchmark.scala @@ -43,12 +43,12 @@ trait HandTooledWritingBenchmark { @BenchmarkMode(Array(Mode.Throughput)) @OutputTimeUnit(TimeUnit.SECONDS) class ReadingBenchmark - extends CirceZ.CirceReadingBenchmark - with ScalaJackZ.ScalaJackReadingBenchmark - with JsoniterZ.JsoniterReadingBenchmark - with ZIOZ.ZIOJsonReadingBenchmark - with PlayZ.PlayReadingBenchmark - with ArgonautZ.ArgonautReadingBenchmark + // extends CirceZ.CirceReadingBenchmark + extends ScalaJackZ.ScalaJackReadingBenchmark + //with JsoniterZ.JsoniterReadingBenchmark + // with ZIOZ.ZIOJsonReadingBenchmark + // with PlayZ.PlayReadingBenchmark + // with ArgonautZ.ArgonautReadingBenchmark @State(Scope.Thread) @BenchmarkMode(Array(Mode.Throughput)) @@ -75,3 +75,17 @@ class WritingBenchmark // Jawn (parse only + AST) 336384.617 // ScalaJack JsonParser3 (parse only + AST) 279456.523 // Fabric (new!) (parse only + AST) 270706.567 + + +/* LATEST RUN: + +[info] Benchmark Mode Cnt Score Error Units +[info] ReadingBenchmark.readRecordArgonaut thrpt 20 177341.827 ± 7090.749 ops/s +[info] ReadingBenchmark.readRecordCirce thrpt 20 289018.255 ± 1797.188 ops/s +[info] ReadingBenchmark.readRecordJsoniter thrpt 20 1313108.400 ± 17657.823 ops/s +[info] ReadingBenchmark.readRecordPlay thrpt 20 201570.409 ± 3736.467 ops/s +[info] ReadingBenchmark.readRecordScalaJack thrpt 20 501950.975 ± 5631.196 ops/s +[info] ReadingBenchmark.readRecordZIOJson thrpt 20 587611.803 ± 4701.410 ops/s +[info] WritingBenchmark.writeRecordScalaJack thrpt 20 352825.894 ± 7772.927 ops/s + + */ diff --git a/benchmark/src/main/scala/co.blocke/ScalaJack.scala b/benchmark/src/main/scala/co.blocke/ScalaJack.scala index 84faf749..63cb96c1 100644 --- a/benchmark/src/main/scala/co.blocke/ScalaJack.scala +++ b/benchmark/src/main/scala/co.blocke/ScalaJack.scala @@ -10,8 +10,8 @@ object ScalaJackZ: trait ScalaJackReadingBenchmark{ @Benchmark - // def readRecordScalaJack = sj[Record].fromJson(jsData) - def readRecordScalaJack = ScalaJack[Record].fromJson(jsData) + // def readRecordScalaJack = sj[Record].fromJson(jsData) // 500K + def readRecordScalaJack = ScalaJack[Record].fromJson(jsData) // 515K :-( } trait ScalaJackWritingBenchmark { diff --git a/src/main/scala/co.blocke.scalajack/ScalaJack.scala b/src/main/scala/co.blocke.scalajack/ScalaJack.scala index e8258682..cc7d4a2a 100644 --- a/src/main/scala/co.blocke.scalajack/ScalaJack.scala +++ b/src/main/scala/co.blocke.scalajack/ScalaJack.scala @@ -39,7 +39,7 @@ object ScalaJack: val classRef = ReflectOnType[T](quotes)(TypeRepr.of[T], true)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) // val jsonDecoder = reading.JsonReader.refRead2(classRef) // println(s"Decoder: ${jsonDecoder.show}") - val jsonCodec = writing.JsonCodecMaker.generateCodecFor(classRef, JsonConfig) + val jsonCodec = JsonCodecMaker.generateCodecFor(classRef, JsonConfig) '{ ScalaJack($jsonCodec) } @@ -50,7 +50,7 @@ object ScalaJack: val cfg = summon[FromExpr[JsonConfig]].unapply(cfgE) val classRef = ReflectOnType[T](quotes)(TypeRepr.of[T], true)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) // val jsonDecoder = reading.JsonReader.refRead2(classRef) - val jsonCodec = writing.JsonCodecMaker.generateCodecFor(classRef, cfg.getOrElse(JsonConfig)) + val jsonCodec = JsonCodecMaker.generateCodecFor(classRef, cfg.getOrElse(JsonConfig)) '{ ScalaJack($jsonCodec) } // refRead[T](classRef) diff --git a/src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala b/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala similarity index 93% rename from src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala rename to src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala index 5d8fc79f..0f174fae 100644 --- a/src/main/scala/co.blocke.scalajack/json/writing/JsonCodecMaker.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala @@ -1,7 +1,7 @@ package co.blocke.scalajack package json -package writing +import writing.* import co.blocke.scala_reflection.{RTypeRef, TypedName} import co.blocke.scala_reflection.reflect.ReflectOnType import co.blocke.scala_reflection.reflect.rtypeRefs.* @@ -813,71 +813,74 @@ object JsonCodecMaker: import scala.reflect.ClassTag def ofArray[T](xs: Seq[Expr[T]])(using Type[T])(using Quotes): Expr[Array[T]] = '{ scala.Array.apply[T](${ quoted.Varargs(xs) }*)(${ Expr.summon[ClassTag[T]].get }) } - + def genDecFnBody[T: Type](r: RTypeRef[?], in: Expr[JsonSource])(using Quotes): Expr[Unit] = + import quotes.reflect.* + + def typeArgs(tpe: TypeRepr): List[TypeRepr] = tpe match + case AppliedType(_, typeArgs) => typeArgs.map(_.dealias) + case _ => Nil + r.refType match // refType is Type[r.R] case '[b] => r match case t: ScalaClassRef[?] => makeReadFn[T](MethodKey(t, false), in)(in => val fieldNames = Expr(t.fields.map(_.name).toArray) - val instantiator = JsonReaderUtil.classInstantiator[T](t.asInstanceOf[ClassRef[T]]) - // Constructor argument list, preloaded with optional 'None' values and any default values specified - val argListArray = - ofArray(t.fields.map { f => - val scalaF = f.asInstanceOf[ScalaFieldInfoRef] - if scalaF.defaultValueAccessorName.isDefined then - r.refType match - case '[g] => - val tpe = TypeRepr.of[g].widen - val sym = tpe.typeSymbol - val companionBody = sym.companionClass.tree.asInstanceOf[ClassDef].body - val companion = Ref(sym.companionModule) - companionBody - .collect { - case defaultMethod @ DefDef(name, _, _, _) if name.startsWith("$lessinit$greater$default$" + (f.index + 1)) => - companion.select(defaultMethod.symbol).appliedToTypes(tpe.typeArgs).asExpr - } - .headOption - .getOrElse(Expr(null.asInstanceOf[Boolean])) - else if scalaF.fieldRef.isInstanceOf[OptionRef[_]] then Expr(None) - else Expr(null.asInstanceOf[Int]) - }) - - /* HOWTO: Wildcard case clause: (from https://github.com/arainko/ducktape/blob/bc65ab2a259b85ce51dc6c762b76904d735b7be6/ducktape/src/main/scala/io/github/arainko/ducktape/internal/modules/ZippedProduct.scala#L66) - (ducktape/ducktape/src/main/scala/io/github/arainko/ducktape/internal/modules/ZippedProduct.scala) - val matchErrorCase = CaseDef(Bind(matchErrorBind, Wildcard()), None, '{ throw new MatchError($wronglyMatchedReference) }.asTerm) + // Generate vars for each contractor argument, populated with either a "unit" value (eg 0, "") or given default value + val tpe = TypeRepr.of[b] + val classCompanion = tpe.typeSymbol.companionClass + val companionModule = tpe.typeSymbol.companionModule + val together = t.fields.map{ oneField => + oneField.fieldRef.refType match { + case '[f] => + val dvMembers = classCompanion.methodMember("$lessinit$greater$default$" + (oneField.index+1)) + val sym = Symbol.newVal(Symbol.spliceOwner, "_" + oneField.name, TypeRepr.of[f], Flags.Mutable, Symbol.noSymbol) + val fieldSymRef = Ident(sym.termRef) + val caseDef = CaseDef( + Literal(IntConstant(oneField.index)), + None, + Assign(fieldSymRef, genReadVal[f](oneField.fieldRef.asInstanceOf[RTypeRef[f]], in).asTerm) + ) + if (dvMembers.isEmpty) + (ValDef(sym, Some(oneField.fieldRef.unitVal.asTerm)), + caseDef, fieldSymRef) + else + val methodSymbol = dvMembers.head + val dvSelectNoTArgs = Ref(companionModule).select(methodSymbol) + val dvSelect = methodSymbol.paramSymss match + case Nil => dvSelectNoTArgs + case List(params) if (params.exists(_.isTypeParam)) => typeArgs(tpe) match + case Nil => ??? //throw JsonParseError("Expected an applied type", ???) + case typeArgs => TypeApply(dvSelectNoTArgs, typeArgs.map(Inferred(_))) + case _ => ??? //fail(s"Default method for ${symbol.name} of class ${tpe.show} have a complex " + + // s"parameter list: ${methodSymbol.paramSymss}") + (ValDef(sym, Some(dvSelect)), caseDef, fieldSymRef) + } + } + val (varDefs, caseDefs, idents) = together.unzip3 - */ + val argss = List( idents ) + val primaryConstructor = tpe.classSymbol.get.primaryConstructor + val constructorNoTypes = Select(New(Inferred(tpe)), primaryConstructor) + val constructor = typeArgs(tpe) match + case Nil => constructorNoTypes + case typeArgs => TypeApply(constructorNoTypes, typeArgs.map(Inferred(_))) + val instantiateClass = argss.tail.foldLeft(Apply(constructor, argss.head))((acc, args) => Apply(acc, args)) - '{ + val parseLoop = '{ val fieldMatrix = new StringMatrix($fieldNames) - val args = $argListArray - if ! $in.expectObjectStart() then null.asInstanceOf[T] + var fldIdx = $in.expectFirstObjectField(fieldMatrix) + while( fldIdx >= -1 ) do // -2: end-of-object, -3: null (-1: unknown field -> skip) + ${ Match('{ fldIdx }.asTerm, caseDefs :+ CaseDef(Literal(IntConstant(-1)), None, '{ $in.skipValue() }.asTerm)).asExprOf[Any] } + fldIdx = $in.expectObjectField(fieldMatrix) + if fldIdx == -3 then null.asInstanceOf[T] else - if $in.here != '}' then - while - ${ - val fieldCases = t.fields.zipWithIndex.map { (f, fidex) => - f.fieldRef.refType match - case '[c] => - val fref: RTypeRef[c] = f.fieldRef.asInstanceOf[RTypeRef[c]] - CaseDef( - Literal(IntConstant(fidex)), - None, - '{ - args.update(${ Expr(fidex) }, ${ genReadVal[c](fref, in) }) - }.asTerm - ) - } :+ CaseDef(Literal(IntConstant(-1)), None, '{ $in.skipValue() }.asTerm) - Match('{ $in.expectFieldName(fieldMatrix) }.asTerm, fieldCases).asExprOf[Any] - } - $in.nextField() - do () - else $in.read() // skip the '}' - $instantiator(args) // instantiate class - } + ${instantiateClass.asExprOf[T]} + }.asTerm + + Block(varDefs, parseLoop).asExprOf[T] ) case _ => ??? @@ -895,7 +898,6 @@ object JsonCodecMaker: readMethodSyms .get(methodKey) .map { sym => // hit cache first... then match on Ref type - println("Calling generated method " + sym) Apply(Ref(sym), List(in.asTerm)).asExprOf[T] } .getOrElse( @@ -932,7 +934,7 @@ object JsonCodecMaker: // if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Short] }) } // else '{ $out.value(${ aE.asExprOf[Short] }) } case t: StringRef => - '{ Option($in.expectString()).map(_.toString).getOrElse(null) }.asExprOf[T] + '{ $in.expectString().toString }.asExprOf[T] // if cfg.escapedStrings then '{ $out.value(StringEscapeUtils.escapeJson(${ aE.asExprOf[String] })) } // else '{ $out.value(${ aE.asExprOf[String] }) } @@ -946,7 +948,7 @@ object JsonCodecMaker: '{ if ! $in.expectArrayStart() then null.asInstanceOf[T] else if $in.here == ']' then // empty Seq - $in.read() // skip the ']' + $in.readChar() // skip the ']' List.empty[e].to(${ Expr.summon[Factory[e, T]].get }) // create appropriate Seq[T] here else val acc = scala.collection.mutable.ListBuffer.empty[e] diff --git a/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala b/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala index 82fc158c..5831ee2d 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala @@ -28,7 +28,7 @@ class JsonConfig private[scalajack] ( def withTypeHintLabel(label: String): JsonConfig = copy(typeHintLabel = label) def withTypeHintPolicy(hintPolicy: TypeHintPolicy): JsonConfig = copy(typeHintPolicy = hintPolicy) def withEnumsAsIds(asIds: Option[List[String]]): JsonConfig = copy(enumsAsIds = asIds) - def withoutEscapedStrings(): JsonConfig = copy(escapedStrings = false) + def withEscapedStrings(): JsonConfig = copy(escapedStrings = false) private[this] def copy( noneAsNull: Boolean = noneAsNull, @@ -71,7 +71,7 @@ object JsonConfig typeHintLabel = "_hint", typeHintPolicy = TypeHintPolicy.SIMPLE_CLASSNAME, enumsAsIds = None, - escapedStrings = true + escapedStrings = false ): import scala.quoted.FromExpr.* @@ -90,7 +90,7 @@ object JsonConfig else '{ jc } } val jc3 = ${ - if !x.escapedStrings then '{ jc2.withoutEscapedStrings() } + if !x.escapedStrings then '{ jc2.withEscapedStrings() } else '{ jc2 } } jc3 @@ -152,7 +152,7 @@ object JsonConfig case '{ ($x: JsonConfig).withTypeHintLabel($v) } => Some(x.valueOrAbort.withTypeHintLabel(v.valueOrAbort)) case '{ ($x: JsonConfig).withTypeHintPolicy($v) } => Some(x.valueOrAbort.withTypeHintPolicy(v.valueOrAbort)) case '{ ($x: JsonConfig).withEnumsAsIds($v) } => Some(x.valueOrAbort.withEnumsAsIds(v.valueOrAbort)) - case '{ ($x: JsonConfig).withoutEscapedStrings() } => Some(x.valueOrAbort.withoutEscapedStrings()) + case '{ ($x: JsonConfig).withEscapedStrings() } => Some(x.valueOrAbort.withEscapedStrings()) } private[scalajack] given ToExpr[TryPolicy] with { diff --git a/src/main/scala/co.blocke.scalajack/json/JsonError.scala b/src/main/scala/co.blocke.scalajack/json/JsonError.scala index f790f1d0..6103ed01 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonError.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonError.scala @@ -28,3 +28,16 @@ case class JsonParseError(override val msg: String, context: reading.JsonSource) case ep => ("..." + js.substring(ep - 49, ep + 27) + "...", 52) } msg + s" at positio [${context.pos}]" + "\n" + clip.replaceAll("[\n\t]", "~") + "\n" + ("-" * dashes) + "^" + + // Temporary +case class JsonParseError2(override val msg: String, context: reading.JsonSource2) extends ParseError(msg + " at position " + context.pos) with NoStackTrace: + override val show: String = + val js = context.js.toString + val (clip, dashes) = context.pos match { + case ep if ep <= 50 && context.max < 80 => (js, ep) + case ep if ep <= 50 => (js.substring(0, 77) + "...", ep) + case ep if ep > 50 && ep + 30 >= context.max => + ("..." + js.substring(context.pos - 49), 52) + case ep => ("..." + js.substring(ep - 49, ep + 27) + "...", 52) + } + msg + s" at positio [${context.pos}]" + "\n" + clip.replaceAll("[\n\t]", "~") + "\n" + ("-" * dashes) + "^" diff --git a/src/main/scala/co.blocke.scalajack/json/reading/ClassDecoder.scala b/src/main/scala/co.blocke.scalajack/json/reading/ClassDecoder.scalax similarity index 100% rename from src/main/scala/co.blocke.scalajack/json/reading/ClassDecoder.scala rename to src/main/scala/co.blocke.scalajack/json/reading/ClassDecoder.scalax diff --git a/src/main/scala/co.blocke.scalajack/json/reading/JsonDecoder.scala b/src/main/scala/co.blocke.scalajack/json/reading/JsonDecoder.scalax similarity index 100% rename from src/main/scala/co.blocke.scalajack/json/reading/JsonDecoder.scala rename to src/main/scala/co.blocke.scalajack/json/reading/JsonDecoder.scalax diff --git a/src/main/scala/co.blocke.scalajack/json/reading/JsonParser.scala b/src/main/scala/co.blocke.scalajack/json/reading/JsonParser.scalax similarity index 100% rename from src/main/scala/co.blocke.scalajack/json/reading/JsonParser.scala rename to src/main/scala/co.blocke.scalajack/json/reading/JsonParser.scalax diff --git a/src/main/scala/co.blocke.scalajack/json/reading/JsonReaderUtil.scala b/src/main/scala/co.blocke.scalajack/json/reading/JsonReaderUtil.scala index 42979c52..611f831f 100644 --- a/src/main/scala/co.blocke.scalajack/json/reading/JsonReaderUtil.scala +++ b/src/main/scala/co.blocke.scalajack/json/reading/JsonReaderUtil.scala @@ -25,25 +25,30 @@ object JsonReaderUtil: // } // } + // Given an array of Any, produce a T (class instance) def classInstantiator[T: Type](ref: ClassRef[T])(using Quotes): Expr[Array[?] => T] = import quotes.reflect.* - val sym = TypeRepr.of[T].classSymbol.get + + val aTpr = TypeRepr.of[T] + val ctor = aTpr.typeSymbol.primaryConstructor + '{ (fieldValues: Array[?]) => ${ - val tree = Apply( - Select.unique(New(TypeIdent(sym)), ""), - ref.fields.zipWithIndex.map { case (f, i) => - f.fieldRef.refType match - case '[t] => - val idx = Expr(i) - '{ fieldValues($idx).asInstanceOf[t] }.asTerm - } - ) - tree.asExpr.asExprOf[T] + New(Inferred(aTpr)) + .select(ctor) + .appliedToArgs({ + ref.fields.map { case f => + f.fieldRef.refType match + case '[t] => // get the Type of each field (stored in fieldRef.refType) + val idx = Expr(f.index) + '{ fieldValues($idx).asInstanceOf[t] }.asTerm + } + }) + .asExprOf[T] } } - /* + /* def tupleInstantiator[T: Type](ref: TupleRef[T])(using Quotes): Expr[List[?] => T] = import quotes.reflect.* val sym = TypeRepr.of[T].classSymbol.get @@ -69,7 +74,7 @@ object JsonReaderUtil: tree.asExpr.asExprOf[T] } } - */ + */ // def classParseMap[T: Type](ref: ClassRef[T], root: ReaderModule)(using q: Quotes)(using // cache: scala.collection.mutable.HashMap[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]] diff --git a/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala b/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala index 7293f87c..b3d14c10 100644 --- a/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala +++ b/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala @@ -20,22 +20,22 @@ case class JsonSource(js: CharSequence): inline def here = js.charAt(i) - inline def read(): Char = + private var c: Char = 0 + inline def readChar(): Char = if i < max then - val c = history(i) + c = here i += 1 c else BUFFER_EXCEEDED - inline def readSkipWhitespace(): Char = + inline def readCharWS(): Char = var c: Char = 0 - while { c = read(); isWhitespace(c) } do () + while { c = readChar(); isWhitespace(c) && c != BUFFER_EXCEEDED } do () c - private inline def history(p: Int): Char = js.charAt(p) - - inline def retract() = i -= 1 + // inline def retract() = i -= 1 + // JSON definition of whitespace private inline def isWhitespace(c: Char): Boolean = (c: @switch) match { case ' ' => true @@ -51,10 +51,10 @@ case class JsonSource(js: CharSequence): case _ => false // Read, transforming escaped chars and stopping when we hit '"' - inline def readEscapedString(): Char = - read() match + inline def readEscapedChar(): Char = + readChar() match case '\\' => - val c2 = read() + val c2 = readChar() (c2: @switch) match case '"' | '\\' | '/' => c2 case 'b' => '\b' @@ -72,7 +72,7 @@ case class JsonSource(js: CharSequence): var i: Int = 0 var accum: Int = 0 while i < 4 do - var c = read().toInt + var c = readChar().toInt if c == BUFFER_EXCEEDED then throw JsonParseError("Unexpected EOB in string", this) c = if '0' <= c && c <= '9' then c - '0' @@ -86,19 +86,55 @@ case class JsonSource(js: CharSequence): //------- // returns false if 'null' found - def expectObjectStart(): Boolean = - readSkipWhitespace() match { + def expectFirstObjectField(fieldNameMatrix: StringMatrix): Int = + readCharWS() match { case '{' => - true + readCharWS() match { + case '"' => + var fi: Int = 0 + var bs: Long = fieldNameMatrix.initial + var c: Int = -1 + while { c = readEscapedChar(); c != END_OF_STRING && c != BUFFER_EXCEEDED } do { + bs = fieldNameMatrix.update(bs, fi, c) + fi += 1 + } + if readCharWS() != ':' then throw new JsonParseError(s"Expected ':' field separator", this) + bs = fieldNameMatrix.exact(bs, fi) + fieldNameMatrix.first(bs) + case '}' => -2 // end-of-object (empty, not null) + case c => throw new JsonParseError(s"Expected object field name or '}' but found '$c'", this) + } case 'n' => readChars(JsonSource.ull, "null") - false + -3 // null case c => throw new JsonParseError(s"Expected object start '{' but found '$c'", this) } + def expectObjectField(fieldNameMatrix: StringMatrix): Int = + readCharWS() match { + case ',' => + readCharWS() match { + case '"' => + var fi: Int = 0 + var bs: Long = fieldNameMatrix.initial + var c: Int = -1 + while { c = readEscapedChar(); c != END_OF_STRING && c != BUFFER_EXCEEDED } do { + bs = fieldNameMatrix.update(bs, fi, c) + fi += 1 + } + if readCharWS() != ':' then throw new JsonParseError(s"Expected ':' field separator", this) + bs = fieldNameMatrix.exact(bs, fi) + fieldNameMatrix.first(bs) + case c => throw new JsonParseError(s"Expected object field name but found '$c'", this) + } + case '}' => -2 // end-of-object + case c => throw new JsonParseError(s"Expected ',' or '}' but found '$c'", this) + } + def expectArrayStart(): Boolean = - readSkipWhitespace() match { + readCharWS() match { case '[' => + i += 1 true case 'n' => readChars(JsonSource.ull, "null") @@ -106,9 +142,11 @@ case class JsonSource(js: CharSequence): case c => throw new JsonParseError(s"Expected array start '[' but found '$c'", this) } + /// ------------------- Continue refresh here.... >>>>>>>> + // True if we got anything besides a ], False for ] def firstArrayElement(): Boolean = - (readSkipWhitespace(): @switch) match + (readCharWS(): @switch) match case ']' => false case _ => retract() @@ -116,7 +154,7 @@ case class JsonSource(js: CharSequence): // True if we got a comma, and False for ] def nextArrayElement(): Boolean = - (readSkipWhitespace(): @switch) match + (readCharWS(): @switch) match case ',' => true case ']' => @@ -125,7 +163,7 @@ case class JsonSource(js: CharSequence): // True if we got a string (implies a retraction), False for } def firstField(): Boolean = - (readSkipWhitespace(): @switch) match { + (readCharWS(): @switch) match { case '"' => true case '}' => false case c => @@ -134,7 +172,7 @@ case class JsonSource(js: CharSequence): // True if we got a comma, and False for } def nextField(): Boolean = - (readSkipWhitespace(): @switch) match { + (readCharWS(): @switch) match { case ',' => expectFieldValue = false true @@ -153,7 +191,7 @@ case class JsonSource(js: CharSequence): var fi: Int = 0 var bs: Long = fieldNameMatrix.initial var c: Int = -1 - while { c = readEscapedString(); c != END_OF_STRING } do { + while { c = readEscapedChar(); c != END_OF_STRING } do { bs = fieldNameMatrix.update(bs, fi, c) fi += 1 } @@ -163,7 +201,7 @@ case class JsonSource(js: CharSequence): // Value might be null! def expectString(): CharSequence = - readSkipWhitespace() match { + readCharWS() match { case '"' => retract() parseString() @@ -176,11 +214,12 @@ case class JsonSource(js: CharSequence): private def parseString(): CharSequence = charWithWS('"') val sb = new FastStringBuilder(64) - while true do - val c = readEscapedString() - if c == END_OF_STRING then return sb.buffer // mutable thing escapes, but cannot be changed - sb.append(c.toChar) - throw JsonParseError("Invalid string value detected", this) + var c: Char = END_OF_STRING + while + c = readEscapedChar() + c != END_OF_STRING + do sb.append(c.toChar) + sb.buffer inline def expectChar(): Char = expectString() match { @@ -189,7 +228,7 @@ case class JsonSource(js: CharSequence): } def expectBoolean(): Boolean = - (readSkipWhitespace(): @switch) match + (readCharWS(): @switch) match case 't' => readChars(JsonSource.rue, "true") true @@ -214,21 +253,21 @@ case class JsonSource(js: CharSequence): ): Unit = var i: Int = 0 while i < expect.length do - if read() != expect(i) then throw JsonParseError(s"Expected $errMsg", this) + if readChar() != expect(i) then throw JsonParseError(s"Expected $errMsg", this) i += 1 private def checkNumber(): Unit = - (readSkipWhitespace(): @switch) match + (readCharWS(): @switch) match case '-' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '.' => () case c => throw JsonParseError(s"Expected a number, got $c", this) retract() inline def charWithWS(c: Char): Unit = - val got = readSkipWhitespace() + val got = readCharWS() if got != c then throw JsonParseError(s"Expected '$c' got '$got'", this) def skipValue(): Unit = - (readSkipWhitespace(): @switch) match { + (readCharWS(): @switch) match { case 'n' => readChars(JsonSource.ull, "null") case 'f' => readChars(JsonSource.alse, "false") case 't' => readChars(JsonSource.rue, "true") @@ -255,10 +294,12 @@ case class JsonSource(js: CharSequence): } def skipNumber(): Unit = { - while isNumber(read()) do {} + while isNumber(readChar()) do {} retract() } def skipString(): Unit = var i: Int = 0 - while { i = readEscapedString(); i != -1 } do () + while { i = readCharWS(); i != -1 } do () + + inline def retract() = i -= 1 diff --git a/src/main/scala/co.blocke.scalajack/json/reading/JsonSource2.scala b/src/main/scala/co.blocke.scalajack/json/reading/JsonSource2.scala new file mode 100644 index 00000000..7e1c6c31 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/reading/JsonSource2.scala @@ -0,0 +1,150 @@ +package co.blocke.scalajack +package json +package reading + +import scala.annotation.* + +object JsonSource2: + protected val ull: Array[Char] = "ull".toCharArray + protected val _null: Array[Char] = "null".toCharArray + protected val alse: Array[Char] = "alse".toCharArray + protected val rue: Array[Char] = "rue".toCharArray + +case class JsonSource2(js: CharSequence): + private var i = 0 // The great, omnipresent index + private[json] val max = js.length // don't overrun this, or else... + + inline def here: Char = js.charAt(i) + inline def pos = i + + // Parse philosophy: After parsing a character, always leave index (i) ready to read the next + // character + + private inline def getCharWS: Char = + while i < max && here.isWhitespace do i += 1 + if i == max then BUFFER_EXCEEDED + else here + + private inline def expectChars( + expect: Array[Char], + errMsg: String + ): Unit = + if i + expect.length >= max then throw JsonParseError2("(a) Tried to read past end of JSON buffer", this) + var j = 0 + while j < expect.length && here == expect(j) do { i += 1; j += 1 } + if j == expect.length then () + else throw JsonParseError2(s"(a) Expected $errMsg", this) + + // -- Parse Objects + + inline def expectObject: Boolean = // returns false if null + getCharWS match { + case '{' => + i += 1 + true // true -> start of object found + case 'n' => + i += 1 + expectChars(JsonSource2.ull, "'{' or null") + false // false -> null found + case BUFFER_EXCEEDED => throw JsonParseError2("(1) Tried to read past end of JSON buffer", this) + case _ => + throw JsonParseError2("(1) Expected '{' or null", this) + } + + def expectFieldIndex(fieldNameMatrix: StringMatrix): Int = + (getCharWS: @switch) match { + case '"' => + var fi: Int = 0 + var bs: Long = fieldNameMatrix.initial + var c: Int = -1 + i += 1 + while i < max && here != '"' do + bs = fieldNameMatrix.update(bs, fi, here) + i += 1 + fi += 1 + bs = fieldNameMatrix.exact(bs, fi) + if i == max then throw JsonParseError2("(2) Tried to read past end of JSON buffer", this) + else + i += 1 + (getCharWS: @switch) match { + case ':' => + i += 1 + fieldNameMatrix.first(bs) + case BUFFER_EXCEEDED => throw JsonParseError2("(2) Tried to read past end of JSON buffer", this) + case _ => throw JsonParseError2(s"(2) Expected ':' but got '$here'", this) + } + case BUFFER_EXCEEDED => throw JsonParseError2("(2) Tried to read past end of JSON buffer", this) + case _ => throw JsonParseError2(s"(2) Expected start of field name (string) '\"' but got '$here'", this) + } + + inline def hasFields: Boolean = + (getCharWS: @switch) match { + case '}' => + i += 1 + false + case _ => true + } + + def nextField: Boolean = // true means there is another field. false means '}' -- end of object + (getCharWS: @switch) match { + case '}' => + i += 1 + false + case ',' => + i += 1 + true + case _ => + throw JsonParseError2(s"(3) Malformed JSON. Expected ',' field separator", this) + } + + // -- Parse Array + + inline def expectArray: Boolean = // returns false if null + getCharWS match { + case '[' => + i += 1 + true // true -> start of object found + case 'n' => + i += 1 + expectChars(JsonSource2.ull, "'[' or null") + false // false -> null found + case BUFFER_EXCEEDED => throw JsonParseError2("(4) Tried to read past end of JSON buffer", this) + case _ => + throw JsonParseError2("(4) Expected '[' or null", this) + } + + inline def hasElements: Boolean = + (getCharWS: @switch) match { + case ']' => + i += 1 + false + case _ => true + } + + def nextElement: Boolean = // true means there is another field. false means '}' -- end of object + (getCharWS: @switch) match { + case ']' => + i += 1 + false + case ',' => + i += 1 + true + case _ => + throw JsonParseError2(s"(5) Malformed JSON. Expected ',' field separator", this) + } + + // -- Parse JSON Simple Data Types + + def expectBoolean: Boolean = + getCharWS match { + case 't' => + i += 1 + expectChars(JsonSource2.rue, "true") + true + case 'f' => + i += 1 + expectChars(JsonSource2.alse, "false") + false + case BUFFER_EXCEEDED => throw JsonParseError2("(6) Tried to read past end of JSON buffer", this) + case _ => throw JsonParseError2(s"(6) Expected boolean value", this) + } \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json/reading/Numbers.scala b/src/main/scala/co.blocke.scalajack/json/reading/Numbers.scala index ba9736e8..30d43c29 100644 --- a/src/main/scala/co.blocke.scalajack/json/reading/Numbers.scala +++ b/src/main/scala/co.blocke.scalajack/json/reading/Numbers.scala @@ -555,13 +555,13 @@ object UnsafeNumbers { consume: Boolean, max_bits: Int ): java.math.BigInteger = { - var current: Int = in.read() + var current: Int = in.readChar() var negative = false if current == '-' then { negative = true - current = in.read() - } else if current == '+' then current = in.read() + current = in.readChar() + } else if current == '+' then current = in.readChar() if current == -1 then throw UnsafeNumber bigDecimal__(in, consume, negative, current, true, max_bits).unscaledValue @@ -576,15 +576,15 @@ object UnsafeNumbers { def long__(in: JsonSource, lower: Long, upper: Long, consume: Boolean): Long = { var current: Int = 0 - current = in.read() + current = in.readChar() if current == -1 then throw UnsafeNumber var negative = false if current == '-' then { negative = true - current = in.read() + current = in.readChar() if current == -1 then throw UnsafeNumber } else if current == '+' then { - current = in.read() + current = in.readChar() if current == -1 then throw UnsafeNumber } @@ -599,7 +599,7 @@ object UnsafeNumbers { else if accum == longunderflow && c == 9 then throw UnsafeNumber // count down, not up, because it is larger accum = accum * 10 - c // should never underflow - current = in.read() + current = in.readChar() }; current != -1 && isDigit(current) } do () @@ -620,7 +620,7 @@ object UnsafeNumbers { float_(new JsonSource(num), true, max_bits) def float_(in: JsonSource, consume: Boolean, max_bits: Int): Float = { - var current: Int = in.read() + var current: Int = in.readChar() var negative = false def readAll(s: String): Unit = { @@ -628,12 +628,12 @@ object UnsafeNumbers { val len = s.length while i < len do { - current = in.read() + current = in.readChar() if current != s(i) then throw UnsafeNumber i += 1 } - current = in.read() // to be consistent read the terminator + current = in.readChar() // to be consistent read the terminator if consume && current != -1 then throw UnsafeNumber } @@ -645,9 +645,9 @@ object UnsafeNumbers { if current == '-' then { negative = true - current = in.read() + current = in.readChar() } else if current == '+' then { - current = in.read() + current = in.readChar() } if current == 'I' then { @@ -669,18 +669,18 @@ object UnsafeNumbers { double_(new JsonSource(num), true, max_bits) def double_(in: JsonSource, consume: Boolean, max_bits: Int): Double = { - var current: Int = in.read() + var current: Int = in.readChar() var negative = false def readall(s: String): Unit = { var i = 0 val len = s.length while i < len do { - current = in.read() + current = in.readChar() if current != s(i) then throw UnsafeNumber i += 1 } - current = in.read() // to be consistent read the terminator + current = in.readChar() // to be consistent read the terminator if consume && current != -1 then throw UnsafeNumber } @@ -691,8 +691,8 @@ object UnsafeNumbers { if current == '-' then { negative = true - current = in.read() - } else if current == '+' then current = in.read() + current = in.readChar() + } else if current == '+' then current = in.readChar() if current == 'I' then { readall("nfinity") @@ -725,13 +725,13 @@ object UnsafeNumbers { consume: Boolean, max_bits: Int ): java.math.BigDecimal = { - var current: Int = in.read() + var current: Int = in.readChar() var negative = false if current == '-' then { negative = true - current = in.read() - } else if current == '+' then current = in.read() + current = in.readChar() + } else if current == '+' then current = in.readChar() if current == -1 then throw UnsafeNumber bigDecimal__(in, consume, negative, current, false, max_bits) @@ -753,7 +753,7 @@ object UnsafeNumbers { var exp: Int = 0 // implied def advance(): Boolean = { - current = in.read() + current = in.readChar() current != -1 } diff --git a/src/main/scala/co.blocke.scalajack/json/reading/StringMatrix.scala b/src/main/scala/co.blocke.scalajack/json/reading/StringMatrix.scala index aaf7219a..b2cbbe85 100644 --- a/src/main/scala/co.blocke.scalajack/json/reading/StringMatrix.scala +++ b/src/main/scala/co.blocke.scalajack/json/reading/StringMatrix.scala @@ -9,10 +9,6 @@ package reading // ScalaJack: Removal of ZIO's original aliases feature for speed--we don't need this. // final class StringMatrix(val xs: Array[String]) { - require(xs.forall(_.nonEmpty)) - require(xs.nonEmpty) - require(xs.length < 64) - val width = xs.length val height: Int = xs.map(_.length).max val lengths: Array[Int] = xs.map(_.length) diff --git a/src/main/scala/co.blocke.scalajack/run/Foo.scalax b/src/main/scala/co.blocke.scalajack/run/Foo.scalax new file mode 100644 index 00000000..28413353 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/run/Foo.scalax @@ -0,0 +1,351 @@ +package `co.blocke.scalajack`.run + +Codec: { + + //------------------------- Don't care about these--they appear to map index->field name, which we do effectively with StringMatrix + + def f0(i: scala.Int): java.lang.String = i match { + case 0 => + "person" + case 1 => + "hobbies" + case 2 => + "friends" + case 3 => + "pets" + } + def f1(`i₂`: scala.Int): java.lang.String = `i₂` match { + case 0 => + "name" + case 1 => + "species" + case 2 => + "age" + } + def f2(`i₃`: scala.Int): java.lang.String = `i₃` match { + case 0 => + "name" + case 1 => + "age" + case 2 => + "email" + } + def f3(`i₄`: scala.Int): java.lang.String = `i₄` match { + case 0 => + "name" + case 1 => + "age" + case 2 => + "address" + case 3 => + "email" + case 4 => + "phone_numbers" + case 5 => + "is_employed" + } + def f4(`i₅`: scala.Int): java.lang.String = `i₅` match { + case 0 => + "street" + case 1 => + "city" + case 2 => + "state" + case 3 => + "postal_code" + } + + //------------------------- Read (decode) Pet + + def d2(in: com.github.plokhotnyuk.jsoniter_scala.core.JsonReader, default: co.blocke.Pet): co.blocke.Pet = if (in.isNextToken(123)) { + var _name: java.lang.String = (null: java.lang.String) // <-- Secret revealed! They create local vars for each field then "new" at the end... no Array[Any] + var _species: java.lang.String = (null: java.lang.String) + var _age: scala.Int = 0 + var p0: scala.Int = 7 // <-- This seems to be a bit field used to ensure all required fields are set + if (in.isNextToken(125).unary_!) { + in.rollbackToken() + var l: scala.Int = -1 + while (l.<(0).||(in.isNextToken(44))) { + l = in.readKeyAsCharBuf() + if (in.isCharBufEqualsTo(l, "name")) { + if (p0.&(1).!=(0)) p0 = p0.^(1) else in.duplicatedKeyError(l) + _name = in.readString(_name) + } else if (in.isCharBufEqualsTo(l, "species")) { + if (p0.&(2).!=(0)) p0 = p0.^(2) else in.duplicatedKeyError(l) + _species = in.readString(_species) + } else if (in.isCharBufEqualsTo(l, "age")) { + if (p0.&(4).!=(0)) p0 = p0.^(4) else in.duplicatedKeyError(l) + _age = in.readInt() + } else in.skip() + } + if (in.isCurrentToken(125).unary_!) in.objectEndOrCommaError() else () + } else () + // Here's where we check the bit field to ensure all fields were set. We change the value from 0 (all fields required) + // to some other Int representation of bitmap based on which fields were either optional or had pre-supplied default values. + if (p0.!=(0)) in.requiredFieldError(f1(java.lang.Integer.numberOfTrailingZeros(p0))) else () + new co.blocke.Pet(_name, _species, _age) + } else in.readNullOrTokenError[co.blocke.Pet](default, 123) + + //------------------------- List/Seq done this way... (note structure, not necessarily that it's a separate function) + + def d1(`in₂`: com.github.plokhotnyuk.jsoniter_scala.core.JsonReader, `default₂`: scala.collection.immutable.List[co.blocke.Pet]): scala.collection.immutable.List[co.blocke.Pet] = if (`in₂`.isNextToken(91)) if (`in₂`.isNextToken(93)) `default₂` else { + `in₂`.rollbackToken() + val x: scala.collection.mutable.ListBuffer[co.blocke.Pet] = new scala.collection.mutable.ListBuffer[co.blocke.Pet]() + while ({ + x.addOne(d2(`in₂`, null)) + `in₂`.isNextToken(44) + }) () + if (`in₂`.isCurrentToken(93)) x.toList else `in₂`.arrayEndOrCommaError() + } else `in₂`.readNullOrTokenError[scala.collection.immutable.List[co.blocke.Pet]](`default₂`, 91) + + //------------------------- + + def d4(`in₃`: com.github.plokhotnyuk.jsoniter_scala.core.JsonReader, `default₃`: co.blocke.Friend): co.blocke.Friend = if (`in₃`.isNextToken(123)) { + var `_name₂`: java.lang.String = (null: java.lang.String) + var `_age₂`: scala.Int = 0 + var _email: java.lang.String = (null: java.lang.String) + var `p0₂`: scala.Int = 7 + if (`in₃`.isNextToken(125).unary_!) { + `in₃`.rollbackToken() + var `l₂`: scala.Int = -1 + while (`l₂`.<(0).||(`in₃`.isNextToken(44))) { + `l₂` = `in₃`.readKeyAsCharBuf() + if (`in₃`.isCharBufEqualsTo(`l₂`, "name")) { + if (`p0₂`.&(1).!=(0)) `p0₂` = `p0₂`.^(1) else `in₃`.duplicatedKeyError(`l₂`) + `_name₂` = `in₃`.readString(`_name₂`) + } else if (`in₃`.isCharBufEqualsTo(`l₂`, "age")) { + if (`p0₂`.&(2).!=(0)) `p0₂` = `p0₂`.^(2) else `in₃`.duplicatedKeyError(`l₂`) + `_age₂` = `in₃`.readInt() + } else if (`in₃`.isCharBufEqualsTo(`l₂`, "email")) { + if (`p0₂`.&(4).!=(0)) `p0₂` = `p0₂`.^(4) else `in₃`.duplicatedKeyError(`l₂`) + _email = `in₃`.readString(_email) + } else `in₃`.skip() + } + if (`in₃`.isCurrentToken(125).unary_!) `in₃`.objectEndOrCommaError() else () + } else () + if (`p0₂`.!=(0)) `in₃`.requiredFieldError(f2(java.lang.Integer.numberOfTrailingZeros(`p0₂`))) else () + new co.blocke.Friend(`_name₂`, `_age₂`, _email) + } else `in₃`.readNullOrTokenError[co.blocke.Friend](`default₃`, 123) + def d3(`in₄`: com.github.plokhotnyuk.jsoniter_scala.core.JsonReader, `default₄`: scala.collection.immutable.List[co.blocke.Friend]): scala.collection.immutable.List[co.blocke.Friend] = if (`in₄`.isNextToken(91)) if (`in₄`.isNextToken(93)) `default₄` else { + `in₄`.rollbackToken() + val `x₂`: scala.collection.mutable.ListBuffer[co.blocke.Friend] = new scala.collection.mutable.ListBuffer[co.blocke.Friend]() + while ({ + `x₂`.addOne(d4(`in₄`, null)) + `in₄`.isNextToken(44) + }) () + if (`in₄`.isCurrentToken(93)) `x₂`.toList else `in₄`.arrayEndOrCommaError() + } else `in₄`.readNullOrTokenError[scala.collection.immutable.List[co.blocke.Friend]](`default₄`, 91) + def d5(`in₅`: com.github.plokhotnyuk.jsoniter_scala.core.JsonReader, `default₅`: scala.collection.immutable.List[scala.Predef.String]): scala.collection.immutable.List[scala.Predef.String] = if (`in₅`.isNextToken(91)) if (`in₅`.isNextToken(93)) `default₅` else { + `in₅`.rollbackToken() + val `x₃`: scala.collection.mutable.ListBuffer[java.lang.String] = new scala.collection.mutable.ListBuffer[java.lang.String]() + while ({ + `x₃`.addOne(`in₅`.readString((null: java.lang.String))) + `in₅`.isNextToken(44) + }) () + if (`in₅`.isCurrentToken(93)) `x₃`.toList else `in₅`.arrayEndOrCommaError() + } else `in₅`.readNullOrTokenError[scala.collection.immutable.List[java.lang.String]](`default₅`, 91) + def d7(`in₆`: com.github.plokhotnyuk.jsoniter_scala.core.JsonReader, `default₆`: co.blocke.Address): co.blocke.Address = if (`in₆`.isNextToken(123)) { + var _street: java.lang.String = (null: java.lang.String) + var _city: java.lang.String = (null: java.lang.String) + var _state: java.lang.String = (null: java.lang.String) + var _postal_code: java.lang.String = (null: java.lang.String) + var `p0₃`: scala.Int = 15 + if (`in₆`.isNextToken(125).unary_!) { + `in₆`.rollbackToken() + var `l₃`: scala.Int = -1 + while (`l₃`.<(0).||(`in₆`.isNextToken(44))) { + `l₃` = `in₆`.readKeyAsCharBuf() + if (`in₆`.isCharBufEqualsTo(`l₃`, "street")) { + if (`p0₃`.&(1).!=(0)) `p0₃` = `p0₃`.^(1) else `in₆`.duplicatedKeyError(`l₃`) + _street = `in₆`.readString(_street) + } else if (`in₆`.isCharBufEqualsTo(`l₃`, "city")) { + if (`p0₃`.&(2).!=(0)) `p0₃` = `p0₃`.^(2) else `in₆`.duplicatedKeyError(`l₃`) + _city = `in₆`.readString(_city) + } else if (`in₆`.isCharBufEqualsTo(`l₃`, "state")) { + if (`p0₃`.&(4).!=(0)) `p0₃` = `p0₃`.^(4) else `in₆`.duplicatedKeyError(`l₃`) + _state = `in₆`.readString(_state) + } else if (`in₆`.isCharBufEqualsTo(`l₃`, "postal_code")) { + if (`p0₃`.&(8).!=(0)) `p0₃` = `p0₃`.^(8) else `in₆`.duplicatedKeyError(`l₃`) + _postal_code = `in₆`.readString(_postal_code) + } else `in₆`.skip() + } + if (`in₆`.isCurrentToken(125).unary_!) `in₆`.objectEndOrCommaError() else () + } else () + if (`p0₃`.!=(0)) `in₆`.requiredFieldError(f4(java.lang.Integer.numberOfTrailingZeros(`p0₃`))) else () + new co.blocke.Address(_street, _city, _state, _postal_code) + } else `in₆`.readNullOrTokenError[co.blocke.Address](`default₆`, 123) + def d6(`in₇`: com.github.plokhotnyuk.jsoniter_scala.core.JsonReader, `default₇`: co.blocke.Person): co.blocke.Person = if (`in₇`.isNextToken(123)) { + var `_name₃`: java.lang.String = (null: java.lang.String) + var `_age₃`: scala.Int = 0 + var _address: co.blocke.Address = null + var `_email₂`: java.lang.String = (null: java.lang.String) + var _phone_numbers: scala.collection.immutable.List[scala.Predef.String] = scala.Nil + var _is_employed: scala.Boolean = false + var `p0₄`: scala.Int = 63 + if (`in₇`.isNextToken(125).unary_!) { + `in₇`.rollbackToken() + var `l₄`: scala.Int = -1 + while (`l₄`.<(0).||(`in₇`.isNextToken(44))) { + `l₄` = `in₇`.readKeyAsCharBuf() + if (`in₇`.isCharBufEqualsTo(`l₄`, "name")) { + if (`p0₄`.&(1).!=(0)) `p0₄` = `p0₄`.^(1) else `in₇`.duplicatedKeyError(`l₄`) + `_name₃` = `in₇`.readString(`_name₃`) + } else if (`in₇`.isCharBufEqualsTo(`l₄`, "age")) { + if (`p0₄`.&(2).!=(0)) `p0₄` = `p0₄`.^(2) else `in₇`.duplicatedKeyError(`l₄`) + `_age₃` = `in₇`.readInt() + } else if (`in₇`.isCharBufEqualsTo(`l₄`, "address")) { + if (`p0₄`.&(4).!=(0)) `p0₄` = `p0₄`.^(4) else `in₇`.duplicatedKeyError(`l₄`) + _address = d7(`in₇`, _address) + } else if (`in₇`.isCharBufEqualsTo(`l₄`, "email")) { + if (`p0₄`.&(8).!=(0)) `p0₄` = `p0₄`.^(8) else `in₇`.duplicatedKeyError(`l₄`) + `_email₂` = `in₇`.readString(`_email₂`) + } else if (`in₇`.isCharBufEqualsTo(`l₄`, "phone_numbers")) { + if (`p0₄`.&(16).!=(0)) `p0₄` = `p0₄`.^(16) else `in₇`.duplicatedKeyError(`l₄`) + _phone_numbers = d5(`in₇`, _phone_numbers) + } else if (`in₇`.isCharBufEqualsTo(`l₄`, "is_employed")) { + if (`p0₄`.&(32).!=(0)) `p0₄` = `p0₄`.^(32) else `in₇`.duplicatedKeyError(`l₄`) + _is_employed = `in₇`.readBoolean() + } else `in₇`.skip() + } + if (`in₇`.isCurrentToken(125).unary_!) `in₇`.objectEndOrCommaError() else () + } else () + if (`p0₄`.&(47).!=(0)) `in₇`.requiredFieldError(f3(java.lang.Integer.numberOfTrailingZeros(`p0₄`.&(47)))) else () + new co.blocke.Person(`_name₃`, `_age₃`, _address, `_email₂`, _phone_numbers, _is_employed) + } else `in₇`.readNullOrTokenError[co.blocke.Person](`default₇`, 123) + def d0(`in₈`: com.github.plokhotnyuk.jsoniter_scala.core.JsonReader, `default₈`: co.blocke.Record): co.blocke.Record = if (`in₈`.isNextToken(123)) { + var _person: co.blocke.Person = null + var _hobbies: scala.collection.immutable.List[scala.Predef.String] = scala.Nil + var _friends: scala.collection.immutable.List[co.blocke.Friend] = scala.Nil + var _pets: scala.collection.immutable.List[co.blocke.Pet] = scala.Nil + var `p0₅`: scala.Int = 15 + if (`in₈`.isNextToken(125).unary_!) { + `in₈`.rollbackToken() + var `l₅`: scala.Int = -1 + while (`l₅`.<(0).||(`in₈`.isNextToken(44))) { + `l₅` = `in₈`.readKeyAsCharBuf() + if (`in₈`.isCharBufEqualsTo(`l₅`, "person")) { + if (`p0₅`.&(1).!=(0)) `p0₅` = `p0₅`.^(1) else `in₈`.duplicatedKeyError(`l₅`) + _person = d6(`in₈`, _person) + } else if (`in₈`.isCharBufEqualsTo(`l₅`, "hobbies")) { + if (`p0₅`.&(2).!=(0)) `p0₅` = `p0₅`.^(2) else `in₈`.duplicatedKeyError(`l₅`) + _hobbies = d5(`in₈`, _hobbies) + } else if (`in₈`.isCharBufEqualsTo(`l₅`, "friends")) { + if (`p0₅`.&(4).!=(0)) `p0₅` = `p0₅`.^(4) else `in₈`.duplicatedKeyError(`l₅`) + _friends = d3(`in₈`, _friends) + } else if (`in₈`.isCharBufEqualsTo(`l₅`, "pets")) { + if (`p0₅`.&(8).!=(0)) `p0₅` = `p0₅`.^(8) else `in₈`.duplicatedKeyError(`l₅`) + _pets = d1(`in₈`, _pets) + } else `in₈`.skip() + } + if (`in₈`.isCurrentToken(125).unary_!) `in₈`.objectEndOrCommaError() else () + } else () + if (`p0₅`.&(1).!=(0)) `in₈`.requiredFieldError(f0(java.lang.Integer.numberOfTrailingZeros(`p0₅`.&(1)))) else () + new co.blocke.Record(_person, _hobbies, _friends, _pets) + } else `in₈`.readNullOrTokenError[co.blocke.Record](`default₈`, 123) + def e2(`x₄`: co.blocke.Address, out: com.github.plokhotnyuk.jsoniter_scala.core.JsonWriter): scala.Unit = { + out.writeObjectStart() + out.writeNonEscapedAsciiKey("street") + out.writeVal(`x₄`.street) + out.writeNonEscapedAsciiKey("city") + out.writeVal(`x₄`.city) + out.writeNonEscapedAsciiKey("state") + out.writeVal(`x₄`.state) + out.writeNonEscapedAsciiKey("postal_code") + out.writeVal(`x₄`.postal_code) + out.writeObjectEnd() + } + def e3(`x₅`: scala.collection.immutable.List[scala.Predef.String] & scala.collection.Iterable[_ >: scala.Nothing <: scala.Any], `out₂`: com.github.plokhotnyuk.jsoniter_scala.core.JsonWriter): scala.Unit = { + `out₂`.writeArrayStart() + var `l₆`: scala.collection.immutable.List[java.lang.String] = `x₅` + while (`l₆`.ne(scala.Nil)) { + `out₂`.writeVal(`l₆`.head) + `l₆` = `l₆`.tail + } + `out₂`.writeArrayEnd() + } + def e1(`x₆`: co.blocke.Person, `out₃`: com.github.plokhotnyuk.jsoniter_scala.core.JsonWriter): scala.Unit = { + `out₃`.writeObjectStart() + `out₃`.writeNonEscapedAsciiKey("name") + `out₃`.writeVal(`x₆`.name) + `out₃`.writeNonEscapedAsciiKey("age") + `out₃`.writeVal(`x₆`.age) + `out₃`.writeNonEscapedAsciiKey("address") + e2(`x₆`.address, `out₃`) + `out₃`.writeNonEscapedAsciiKey("email") + `out₃`.writeVal(`x₆`.email) + val v: scala.collection.immutable.List[scala.Predef.String] & scala.collection.Iterable[_ >: scala.Nothing <: scala.Any] = `x₆`.phone_numbers + if (v.isEmpty.unary_!) { + `out₃`.writeNonEscapedAsciiKey("phone_numbers") + e3(v, `out₃`) + } else () + `out₃`.writeNonEscapedAsciiKey("is_employed") + `out₃`.writeVal(`x₆`.is_employed) + `out₃`.writeObjectEnd() + } + def e5(`x₇`: co.blocke.Friend, `out₄`: com.github.plokhotnyuk.jsoniter_scala.core.JsonWriter): scala.Unit = { + `out₄`.writeObjectStart() + `out₄`.writeNonEscapedAsciiKey("name") + `out₄`.writeVal(`x₇`.name) + `out₄`.writeNonEscapedAsciiKey("age") + `out₄`.writeVal(`x₇`.age) + `out₄`.writeNonEscapedAsciiKey("email") + `out₄`.writeVal(`x₇`.email) + `out₄`.writeObjectEnd() + } + def e4(`x₈`: scala.collection.immutable.List[co.blocke.Friend] & scala.collection.Iterable[_ >: scala.Nothing <: scala.Any], `out₅`: com.github.plokhotnyuk.jsoniter_scala.core.JsonWriter): scala.Unit = { + `out₅`.writeArrayStart() + var `l₇`: scala.collection.immutable.List[co.blocke.Friend] = `x₈` + while (`l₇`.ne(scala.Nil)) { + e5(`l₇`.head, `out₅`) + `l₇` = `l₇`.tail + } + `out₅`.writeArrayEnd() + } + def e7(`x₉`: co.blocke.Pet, `out₆`: com.github.plokhotnyuk.jsoniter_scala.core.JsonWriter): scala.Unit = { + `out₆`.writeObjectStart() + `out₆`.writeNonEscapedAsciiKey("name") + `out₆`.writeVal(`x₉`.name) + `out₆`.writeNonEscapedAsciiKey("species") + `out₆`.writeVal(`x₉`.species) + `out₆`.writeNonEscapedAsciiKey("age") + `out₆`.writeVal(`x₉`.age) + `out₆`.writeObjectEnd() + } + def e6(`x₁₀`: scala.collection.immutable.List[co.blocke.Pet] & scala.collection.Iterable[_ >: scala.Nothing <: scala.Any], `out₇`: com.github.plokhotnyuk.jsoniter_scala.core.JsonWriter): scala.Unit = { + `out₇`.writeArrayStart() + var `l₈`: scala.collection.immutable.List[co.blocke.Pet] = `x₁₀` + while (`l₈`.ne(scala.Nil)) { + e7(`l₈`.head, `out₇`) + `l₈` = `l₈`.tail + } + `out₇`.writeArrayEnd() + } + def e0(`x₁₁`: co.blocke.Record, `out₈`: com.github.plokhotnyuk.jsoniter_scala.core.JsonWriter): scala.Unit = { + `out₈`.writeObjectStart() + `out₈`.writeNonEscapedAsciiKey("person") + e1(`x₁₁`.person, `out₈`) + val `v₂`: scala.collection.immutable.List[scala.Predef.String] & scala.collection.Iterable[_ >: scala.Nothing <: scala.Any] = `x₁₁`.hobbies + if (`v₂`.isEmpty.unary_!) { + `out₈`.writeNonEscapedAsciiKey("hobbies") + e3(`v₂`, `out₈`) + } else () + val `v₃`: scala.collection.immutable.List[co.blocke.Friend] & scala.collection.Iterable[_ >: scala.Nothing <: scala.Any] = `x₁₁`.friends + if (`v₃`.isEmpty.unary_!) { + `out₈`.writeNonEscapedAsciiKey("friends") + e4(`v₃`, `out₈`) + } else () + val `v₄`: scala.collection.immutable.List[co.blocke.Pet] & scala.collection.Iterable[_ >: scala.Nothing <: scala.Any] = `x₁₁`.pets + if (`v₄`.isEmpty.unary_!) { + `out₈`.writeNonEscapedAsciiKey("pets") + e6(`v₄`, `out₈`) + } else () + `out₈`.writeObjectEnd() + } + final class $anon() extends com.github.plokhotnyuk.jsoniter_scala.core.JsonValueCodec[co.blocke.Record] { + def nullValue: co.blocke.Record = null + def decodeValue(`in₉`: com.github.plokhotnyuk.jsoniter_scala.core.JsonReader, `default₉`: co.blocke.Record): co.blocke.Record = d0(`in₉`, `default₉`) + def encodeValue(`x₁₂`: co.blocke.Record, `out₉`: com.github.plokhotnyuk.jsoniter_scala.core.JsonWriter): scala.Unit = e0(`x₁₂`, `out₉`) + } + + (new $anon(): com.github.plokhotnyuk.jsoniter_scala.core.JsonValueCodec[co.blocke.Record]) +} \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index 7e2ca062..44281c25 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -34,7 +34,7 @@ object RunMe extends App: // def parseString(in: reading.JsonSource): CharSequence = // // charWithWS(in, '"') - // val sb = new reading.FastStringBuilder(64) + // val sb = new reading.FastStringBuilder(64)x // while true do // val c = in.readEscapedString() // if c == END_OF_STRING then return sb.buffer // mutable thing escapes, but cannot be changed @@ -45,13 +45,36 @@ object RunMe extends App: import co.blocke.scalajack.json.run.Record println("\n") - implicit val blah: ScalaJack[Foom] = sj[Foom] - println(ScalaJack[Foom].fromJson("""{"a": -12, "b":"Greg Z"}""")) + // implicit val blah: ScalaJack[Foom] = sj[Foom] + // println(ScalaJack[Foom].fromJson("""{"a": -12, "b":"Greg Z"}""")) // implicit val blah: ScalaJack[List[Queue[Int]]] = sj[List[Queue[Int]]] // println(ScalaJack[List[Queue[Int]]].fromJson("null")) // "[[1,2,3],[4,5,6],[7,8,9]]")) - // implicit val blah: ScalaJack[Record] = sj[Record] - // println(ScalaJack[Record].fromJson(co.blocke.scalajack.run.jsData)) + implicit val blah: ScalaJack[Record] = sj[Record] + println(blah.fromJson(jsData)) println("done.") + + // def r0(in: JsonSource): Foom = { + // val fieldMatrix: StringMatrix = new StringMatrix(Array("a", "b")) + // val args: Array[Any] = Array[Any](Foom.$lessinit$greater$default$1, Foom.$lessinit$greater$default$2)(ClassTag.Any) + + // if !in.expectObjectStart() then null.asInstanceOf[Foom] + // else { + // if in.here.!=('}') then + // while { + // in.expectFieldName(fieldMatrix) match { + // case 0 => + // args(0) = in.expectInt() + // case 1 => + // args(1) = in.expectString().toString() + // case -1 => + // in.skipValue() + // } + // in.nextField() + // } do () + // else in.read() + // ((fieldValues: Array[Any]) => new Foom(fieldValues(0).asInstanceOf[scala.Int], fieldValues(1).asInstanceOf[String])).apply(args) + // } + // } diff --git a/src/main/scala/co.blocke.scalajack/run/Record.scala b/src/main/scala/co.blocke.scalajack/run/Record.scala index 94322c55..2dfcbd66 100644 --- a/src/main/scala/co.blocke.scalajack/run/Record.scala +++ b/src/main/scala/co.blocke.scalajack/run/Record.scala @@ -86,7 +86,7 @@ given EmptyString: Newtype[String] with case class Person2(age: XList) -case class Foom(a: Int = 5, b: String = "Wow") +case class Foom(a: Int, b: String) val jsData = """{ diff --git a/src/test/scala/co.blocke.scalajack/json/misc/MiscTests.scala b/src/test/scala/co.blocke.scalajack/json/misc/MiscTests.scala index 3b605ad8..ff4418d0 100644 --- a/src/test/scala/co.blocke.scalajack/json/misc/MiscTests.scala +++ b/src/test/scala/co.blocke.scalajack/json/misc/MiscTests.scala @@ -25,10 +25,12 @@ class MiscSpec() extends AnyFunSpec with JsonMatchers: val js = sj[StringHolder].toJson(inst) js should matchJson("""{"a":"This is a \"strange\" test\\non another level."}""") } - it("String without escaping must work (bad JSON, but proves escape can be turned off)") { + it("String escaping must work (bad JSON, but proves escape can be turned off)") { val inst = StringHolder("""This is a "strange" test\non another level.""") - val js = sj[StringHolder](JsonConfig.withoutEscapedStrings()).toJson(inst) - js should equal("""{"a":"This is a "strange" test\non another level."}""") + val js1 = sj[StringHolder].toJson(inst) + val js2 = sj[StringHolder](JsonConfig.withEscapedStrings()).toJson(inst) + js1 should equal("""{"a":"This is a "strange" test\non another level."}""") + js2 should equal("""{"a":"This is a "strange" test\non another level."}""") } it("NeoType integration must work") { val inst = Validated(NonEmptyString("Mike"), XList(List("x", "y", "z")), List(EmptyString(""), EmptyString(""), EmptyString(""))) From 0ca9f0dea40167783e0086a413dda16ab90f79a8 Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Wed, 28 Feb 2024 23:19:44 -0600 Subject: [PATCH 42/65] test of Jsoniter parse string --- benchmark/build.sbt | 2 +- build.sbt | 6 +- project/metals.sbt | 2 +- project/plugins.sbt | 1 + .../scalajack/util/ByteArrayAccess.java | 46 ++++ .../scala/co.blocke.scalajack/ScalaJack.scala | 2 +- .../json/JsonCodecMaker.scala | 36 ++- .../{JsonReader.scala => JsonReader.scalax} | 0 ...ption.scala => JsonReaderException.scalax} | 0 .../exp/{package.scala => package.scalax} | 0 .../json/reading/ByteArrayAccess.scalax} | 4 +- .../json/reading/JsonSource.scala | 33 ++- .../json/reading/JsonSource2.scala | 110 ++++----- .../json/reading/Numbers.scala | 16 +- .../json/reading/ParseString.scala | 226 ++++++++++++++++++ .../scala/co.blocke.scalajack/run/Play.scala | 110 +++++++++ 16 files changed, 493 insertions(+), 101 deletions(-) create mode 100644 src/main/java/co/blocke/scalajack/util/ByteArrayAccess.java rename src/main/scala/co.blocke.scalajack/json/exp/{JsonReader.scala => JsonReader.scalax} (100%) rename src/main/scala/co.blocke.scalajack/json/exp/{JsonReaderException.scala => JsonReaderException.scalax} (100%) rename src/main/scala/co.blocke.scalajack/json/exp/{package.scala => package.scalax} (100%) rename src/main/{java/co/blocke/scalajack/json/exp/ByteArrayAccess.java => scala/co.blocke.scalajack/json/reading/ByteArrayAccess.scalax} (96%) create mode 100644 src/main/scala/co.blocke.scalajack/json/reading/ParseString.scala diff --git a/benchmark/build.sbt b/benchmark/build.sbt index 64615583..e493b031 100644 --- a/benchmark/build.sbt +++ b/benchmark/build.sbt @@ -36,7 +36,7 @@ lazy val benchmark = project libraryDependencies ++= Seq( "org.playframework" %% "play-json" % "3.0.1", "io.argonaut" %% "argonaut" % "6.3.9", - "co.blocke" %% "scalajack" % "c8b0d6_unknown", + "co.blocke" %% "scalajack" % "241661_unknown", "co.blocke" %% "scala-reflection" % "sj_fixes_f43af7", "dev.zio" %% "zio-json" % "0.6.1", "org.typelevel" %% "fabric-core" % "1.12.6", diff --git a/build.sbt b/build.sbt index b4dd583d..e89ec5f7 100644 --- a/build.sbt +++ b/build.sbt @@ -19,7 +19,7 @@ inThisBuild(List( name := "scalajack" ThisBuild / organization := "co.blocke" -ThisBuild / scalaVersion := "3.3.0" +ThisBuild / scalaVersion := "3.3.2" lazy val root = project .in(file(".")) @@ -38,6 +38,7 @@ lazy val root = project "co.blocke" %% "scala-reflection" % "sj_fixes_f43af7", "org.apache.commons" % "commons-text" % "1.11.0", "io.github.kitlangton" %% "neotype" % "0.0.9", + "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core" % "2.24.5-SNAPSHOT", "org.scalatest" %% "scalatest" % "3.2.17" % Test, "org.json4s" %% "json4s-core" % "4.0.6" % Test, "org.json4s" %% "json4s-native" % "4.0.6" % Test @@ -77,8 +78,9 @@ lazy val compilerOptions = Seq( "-feature", "-language:implicitConversions", "-deprecation", - // "-explain", + "-explain", "-encoding", "utf8" ) +//enablePlugins(ScalaNativePlugin) \ No newline at end of file diff --git a/project/metals.sbt b/project/metals.sbt index cbb25c6a..119c9296 100644 --- a/project/metals.sbt +++ b/project/metals.sbt @@ -2,5 +2,5 @@ // This file enables sbt-bloop to create bloop config files. -addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.5.11") +addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.5.15") diff --git a/project/plugins.sbt b/project/plugins.sbt index 2424290e..4034b53b 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -3,3 +3,4 @@ addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.5.11") addSbtPlugin("org.typelevel" % "sbt-typelevel-sonatype-ci-release" % "0.5.0-M6") addSbtPlugin("org.typelevel" % "sbt-typelevel-github-actions" % "0.4.16") addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.2") +// addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.17") diff --git a/src/main/java/co/blocke/scalajack/util/ByteArrayAccess.java b/src/main/java/co/blocke/scalajack/util/ByteArrayAccess.java new file mode 100644 index 00000000..b06af59c --- /dev/null +++ b/src/main/java/co/blocke/scalajack/util/ByteArrayAccess.java @@ -0,0 +1,46 @@ +package co.blocke.scalajack.util; + +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; +import java.nio.ByteOrder; + +public class ByteArrayAccess { // FIXME: Use Java wrapper as w/a for missing support of @PolymorphicSignature methods in Scala 3, see: https://github.com/lampepfl/dotty/issues/11332 + private static final VarHandle VH_LONG = + MethodHandles.byteArrayViewVarHandle(long[].class, ByteOrder.LITTLE_ENDIAN); + private static final VarHandle VH_INT = + MethodHandles.byteArrayViewVarHandle(int[].class, ByteOrder.LITTLE_ENDIAN); + private static final VarHandle VH_SHORT = + MethodHandles.byteArrayViewVarHandle(short[].class, ByteOrder.LITTLE_ENDIAN); + private static final VarHandle VH_LONG_REVERSED = + MethodHandles.byteArrayViewVarHandle(long[].class, ByteOrder.BIG_ENDIAN); + private static final VarHandle VH_INT_REVERSED = + MethodHandles.byteArrayViewVarHandle(int[].class, ByteOrder.BIG_ENDIAN); + + static public void setLong(byte[] buf, int pos, long value) { + VH_LONG.set(buf, pos, value); + } + + static public long getLong(byte[] buf, int pos) { + return (long) VH_LONG.get(buf, pos); + } + + static public void setInt(byte[] buf, int pos, int value) { + VH_INT.set(buf, pos, value); + } + + static public int getInt(byte[] buf, int pos) { + return (int) VH_INT.get(buf, pos); + } + + static public void setShort(byte[] buf, int pos, short value) { + VH_SHORT.set(buf, pos, value); + } + + static public void setLongReversed(byte[] buf, int pos, long value) { + VH_LONG_REVERSED.set(buf, pos, value); + } + + static public int getIntReversed(byte[] buf, int pos) { + return (int) VH_INT_REVERSED.get(buf, pos); + } +} \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/ScalaJack.scala b/src/main/scala/co.blocke.scalajack/ScalaJack.scala index cc7d4a2a..d6c50c8d 100644 --- a/src/main/scala/co.blocke.scalajack/ScalaJack.scala +++ b/src/main/scala/co.blocke.scalajack/ScalaJack.scala @@ -9,7 +9,7 @@ import json.* case class ScalaJack[T](jsonCodec: JsonCodec[T]): // extends JsonCodec[T] //with YamlCodec with MsgPackCodec def fromJson(js: String): T = // Either[JsonParseError, T] = - jsonCodec.decodeValue(reading.JsonSource(js)) + jsonCodec.decodeValue(reading.JsonSource(js, js.getBytes)) val out = writing.JsonOutput() // let's clear & re-use JsonOutput--avoid re-allocating all the internal buffer space def toJson(a: T): String = diff --git a/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala b/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala index 0f174fae..078ae3c7 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala @@ -813,13 +813,13 @@ object JsonCodecMaker: import scala.reflect.ClassTag def ofArray[T](xs: Seq[Expr[T]])(using Type[T])(using Quotes): Expr[Array[T]] = '{ scala.Array.apply[T](${ quoted.Varargs(xs) }*)(${ Expr.summon[ClassTag[T]].get }) } - + def genDecFnBody[T: Type](r: RTypeRef[?], in: Expr[JsonSource])(using Quotes): Expr[Unit] = import quotes.reflect.* def typeArgs(tpe: TypeRepr): List[TypeRepr] = tpe match case AppliedType(_, typeArgs) => typeArgs.map(_.dealias) - case _ => Nil + case _ => Nil r.refType match // refType is Type[r.R] case '[b] => @@ -832,52 +832,50 @@ object JsonCodecMaker: val tpe = TypeRepr.of[b] val classCompanion = tpe.typeSymbol.companionClass val companionModule = tpe.typeSymbol.companionModule - val together = t.fields.map{ oneField => + val together = t.fields.map { oneField => oneField.fieldRef.refType match { - case '[f] => - val dvMembers = classCompanion.methodMember("$lessinit$greater$default$" + (oneField.index+1)) - val sym = Symbol.newVal(Symbol.spliceOwner, "_" + oneField.name, TypeRepr.of[f], Flags.Mutable, Symbol.noSymbol) + case '[f] => + val dvMembers = classCompanion.methodMember("$lessinit$greater$default$" + (oneField.index + 1)) + val sym = Symbol.newVal(Symbol.spliceOwner, "_" + oneField.name, TypeRepr.of[f], Flags.Mutable, Symbol.noSymbol) val fieldSymRef = Ident(sym.termRef) val caseDef = CaseDef( Literal(IntConstant(oneField.index)), None, Assign(fieldSymRef, genReadVal[f](oneField.fieldRef.asInstanceOf[RTypeRef[f]], in).asTerm) ) - if (dvMembers.isEmpty) - (ValDef(sym, Some(oneField.fieldRef.unitVal.asTerm)), - caseDef, fieldSymRef) - else + if dvMembers.isEmpty then (ValDef(sym, Some(oneField.fieldRef.unitVal.asTerm)), caseDef, fieldSymRef) + else val methodSymbol = dvMembers.head val dvSelectNoTArgs = Ref(companionModule).select(methodSymbol) val dvSelect = methodSymbol.paramSymss match case Nil => dvSelectNoTArgs - case List(params) if (params.exists(_.isTypeParam)) => typeArgs(tpe) match - case Nil => ??? //throw JsonParseError("Expected an applied type", ???) - case typeArgs => TypeApply(dvSelectNoTArgs, typeArgs.map(Inferred(_))) - case _ => ??? //fail(s"Default method for ${symbol.name} of class ${tpe.show} have a complex " + + case List(params) if (params.exists(_.isTypeParam)) => + typeArgs(tpe) match + case Nil => ??? // throw JsonParseError("Expected an applied type", ???) + case typeArgs => TypeApply(dvSelectNoTArgs, typeArgs.map(Inferred(_))) + case _ => ??? // fail(s"Default method for ${symbol.name} of class ${tpe.show} have a complex " + // s"parameter list: ${methodSymbol.paramSymss}") (ValDef(sym, Some(dvSelect)), caseDef, fieldSymRef) } } val (varDefs, caseDefs, idents) = together.unzip3 - val argss = List( idents ) + val argss = List(idents) val primaryConstructor = tpe.classSymbol.get.primaryConstructor val constructorNoTypes = Select(New(Inferred(tpe)), primaryConstructor) val constructor = typeArgs(tpe) match - case Nil => constructorNoTypes + case Nil => constructorNoTypes case typeArgs => TypeApply(constructorNoTypes, typeArgs.map(Inferred(_))) val instantiateClass = argss.tail.foldLeft(Apply(constructor, argss.head))((acc, args) => Apply(acc, args)) val parseLoop = '{ val fieldMatrix = new StringMatrix($fieldNames) var fldIdx = $in.expectFirstObjectField(fieldMatrix) - while( fldIdx >= -1 ) do // -2: end-of-object, -3: null (-1: unknown field -> skip) + while fldIdx >= -1 do // -2: end-of-object, -3: null (-1: unknown field -> skip) ${ Match('{ fldIdx }.asTerm, caseDefs :+ CaseDef(Literal(IntConstant(-1)), None, '{ $in.skipValue() }.asTerm)).asExprOf[Any] } fldIdx = $in.expectObjectField(fieldMatrix) if fldIdx == -3 then null.asInstanceOf[T] - else - ${instantiateClass.asExprOf[T]} + else ${ instantiateClass.asExprOf[T] } }.asTerm Block(varDefs, parseLoop).asExprOf[T] diff --git a/src/main/scala/co.blocke.scalajack/json/exp/JsonReader.scala b/src/main/scala/co.blocke.scalajack/json/exp/JsonReader.scalax similarity index 100% rename from src/main/scala/co.blocke.scalajack/json/exp/JsonReader.scala rename to src/main/scala/co.blocke.scalajack/json/exp/JsonReader.scalax diff --git a/src/main/scala/co.blocke.scalajack/json/exp/JsonReaderException.scala b/src/main/scala/co.blocke.scalajack/json/exp/JsonReaderException.scalax similarity index 100% rename from src/main/scala/co.blocke.scalajack/json/exp/JsonReaderException.scala rename to src/main/scala/co.blocke.scalajack/json/exp/JsonReaderException.scalax diff --git a/src/main/scala/co.blocke.scalajack/json/exp/package.scala b/src/main/scala/co.blocke.scalajack/json/exp/package.scalax similarity index 100% rename from src/main/scala/co.blocke.scalajack/json/exp/package.scala rename to src/main/scala/co.blocke.scalajack/json/exp/package.scalax diff --git a/src/main/java/co/blocke/scalajack/json/exp/ByteArrayAccess.java b/src/main/scala/co.blocke.scalajack/json/reading/ByteArrayAccess.scalax similarity index 96% rename from src/main/java/co/blocke/scalajack/json/exp/ByteArrayAccess.java rename to src/main/scala/co.blocke.scalajack/json/reading/ByteArrayAccess.scalax index d85cc1ad..63e99f66 100644 --- a/src/main/java/co/blocke/scalajack/json/exp/ByteArrayAccess.java +++ b/src/main/scala/co.blocke.scalajack/json/reading/ByteArrayAccess.scalax @@ -1,4 +1,6 @@ -package co.blocke.scalajack.json.exp; +package co.blocke.scalajack +package json +package reading import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; diff --git a/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala b/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala index b3d14c10..b74494aa 100644 --- a/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala +++ b/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala @@ -11,11 +11,15 @@ object JsonSource: // ZIO-Json defines a series of different Readers. Not exactly sure why--maybe to support different // modes (streaming, ...)? At least for now we only need one, so merged key bits of Readers into one. -case class JsonSource(js: CharSequence): - private var i = 0 +case class JsonSource(js: CharSequence, jsBytes: Array[Byte]): + var i = 0 private var expectFieldValue = false private[json] val max = js.length + // Jsoniter ParseString machinery + val ps = ParseString(jsBytes) + val cbuf = new Array[Char](4048) + def pos = i inline def here = js.charAt(i) @@ -203,23 +207,26 @@ case class JsonSource(js: CharSequence): def expectString(): CharSequence = readCharWS() match { case '"' => - retract() - parseString() + val parsedCount = ps.parseString(0, max - i, cbuf, i) + i += parsedCount + 1 + new String(cbuf, 0, parsedCount) + // retract() + // parseString() case 'n' => readChars(JsonSource.ull, "null") null case c => throw new JsonParseError(s"Expected a String value but got '$c'", this) } - private def parseString(): CharSequence = - charWithWS('"') - val sb = new FastStringBuilder(64) - var c: Char = END_OF_STRING - while - c = readEscapedChar() - c != END_OF_STRING - do sb.append(c.toChar) - sb.buffer + // private def parseString(): CharSequence = + // charWithWS('"') + // val sb = new FastStringBuilder(64) + // var c: Char = END_OF_STRING + // while + // c = readEscapedChar() + // c != END_OF_STRING + // do sb.append(c.toChar) + // sb.buffer inline def expectChar(): Char = expectString() match { diff --git a/src/main/scala/co.blocke.scalajack/json/reading/JsonSource2.scala b/src/main/scala/co.blocke.scalajack/json/reading/JsonSource2.scala index 7e1c6c31..7d05ca66 100644 --- a/src/main/scala/co.blocke.scalajack/json/reading/JsonSource2.scala +++ b/src/main/scala/co.blocke.scalajack/json/reading/JsonSource2.scala @@ -11,8 +11,8 @@ object JsonSource2: protected val rue: Array[Char] = "rue".toCharArray case class JsonSource2(js: CharSequence): - private var i = 0 // The great, omnipresent index - private[json] val max = js.length // don't overrun this, or else... + private var i = 0 // The great, omnipresent index + private[json] val max = js.length // don't overrun this, or else... inline def here: Char = js.charAt(i) inline def pos = i @@ -37,49 +37,49 @@ case class JsonSource2(js: CharSequence): // -- Parse Objects - inline def expectObject: Boolean = // returns false if null + inline def expectObject: Boolean = // returns false if null getCharWS match { - case '{' => - i += 1 - true // true -> start of object found - case 'n' => - i += 1 - expectChars(JsonSource2.ull, "'{' or null") - false // false -> null found - case BUFFER_EXCEEDED => throw JsonParseError2("(1) Tried to read past end of JSON buffer", this) - case _ => - throw JsonParseError2("(1) Expected '{' or null", this) + case '{' => + i += 1 + true // true -> start of object found + case 'n' => + i += 1 + expectChars(JsonSource2.ull, "'{' or null") + false // false -> null found + case BUFFER_EXCEEDED => throw JsonParseError2("(1) Tried to read past end of JSON buffer", this) + case _ => + throw JsonParseError2("(1) Expected '{' or null", this) } def expectFieldIndex(fieldNameMatrix: StringMatrix): Int = (getCharWS: @switch) match { - case '"' => - var fi: Int = 0 - var bs: Long = fieldNameMatrix.initial - var c: Int = -1 + case '"' => + var fi: Int = 0 + var bs: Long = fieldNameMatrix.initial + var c: Int = -1 + i += 1 + while i < max && here != '"' do + bs = fieldNameMatrix.update(bs, fi, here) + i += 1 + fi += 1 + bs = fieldNameMatrix.exact(bs, fi) + if i == max then throw JsonParseError2("(2) Tried to read past end of JSON buffer", this) + else i += 1 - while i < max && here != '"' do - bs = fieldNameMatrix.update(bs, fi, here) - i += 1 - fi += 1 - bs = fieldNameMatrix.exact(bs, fi) - if i == max then throw JsonParseError2("(2) Tried to read past end of JSON buffer", this) - else - i += 1 - (getCharWS: @switch) match { - case ':' => - i += 1 - fieldNameMatrix.first(bs) - case BUFFER_EXCEEDED => throw JsonParseError2("(2) Tried to read past end of JSON buffer", this) - case _ => throw JsonParseError2(s"(2) Expected ':' but got '$here'", this) - } - case BUFFER_EXCEEDED => throw JsonParseError2("(2) Tried to read past end of JSON buffer", this) - case _ => throw JsonParseError2(s"(2) Expected start of field name (string) '\"' but got '$here'", this) + (getCharWS: @switch) match { + case ':' => + i += 1 + fieldNameMatrix.first(bs) + case BUFFER_EXCEEDED => throw JsonParseError2("(2) Tried to read past end of JSON buffer", this) + case _ => throw JsonParseError2(s"(2) Expected ':' but got '$here'", this) + } + case BUFFER_EXCEEDED => throw JsonParseError2("(2) Tried to read past end of JSON buffer", this) + case _ => throw JsonParseError2(s"(2) Expected start of field name (string) '\"' but got '$here'", this) } - inline def hasFields: Boolean = + inline def hasFields: Boolean = (getCharWS: @switch) match { - case '}' => + case '}' => i += 1 false case _ => true @@ -87,10 +87,10 @@ case class JsonSource2(js: CharSequence): def nextField: Boolean = // true means there is another field. false means '}' -- end of object (getCharWS: @switch) match { - case '}' => + case '}' => i += 1 false - case ',' => + case ',' => i += 1 true case _ => @@ -99,23 +99,23 @@ case class JsonSource2(js: CharSequence): // -- Parse Array - inline def expectArray: Boolean = // returns false if null + inline def expectArray: Boolean = // returns false if null getCharWS match { - case '[' => - i += 1 - true // true -> start of object found - case 'n' => - i += 1 - expectChars(JsonSource2.ull, "'[' or null") - false // false -> null found - case BUFFER_EXCEEDED => throw JsonParseError2("(4) Tried to read past end of JSON buffer", this) - case _ => - throw JsonParseError2("(4) Expected '[' or null", this) + case '[' => + i += 1 + true // true -> start of object found + case 'n' => + i += 1 + expectChars(JsonSource2.ull, "'[' or null") + false // false -> null found + case BUFFER_EXCEEDED => throw JsonParseError2("(4) Tried to read past end of JSON buffer", this) + case _ => + throw JsonParseError2("(4) Expected '[' or null", this) } - inline def hasElements: Boolean = + inline def hasElements: Boolean = (getCharWS: @switch) match { - case ']' => + case ']' => i += 1 false case _ => true @@ -123,10 +123,10 @@ case class JsonSource2(js: CharSequence): def nextElement: Boolean = // true means there is another field. false means '}' -- end of object (getCharWS: @switch) match { - case ']' => + case ']' => i += 1 false - case ',' => + case ',' => i += 1 true case _ => @@ -146,5 +146,5 @@ case class JsonSource2(js: CharSequence): expectChars(JsonSource2.alse, "false") false case BUFFER_EXCEEDED => throw JsonParseError2("(6) Tried to read past end of JSON buffer", this) - case _ => throw JsonParseError2(s"(6) Expected boolean value", this) - } \ No newline at end of file + case _ => throw JsonParseError2(s"(6) Expected boolean value", this) + } diff --git a/src/main/scala/co.blocke.scalajack/json/reading/Numbers.scala b/src/main/scala/co.blocke.scalajack/json/reading/Numbers.scala index 30d43c29..5bcf8b8c 100644 --- a/src/main/scala/co.blocke.scalajack/json/reading/Numbers.scala +++ b/src/main/scala/co.blocke.scalajack/json/reading/Numbers.scala @@ -529,27 +529,27 @@ object UnsafeNumbers { with NoStackTrace def byte(num: String): Byte = - byte_(new JsonSource(num), true) + byte_(new JsonSource(num, num.getBytes), true) def byte_(in: JsonSource, consume: Boolean): Byte = long__(in, Byte.MinValue, Byte.MaxValue, consume).toByte def short(num: String): Short = - short_(new JsonSource(num), true) + short_(new JsonSource(num, num.getBytes), true) def short_(in: JsonSource, consume: Boolean): Short = long__(in, Short.MinValue, Short.MaxValue, consume).toShort def int(num: String): Int = - int_(new JsonSource(num), true) + int_(new JsonSource(num, num.getBytes), true) def int_(in: JsonSource, consume: Boolean): Int = long__(in, Int.MinValue, Int.MaxValue, consume).toInt def long(num: String): Long = - long_(new JsonSource(num), true) + long_(new JsonSource(num, num.getBytes), true) def long_(in: JsonSource, consume: Boolean): Long = long__(in, Long.MinValue, Long.MaxValue, consume) def bigInteger(num: String, max_bits: Int): java.math.BigInteger = - bigInteger_(new JsonSource(num), true, max_bits) + bigInteger_(new JsonSource(num, num.getBytes), true, max_bits) def bigInteger_( in: JsonSource, consume: Boolean, @@ -617,7 +617,7 @@ object UnsafeNumbers { } def float(num: String, max_bits: Int): Float = - float_(new JsonSource(num), true, max_bits) + float_(new JsonSource(num, num.getBytes), true, max_bits) def float_(in: JsonSource, consume: Boolean, max_bits: Int): Float = { var current: Int = in.readChar() @@ -666,7 +666,7 @@ object UnsafeNumbers { } def double(num: String, max_bits: Int): Double = - double_(new JsonSource(num), true, max_bits) + double_(new JsonSource(num, num.getBytes), true, max_bits) def double_(in: JsonSource, consume: Boolean, max_bits: Int): Double = { var current: Int = in.readChar() @@ -719,7 +719,7 @@ object UnsafeNumbers { } def bigDecimal(num: String, max_bits: Int): java.math.BigDecimal = - bigDecimal_(new JsonSource(num), true, max_bits) + bigDecimal_(new JsonSource(num, num.getBytes), true, max_bits) def bigDecimal_( in: JsonSource, consume: Boolean, diff --git a/src/main/scala/co.blocke.scalajack/json/reading/ParseString.scala b/src/main/scala/co.blocke.scalajack/json/reading/ParseString.scala new file mode 100644 index 00000000..f0b07b99 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/reading/ParseString.scala @@ -0,0 +1,226 @@ +package co.blocke.scalajack +package json +package reading + +import scala.annotation.{switch, tailrec} +import java.nio.ByteBuffer +import java.io.InputStream +import co.blocke.scalajack.util.ByteArrayAccess + +case class ParseString(var buf: Array[Byte], var charBuf: Array[Char] = new Array[Char](4096)): + + // var buf: Array[Byte] = new Array[Byte](32768) + var head: Int = 0 + var tail: Int = buf.length + // var mark: Int = -1 + // var bbuf: ByteBuffer = null + // var in: InputStream = null + val maxCharBufSize = 4194304 + + /** + * + * + * @param i - position in charBuf to start populating + * @param minLim -- ??? + * @param charBuf -- Array containing characters of the resultant parsed string + * @param pos -- position in the input buffer to start reading from (ie current parse position) + * @return number of characters read + */ + @tailrec + final def parseString(i: Int, minLim: Int, charBuf: Array[Char], pos: Int): Int = + if (i + 3 < minLim) { // Based on SWAR routine of JSON string parsing: https://github.com/sirthias/borer/blob/fde9d1ce674d151b0fee1dd0c2565020c3f6633a/core/src/main/scala/io/bullet/borer/json/JsonParser.scala#L456 + val bs = ByteArrayAccess.getInt(buf, pos) + val m = ((bs - 0x20202020 ^ 0x3C3C3C3C) - 0x1010101 | (bs ^ 0x5D5D5D5D) + 0x1010101) & 0x80808080 + charBuf(i) = (bs & 0xFF).toChar + charBuf(i + 1) = (bs >> 8 & 0xFF).toChar + charBuf(i + 2) = (bs >> 16 & 0xFF).toChar + charBuf(i + 3) = (bs >> 24).toChar + if (m != 0) { + val offset = java.lang.Integer.numberOfTrailingZeros(m) >> 3 + if ((bs >> (offset << 3)).toByte == '"') { + head = pos + offset + 1 + i + offset + } else throw new Exception("special chars found 1") //else parseEncodedString(i + offset, charBuf.length - 1, charBuf, pos + offset) + } else parseString(i + 4, minLim, charBuf, pos + 4) + } else if (i < minLim) { + val b = buf(pos) + charBuf(i) = b.toChar + if (b == '"') { + head = pos + 1 + i + } else if ((b - 0x20 ^ 0x3C) <= 0) throw new Exception("special chars found 2") //parseEncodedString(i, charBuf.length - 1, charBuf, pos) + else parseString(i + 1, minLim, charBuf, pos + 1) + } else if (pos >= tail) { + throw new Exception("Buffer overlow while parsing string") + } else + parseString(i, Math.min(growCharBuf(i + 1), i + tail - pos), this.charBuf, pos) + + /* + @tailrec + private[this] def parseEncodedString(i: Int, lim: Int, charBuf: Array[Char], pos: Int): Int = { + val remaining = tail - pos + if (i < lim) { + if (remaining > 0) { + val b1 = buf(pos) + if (b1 >= 0) { + if (b1 == '"') { + head = pos + 1 + i + } else if (b1 != '\\') { // 0aaaaaaa (UTF-8 byte) -> 000000000aaaaaaa (UTF-16 char) + if (b1 < ' ') throw new Exception(s"unescapedControlCharacterError($pos)") + charBuf(i) = b1.toChar + parseEncodedString(i + 1, lim, charBuf, pos + 1) + } else if (remaining > 1) { + val b2 = buf(pos + 1) + if (b2 != 'u') { + charBuf(i) = (b2: @switch) match { + case '"' => '"' + case 'n' => '\n' + case 'r' => '\r' + case 't' => '\t' + case 'b' => '\b' + case 'f' => '\f' + case '\\' => '\\' + case '/' => '/' + case _ => throw new Exception(s"escapeSequenceError(${pos + 1})") + } + parseEncodedString(i + 1, lim, charBuf, pos + 2) + } else if (remaining > 5) { + val ch1 = readEscapedUnicode(pos + 2, buf) + charBuf(i) = ch1 + if ((ch1 & 0xF800) != 0xD800) parseEncodedString(i + 1, lim, charBuf, pos + 6) + else if (remaining > 11) { + if (buf(pos + 6) != '\\') throw new Exception(s"escapeSequenceError(${pos + 6})") + if (buf(pos + 7) != 'u') throw new Exception(s"escapeSequenceError(${pos + 7})") + val ch2 = readEscapedUnicode(pos + 8, buf) + charBuf(i + 1) = ch2 + if (ch1 >= 0xDC00 || (ch2 & 0xFC00) != 0xDC00) throw new Exception(s"decodeError(\"illegal surrogate character pair\", ${pos + 11})") + parseEncodedString(i + 2, lim, charBuf, pos + 12) + } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos)) + } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos)) + } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos)) + } else if ((b1 & 0xE0) == 0xC0) { // 110bbbbb 10aaaaaa (UTF-8 bytes) -> 00000bbbbbaaaaaa (UTF-16 char) + if (remaining > 1) { + val b2 = buf(pos + 1) + val ch = (b1 << 6 ^ b2 ^ 0xF80).toChar // 0xF80 == 0xC0.toByte << 6 ^ 0x80.toByte + charBuf(i) = ch + if ((b2 & 0xC0) != 0x80 || ch < 0x80) throw new Exception(s"malformedBytesError($b1, $b2, $pos)") + parseEncodedString(i + 1, lim, charBuf, pos + 2) + } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos)) + } else if ((b1 & 0xF0) == 0xE0) { // 1110cccc 10bbbbbb 10aaaaaa (UTF-8 bytes) -> ccccbbbbbbaaaaaa (UTF-16 char) + if (remaining > 2) { + val b2 = buf(pos + 1) + val b3 = buf(pos + 2) + val ch = (b1 << 12 ^ b2 << 6 ^ b3 ^ 0x1F80).toChar // 0x1F80 == (0x80.toByte << 6 ^ 0x80.toByte).toChar + charBuf(i) = ch + if ((b2 & 0xC0) != 0x80 || (b3 & 0xC0) != 0x80 || ch < 0x800 || + (ch & 0xF800) == 0xD800) throw new Exception(s"malformedBytesError($b1, $b2, $b3, $pos)") + parseEncodedString(i + 1, lim, charBuf, pos + 3) + } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos)) + } else if ((b1 & 0xF8) == 0xF0) { // 11110ddd 10ddcccc 10bbbbbb 10aaaaaa (UTF-8 bytes) -> 110110uuuuccccbb 110111bbbbaaaaaa (UTF-16 chars), where uuuu = ddddd - 1 + if (remaining > 3) { + val b2 = buf(pos + 1) + val b3 = buf(pos + 2) + val b4 = buf(pos + 3) + val cp = b1 << 18 ^ b2 << 12 ^ b3 << 6 ^ b4 ^ 0x381F80 // 0x381F80 == 0xF0.toByte << 18 ^ 0x80.toByte << 12 ^ 0x80.toByte << 6 ^ 0x80.toByte + val ch1 = ((cp >>> 10) + 0xD7C0).toChar // 0xD7C0 == 0xD800 - (0x10000 >>> 10) + charBuf(i) = ch1 + charBuf(i + 1) = ((cp & 0x3FF) + 0xDC00).toChar + if ((b2 & 0xC0) != 0x80 || (b3 & 0xC0) != 0x80 || (b4 & 0xC0) != 0x80 || + (ch1 & 0xF800) != 0xD800) throw new Exception(s"malformedBytesError($b1, $b2, $b3, $b4, $pos)") + parseEncodedString(i + 2, lim, charBuf, pos + 4) + } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos)) + } else throw new Exception(s"malformedBytesError($b1, $pos)") + } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos)) + } else parseEncodedString(i, growCharBuf(i + 2) - 1, this.charBuf, pos) // 2 is length of surrogate pair + } + + private[this] def readEscapedUnicode(pos: Int, buf: Array[Byte]): Char = { + val ns = nibbles + val x = + ns(buf(pos) & 0xFF) << 12 | + ns(buf(pos + 1) & 0xFF) << 8 | + ns(buf(pos + 2) & 0xFF) << 4 | + ns(buf(pos + 3) & 0xFF) + if (x < 0) throw new Exception(s"hexDigitError($pos)") + x.toChar + } + */ + + private[this] def growCharBuf(required: Int): Int = { + var charBufLen = charBuf.length + if (charBufLen == maxCharBufSize) throw new Exception("tooLongStringError") + charBufLen = (-1 >>> Integer.numberOfLeadingZeros(charBufLen | required)) + 1 + if (charBufLen > maxCharBufSize || charBufLen < 0) charBufLen = maxCharBufSize + charBuf = java.util.Arrays.copyOf(charBuf, charBufLen) + charBufLen + } + + /* + private final val nibbles: Array[Byte] = Array( + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 + ) + */ + + /* + private[this] def loadMoreOrError(pos: Int): Int = { + if ((bbuf eq null) && (in eq null)) throw new Exception("endOfInputError") + loadMore(pos, throwOnEndOfInput = true) + } + + private[this] def loadMore(pos: Int): Int = + if ((bbuf eq null) && (in eq null)) pos + else loadMore(pos, throwOnEndOfInput = false) + + private[this] def loadMore(pos: Int, throwOnEndOfInput: Boolean): Int = { + var newPos = pos + val offset = + if (mark < 0) pos + else mark + if (offset > 0) { + newPos -= offset + val buf = this.buf + val remaining = tail - offset + var i = 0 + while (i < remaining) { + buf(i) = buf(i + offset) + i += 1 + } + if (mark > 0) mark = 0 + tail = remaining + head = newPos + } else growBuf() + var len = buf.length - tail + if (bbuf ne null) { + len = Math.min(bbuf.remaining, len) + bbuf.get(buf, tail, len) + } else len = Math.max(in.read(buf, tail, len), 0) + if (throwOnEndOfInput && len == 0) throw new Exception("endOfInputError") + tail += len + totalRead += len + newPos + } + + private[this] def growBuf(): Unit = { + var bufLen = buf.length + if (bufLen == maxBufSize) throw new Exception("tooLongInputError") + bufLen <<= 1 + if (bufLen > maxBufSize || bufLen < 0) bufLen = maxBufSize + buf = java.util.Arrays.copyOf(buf, bufLen) + } + */ diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index 44281c25..8f61a1fc 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -7,6 +7,7 @@ import scala.jdk.CollectionConverters.* import scala.reflect.ClassTag import json.* import scala.collection.immutable.Queue +import co.blocke.scalajack.json.reading.SafeNumbers.double class Shape[T](polygon: T) class Parallelogram() @@ -78,3 +79,112 @@ object RunMe extends App: // ((fieldValues: Array[Any]) => new Foom(fieldValues(0).asInstanceOf[scala.Int], fieldValues(1).asInstanceOf[String])).apply(args) // } // } + + /* + val buf = Array.fill(1000)('Z') + val buf2 = new String(buf) + val buf3 = buf2.getBytes + + var i = 0 + val t0 = System.nanoTime() + while i < 1000 do + buf2.charAt(i) + i += 1 + val t1 = System.nanoTime() + println("Time: " + (t1 - t0)) // 1,802,166 + + println("------") + + // Wow... so ByteArrayAccess is about 1/2 as fast as not using it. :-O + i = 0 + val max = 997 // buf2.length + println("MAX: " + max) + val t2 = System.nanoTime() + while i < max do + co.blocke.scalajack.util.ByteArrayAccess.getInt(buf3, i) + i += 1 + val t3 = System.nanoTime() + println("Time: " + (t3 - t2)) // 2,267,500 + + def parseInt(s: String): Int = { + var result = 0 + var sign = 1 + var i = 0 + + // Handle optional sign + if s.charAt(0) == '-' then { + sign = -1 + i += 1 + } else if s.charAt(0) == '+' then { + i += 1 + } + + // Parse digits + while i < s.length do { + val digit = s.charAt(i) - '0' + + if digit < 0 || digit > 9 then { + throw new NumberFormatException(s"Invalid character in integer: ${s.charAt(i)}") + } + + result = (result << 3) + (result << 1) + digit // equivalent to result * 10 + digit + + i += 1 + } + + sign * result + } + + println("------") + */ + + /* + var i = 0 + val msg = """This is a test"""" + val cbuf = new Array[Char](4048) + val ps = reading.ParseString(msg.getBytes) + val ta = System.nanoTime() + while i < 1000 do + val z = ps.parseString(0, msg.length(), cbuf, 0) + String(cbuf.take(z)) + i += 1 + val tb = System.nanoTime() + println("Time: " + (tb - ta)) // 3235208 + + println("------") + + i = 0 + val src = reading.JsonSource(""""This is a test"""") + val tc = System.nanoTime() + while i < 1000 do + src.expectString() + src.i = 0 + i += 1 + val td = System.nanoTime() + println("Time: " + (td - tc)) // 4927792 --> about a 52% improvement!!! + */ + + val msg = """This is a test"Another test"""" + val max = msg.length() + println("max: " + max) + val cbufLen = 4048 + val cbuf = new Array[Char](cbufLen) + val ps = reading.ParseString(msg.getBytes) + var pos = 0 + val parsedCount = ps.parseString(0, max - pos, cbuf, pos) + println("Parsed: " + parsedCount) + println("cbuf: " + new String(cbuf, 0, parsedCount)) + pos = parsedCount + 1 + + println("HERE: " + msg.charAt(parsedCount)) + val pc2 = ps.parseString(0, max - pos, cbuf, 15) + println("2: " + pc2 + " : " + new String(cbuf, 0, pc2)) + + // import com.github.plokhotnyuk.jsoniter_scala.core.JsonReader as Wow + + // val jsr = new Wow(msg.getBytes) + // val a = jsr.parseString(0, cbufLen, cbuf, 0) + // println("A: " + new String(cbuf, 0, a)) + + // val b = jsr.parseString(0, cbufLen, cbuf, a + 1) + // println("B: " + new String(cbuf, 0, b)) From f0da3f9d3f273bbec31d806331ba56f5e4357aed Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Fri, 1 Mar 2024 19:30:57 -0600 Subject: [PATCH 43/65] Massive performance boost --- benchmark/build.sbt | 11 +- .../src/main/scala/co.blocke/ScalaJack.scala | 14 +- build.sbt | 2 +- project/plugins.sbt | 1 - .../scalajack/util/ByteArrayAccess.java | 4 + .../scala/co.blocke.scalajack/ScalaJack.scala | 2 +- .../json/JsonCodecMaker.scala | 41 +- .../co.blocke.scalajack/json/JsonError.scala | 13 - .../json/exp/JsonReader.scalax | 432 ------------------ .../json/exp/JsonReaderException.scalax | 5 - .../json/exp/package.scalax | 15 - .../json/reading/ByteArrayAccess.scalax | 48 -- .../json/reading/ClassDecoder.scalax | 48 -- .../json/reading/FastStringBuilder.scala | 21 - .../json/reading/JsonDecoder.scalax | 88 ---- .../json/reading/JsonReader.scalax | 94 ---- .../json/reading/JsonReader.scalax2 | 114 ----- .../json/reading/JsonReaderUtil.scala | 96 ---- .../json/reading/JsonSource.scala | 182 ++++---- .../json/reading/JsonSource2.scala | 150 ------ .../json/reading/Numbers.scala | 16 +- .../json/reading/ParseString.scala | 226 --------- .../json/reading/StringMatrix.scala | 81 ---- .../scala/co.blocke.scalajack/run/Play.scala | 163 +++---- 24 files changed, 195 insertions(+), 1672 deletions(-) delete mode 100644 src/main/scala/co.blocke.scalajack/json/exp/JsonReader.scalax delete mode 100644 src/main/scala/co.blocke.scalajack/json/exp/JsonReaderException.scalax delete mode 100644 src/main/scala/co.blocke.scalajack/json/exp/package.scalax delete mode 100644 src/main/scala/co.blocke.scalajack/json/reading/ByteArrayAccess.scalax delete mode 100644 src/main/scala/co.blocke.scalajack/json/reading/ClassDecoder.scalax delete mode 100644 src/main/scala/co.blocke.scalajack/json/reading/FastStringBuilder.scala delete mode 100644 src/main/scala/co.blocke.scalajack/json/reading/JsonDecoder.scalax delete mode 100644 src/main/scala/co.blocke.scalajack/json/reading/JsonReader.scalax delete mode 100644 src/main/scala/co.blocke.scalajack/json/reading/JsonReader.scalax2 delete mode 100644 src/main/scala/co.blocke.scalajack/json/reading/JsonReaderUtil.scala delete mode 100644 src/main/scala/co.blocke.scalajack/json/reading/JsonSource2.scala delete mode 100644 src/main/scala/co.blocke.scalajack/json/reading/ParseString.scala delete mode 100644 src/main/scala/co.blocke.scalajack/json/reading/StringMatrix.scala diff --git a/benchmark/build.sbt b/benchmark/build.sbt index e493b031..4a0aac27 100644 --- a/benchmark/build.sbt +++ b/benchmark/build.sbt @@ -4,6 +4,7 @@ val compilerOptions = Seq( "-deprecation", "-encoding", "UTF-8", + "-explain", "-feature", "-language:existentials", "-language:higherKinds", @@ -36,17 +37,17 @@ lazy val benchmark = project libraryDependencies ++= Seq( "org.playframework" %% "play-json" % "3.0.1", "io.argonaut" %% "argonaut" % "6.3.9", - "co.blocke" %% "scalajack" % "241661_unknown", + "co.blocke" %% "scalajack" % "0ca9f0_unknown", "co.blocke" %% "scala-reflection" % "sj_fixes_f43af7", "dev.zio" %% "zio-json" % "0.6.1", "org.typelevel" %% "fabric-core" % "1.12.6", "org.typelevel" %% "fabric-io" % "1.12.6", "org.typelevel" %% "jawn-parser" % "1.3.2", "org.typelevel" %% "jawn-ast" % "1.3.2", - "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core" % "2.24.5-SNAPSHOT", - "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-macros" % "2.24.5-SNAPSHOT" % "compile-internal", - // "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core" % "2.24.4", - // "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-macros" % "2.24.4" % "compile-internal", + // "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core" % "2.24.5-SNAPSHOT", + // "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-macros" % "2.24.5-SNAPSHOT" % "compile-internal", + "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core" % "2.24.4", + "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-macros" % "2.24.4" % "compile-internal", // "io.circe" %% "circe-derivation" % "0.15.0-M1", // "io.circe" %% "circe-jackson29" % "0.14.0", // "org.json4s" %% "json4s-jackson" % "4.0.4", diff --git a/benchmark/src/main/scala/co.blocke/ScalaJack.scala b/benchmark/src/main/scala/co.blocke/ScalaJack.scala index 63cb96c1..132466d5 100644 --- a/benchmark/src/main/scala/co.blocke/ScalaJack.scala +++ b/benchmark/src/main/scala/co.blocke/ScalaJack.scala @@ -9,13 +9,13 @@ object ScalaJackZ: implicit val blah: ScalaJack[Record] = sj[Record] trait ScalaJackReadingBenchmark{ - @Benchmark - // def readRecordScalaJack = sj[Record].fromJson(jsData) // 500K - def readRecordScalaJack = ScalaJack[Record].fromJson(jsData) // 515K :-( - } + @Benchmark + // def readRecordScalaJack = sj[Record].fromJson(jsData) // 500K + def readRecordScalaJack = ScalaJack[Record].fromJson(jsData) // 515K :-( + } trait ScalaJackWritingBenchmark { - @Benchmark - // def writeRecordScalaJack = sj[Record].toJson(record) // 677K score - def writeRecordScalaJack = ScalaJack[Record].toJson(record) // 1.7M score <- faster + @Benchmark + // def writeRecordScalaJack = sj[Record].toJson(record) // 677K score + def writeRecordScalaJack = ScalaJack[Record].toJson(record) // 1.7M score <- faster } diff --git a/build.sbt b/build.sbt index e89ec5f7..491e5751 100644 --- a/build.sbt +++ b/build.sbt @@ -19,7 +19,7 @@ inThisBuild(List( name := "scalajack" ThisBuild / organization := "co.blocke" -ThisBuild / scalaVersion := "3.3.2" +ThisBuild / scalaVersion := "3.3.1" lazy val root = project .in(file(".")) diff --git a/project/plugins.sbt b/project/plugins.sbt index 4034b53b..2424290e 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -3,4 +3,3 @@ addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.5.11") addSbtPlugin("org.typelevel" % "sbt-typelevel-sonatype-ci-release" % "0.5.0-M6") addSbtPlugin("org.typelevel" % "sbt-typelevel-github-actions" % "0.4.16") addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.2") -// addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.17") diff --git a/src/main/java/co/blocke/scalajack/util/ByteArrayAccess.java b/src/main/java/co/blocke/scalajack/util/ByteArrayAccess.java index b06af59c..e02d2d2e 100644 --- a/src/main/java/co/blocke/scalajack/util/ByteArrayAccess.java +++ b/src/main/java/co/blocke/scalajack/util/ByteArrayAccess.java @@ -32,6 +32,10 @@ static public int getInt(byte[] buf, int pos) { return (int) VH_INT.get(buf, pos); } + static public short getShort(byte[] buf, int pos) { + return (short) VH_SHORT.get(buf, pos); + } + static public void setShort(byte[] buf, int pos, short value) { VH_SHORT.set(buf, pos, value); } diff --git a/src/main/scala/co.blocke.scalajack/ScalaJack.scala b/src/main/scala/co.blocke.scalajack/ScalaJack.scala index d6c50c8d..cc7d4a2a 100644 --- a/src/main/scala/co.blocke.scalajack/ScalaJack.scala +++ b/src/main/scala/co.blocke.scalajack/ScalaJack.scala @@ -9,7 +9,7 @@ import json.* case class ScalaJack[T](jsonCodec: JsonCodec[T]): // extends JsonCodec[T] //with YamlCodec with MsgPackCodec def fromJson(js: String): T = // Either[JsonParseError, T] = - jsonCodec.decodeValue(reading.JsonSource(js, js.getBytes)) + jsonCodec.decodeValue(reading.JsonSource(js)) val out = writing.JsonOutput() // let's clear & re-use JsonOutput--avoid re-allocating all the internal buffer space def toJson(a: T): String = diff --git a/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala b/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala index 078ae3c7..739d420e 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala @@ -6,7 +6,7 @@ import co.blocke.scala_reflection.{RTypeRef, TypedName} import co.blocke.scala_reflection.reflect.ReflectOnType import co.blocke.scala_reflection.reflect.rtypeRefs.* import co.blocke.scala_reflection.rtypes.{EnumRType, JavaClassRType, NonConstructorFieldInfo} -import reading.{JsonReaderUtil, JsonSource, StringMatrix} +import reading.JsonSource import scala.jdk.CollectionConverters.* import scala.quoted.* import scala.collection.Factory @@ -839,7 +839,7 @@ object JsonCodecMaker: val sym = Symbol.newVal(Symbol.spliceOwner, "_" + oneField.name, TypeRepr.of[f], Flags.Mutable, Symbol.noSymbol) val fieldSymRef = Ident(sym.termRef) val caseDef = CaseDef( - Literal(IntConstant(oneField.index)), + Literal(StringConstant(oneField.name)), None, Assign(fieldSymRef, genReadVal[f](oneField.fieldRef.asInstanceOf[RTypeRef[f]], in).asTerm) ) @@ -859,6 +859,7 @@ object JsonCodecMaker: } } val (varDefs, caseDefs, idents) = together.unzip3 + val caseDefsWithFinal = caseDefs :+ CaseDef(Wildcard(), None, '{ $in.skipValue() }.asTerm) val argss = List(idents) val primaryConstructor = tpe.classSymbol.get.primaryConstructor @@ -869,13 +870,13 @@ object JsonCodecMaker: val instantiateClass = argss.tail.foldLeft(Apply(constructor, argss.head))((acc, args) => Apply(acc, args)) val parseLoop = '{ - val fieldMatrix = new StringMatrix($fieldNames) - var fldIdx = $in.expectFirstObjectField(fieldMatrix) - while fldIdx >= -1 do // -2: end-of-object, -3: null (-1: unknown field -> skip) - ${ Match('{ fldIdx }.asTerm, caseDefs :+ CaseDef(Literal(IntConstant(-1)), None, '{ $in.skipValue() }.asTerm)).asExprOf[Any] } - fldIdx = $in.expectObjectField(fieldMatrix) - if fldIdx == -3 then null.asInstanceOf[T] - else ${ instantiateClass.asExprOf[T] } + var maybeFname = $in.expectFirstObjectField() + if maybeFname == null then null.asInstanceOf[T] + else + while maybeFname.isDefined do + ${ Match('{ maybeFname.get }.asTerm, caseDefsWithFinal).asExprOf[Any] } + maybeFname = $in.expectObjectField() + ${ instantiateClass.asExprOf[T] } }.asTerm Block(varDefs, parseLoop).asExprOf[T] @@ -943,16 +944,20 @@ object JsonCodecMaker: case t: SeqRef[?] => t.elementRef.refType match case '[e] => + val rtypeRef = t.elementRef.asInstanceOf[RTypeRef[e]] '{ - if ! $in.expectArrayStart() then null.asInstanceOf[T] - else if $in.here == ']' then // empty Seq - $in.readChar() // skip the ']' - List.empty[e].to(${ Expr.summon[Factory[e, T]].get }) // create appropriate Seq[T] here - else - val acc = scala.collection.mutable.ListBuffer.empty[e] - acc.addOne(${ genReadVal[e](t.elementRef.asInstanceOf[RTypeRef[e]], in) }) - while $in.nextArrayElement() do acc.addOne(${ genReadVal[e](t.elementRef.asInstanceOf[RTypeRef[e]], in) }) - acc.to(${ Expr.summon[Factory[e, T]].get }) // create appropriate Seq[T] here + val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in) }) + if parsedArray == null then null.asInstanceOf[T] + else parsedArray.to(${ Expr.summon[Factory[e, T]].get }) // create appropriate flavor of Seq[T] here + // if ! $in.expectArrayStart() then null.asInstanceOf[T] + // else if $in.here == ']' then // empty Seq + // $in.readChar() // skip the ']' + // List.empty[e].to(${ Expr.summon[Factory[e, T]].get }) // create appropriate Seq[T] here + // else + // val acc = scala.collection.mutable.ListBuffer.empty[e] + // acc.addOne(${ genReadVal[e](t.elementRef.asInstanceOf[RTypeRef[e]], in) }) + // while $in.nextArrayElement() do acc.addOne(${ genReadVal[e](t.elementRef.asInstanceOf[RTypeRef[e]], in) }) + // acc.to(${ Expr.summon[Factory[e, T]].get }) // create appropriate Seq[T] here } case _ => diff --git a/src/main/scala/co.blocke.scalajack/json/JsonError.scala b/src/main/scala/co.blocke.scalajack/json/JsonError.scala index 6103ed01..f790f1d0 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonError.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonError.scala @@ -28,16 +28,3 @@ case class JsonParseError(override val msg: String, context: reading.JsonSource) case ep => ("..." + js.substring(ep - 49, ep + 27) + "...", 52) } msg + s" at positio [${context.pos}]" + "\n" + clip.replaceAll("[\n\t]", "~") + "\n" + ("-" * dashes) + "^" - - // Temporary -case class JsonParseError2(override val msg: String, context: reading.JsonSource2) extends ParseError(msg + " at position " + context.pos) with NoStackTrace: - override val show: String = - val js = context.js.toString - val (clip, dashes) = context.pos match { - case ep if ep <= 50 && context.max < 80 => (js, ep) - case ep if ep <= 50 => (js.substring(0, 77) + "...", ep) - case ep if ep > 50 && ep + 30 >= context.max => - ("..." + js.substring(context.pos - 49), 52) - case ep => ("..." + js.substring(ep - 49, ep + 27) + "...", 52) - } - msg + s" at positio [${context.pos}]" + "\n" + clip.replaceAll("[\n\t]", "~") + "\n" + ("-" * dashes) + "^" diff --git a/src/main/scala/co.blocke.scalajack/json/exp/JsonReader.scalax b/src/main/scala/co.blocke.scalajack/json/exp/JsonReader.scalax deleted file mode 100644 index e7fb3f6f..00000000 --- a/src/main/scala/co.blocke.scalajack/json/exp/JsonReader.scalax +++ /dev/null @@ -1,432 +0,0 @@ -package co.blocke.scalajack -package json -package exp - -import scala.annotation.{switch, tailrec} -import java.nio.ByteBuffer -import java.io.InputStream -import java.nio.charset.StandardCharsets.UTF_8 -import scala.specialized - -/* -Bottom Line: - Fancy string reading, ByteBuffer handling, etc. did NOT have a material effect on speed. - The SJ way was either equal to, or faster than, the JsonReader approach for reading strings! - */ - -class JsonReader private[json] ( - private[this] var buf: Array[Byte] = new Array[Byte](32768), - private[this] var head: Int = 0, - private[this] var tail: Int = 0, - private[this] var mark: Int = -1, - private[this] var charBuf: Array[Char] = new Array[Char](4096), - private[this] var bbuf: ByteBuffer = null, - private[this] var in: InputStream = null, - private[this] var totalRead: Long = 0 -): - - private[json] def read(s: String): String = { - val currBuf = this.buf - try { - this.buf = s.getBytes(UTF_8) - head = 0 - val to = buf.length - tail = to - totalRead = 0 - mark = -1 - readString("") - } finally this.buf = currBuf - } - - def readString(default: String): String = - if isNextToken('"', head) then { - val pos = head - val len = parseString(0, Math.min(tail - pos, charBuf.length), charBuf, pos) - new String(charBuf, 0, len) - } else readNullOrTokenError(default, '"') - - @tailrec - private[this] def isNextToken(t: Byte, pos: Int): Boolean = - if pos < tail then { - val b = buf(pos) - head = pos + 1 - b == t || ((b == ' ' || b == '\n' || (b | 0x4) == '\r') && nextToken(pos + 1) == t) - } else isNextToken(t, loadMoreOrError(pos)) - - @tailrec - private[this] def nextToken(pos: Int): Byte = - if pos < tail then { - val b = buf(pos) - if b == ' ' || b == '\n' || (b | 0x4) == '\r' then nextToken(pos + 1) - else { - head = pos + 1 - b - } - } else nextToken(loadMoreOrError(pos)) - - @tailrec - private[this] def readNullOrTokenError[@specialized A](default: A, t: Byte): A = - if default != null then { - val pos = head - if pos != 0 then { - if pos + 2 < tail then { - val bs = ByteArrayAccess.getInt(buf, pos - 1) - if bs == 0x6c6c756e then { - head = pos + 3 - default - } else tokenOrNullError(t, bs, pos) - } else if buf(pos - 1) == 'n' then { - head = loadMoreOrError(pos - 1) + 1 - readNullOrTokenError(default, t) - } else tokenOrNullError(t) - } else illegalTokenOperation() - } else tokenError(t) - - @tailrec - private[this] def parseString(i: Int, minLim: Int, charBuf: Array[Char], pos: Int): Int = - if i + 3 < minLim then { // Based on SWAR routine of JSON string parsing: https://github.com/sirthias/borer/blob/fde9d1ce674d151b0fee1dd0c2565020c3f6633a/core/src/main/scala/io/bullet/borer/json/JsonParser.scala#L456 - val bs = ByteArrayAccess.getInt(buf, pos) - val m = ((bs - 0x20202020 ^ 0x3c3c3c3c) - 0x1010101 | (bs ^ 0x5d5d5d5d) + 0x1010101) & 0x80808080 - charBuf(i) = (bs & 0xff).toChar - charBuf(i + 1) = (bs >> 8 & 0xff).toChar - charBuf(i + 2) = (bs >> 16 & 0xff).toChar - charBuf(i + 3) = (bs >> 24).toChar - if m != 0 then { - val offset = java.lang.Integer.numberOfTrailingZeros(m) >> 3 - if (bs >> (offset << 3)).toByte == '"' then { - head = pos + offset + 1 - i + offset - } else parseEncodedString(i + offset, charBuf.length - 1, charBuf, pos + offset) - } else parseString(i + 4, minLim, charBuf, pos + 4) - } else if i < minLim then { - val b = buf(pos) - charBuf(i) = b.toChar - if b == '"' then { - head = pos + 1 - i - } else if (b - 0x20 ^ 0x3c) <= 0 then parseEncodedString(i, charBuf.length - 1, charBuf, pos) - else parseString(i + 1, minLim, charBuf, pos + 1) - } else if pos >= tail then { - val newPos = loadMoreOrError(pos) - parseString(i, Math.min(charBuf.length, i + tail - newPos), charBuf, newPos) - } else parseString(i, Math.min(growCharBuf(i + 1), i + tail - pos), this.charBuf, pos) - - @tailrec - private[this] def parseEncodedString(i: Int, lim: Int, charBuf: Array[Char], pos: Int): Int = { - val remaining = tail - pos - if i < lim then { - if remaining > 0 then { - val b1 = buf(pos) - if b1 >= 0 then { - if b1 == '"' then { - head = pos + 1 - i - } else if b1 != '\\' then { // 0aaaaaaa (UTF-8 byte) -> 000000000aaaaaaa (UTF-16 char) - if b1 < ' ' then unescapedControlCharacterError(pos) - charBuf(i) = b1.toChar - parseEncodedString(i + 1, lim, charBuf, pos + 1) - } else if remaining > 1 then { - val b2 = buf(pos + 1) - if b2 != 'u' then { - charBuf(i) = (b2: @switch) match { - case '"' => '"' - case 'n' => '\n' - case 'r' => '\r' - case 't' => '\t' - case 'b' => '\b' - case 'f' => '\f' - case '\\' => '\\' - case '/' => '/' - case _ => illegalEscapeSequenceError(pos + 1) - } - parseEncodedString(i + 1, lim, charBuf, pos + 2) - } else if remaining > 5 then { - val ch1 = readEscapedUnicode(pos + 2, buf) - charBuf(i) = ch1 - if ch1 < 0xd800 || ch1 > 0xdfff then parseEncodedString(i + 1, lim, charBuf, pos + 6) - else if remaining > 11 then { - if buf(pos + 6) != '\\' then illegalEscapeSequenceError(pos + 6) - if buf(pos + 7) != 'u' then illegalEscapeSequenceError(pos + 7) - val ch2 = readEscapedUnicode(pos + 8, buf) - if ch1 >= 0xdc00 || ch2 < 0xdc00 || ch2 > 0xdfff then decodeError("illegal surrogate character pair", pos + 11) - charBuf(i + 1) = ch2 - parseEncodedString(i + 2, lim, charBuf, pos + 12) - } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos)) - } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos)) - } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos)) - } else if (b1 >> 5) == -2 then { // 110bbbbb 10aaaaaa (UTF-8 bytes) -> 00000bbbbbaaaaaa (UTF-16 char) - if remaining > 1 then { - val b2 = buf(pos + 1) - if (b1 & 0x1e) == 0 || (b2 & 0xc0) != 0x80 then malformedBytesError(b1, b2, pos) - charBuf(i) = (b1 << 6 ^ b2 ^ 0xf80).toChar // 0xF80 == 0xC0.toByte << 6 ^ 0x80.toByte - parseEncodedString(i + 1, lim, charBuf, pos + 2) - } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos)) - } else if (b1 >> 4) == -2 then { // 1110cccc 10bbbbbb 10aaaaaa (UTF-8 bytes) -> ccccbbbbbbaaaaaa (UTF-16 char) - if remaining > 2 then { - val b2 = buf(pos + 1) - val b3 = buf(pos + 2) - val ch = (b1 << 12 ^ b2 << 6 ^ b3 ^ 0xfffe1f80).toChar // 0xFFFE1F80 == 0xE0.toByte << 12 ^ 0x80.toByte << 6 ^ 0x80.toByte - if (b1 == -32 && (b2 & 0xe0) == 0x80) || (b2 & 0xc0) != 0x80 || (b3 & 0xc0) != 0x80 || - (ch >= 0xd800 && ch <= 0xdfff) - then malformedBytesError(b1, b2, b3, pos) - charBuf(i) = ch - parseEncodedString(i + 1, lim, charBuf, pos + 3) - } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos)) - } else if (b1 >> 3) == -2 then { // 11110ddd 10ddcccc 10bbbbbb 10aaaaaa (UTF-8 bytes) -> 110110uuuuccccbb 110111bbbbaaaaaa (UTF-16 chars), where uuuu = ddddd - 1 - if remaining > 3 then { - val b2 = buf(pos + 1) - val b3 = buf(pos + 2) - val b4 = buf(pos + 3) - val cp = b1 << 18 ^ b2 << 12 ^ b3 << 6 ^ b4 ^ 0x381f80 // 0x381F80 == 0xF0.toByte << 18 ^ 0x80.toByte << 12 ^ 0x80.toByte << 6 ^ 0x80.toByte - if (b2 & 0xc0) != 0x80 || (b3 & 0xc0) != 0x80 || (b4 & 0xc0) != 0x80 || - cp < 0x10000 || cp > 0x10ffff - then malformedBytesError(b1, b2, b3, b4, pos) - charBuf(i) = ((cp >>> 10) + 0xd7c0).toChar // 0xD7C0 == 0xD800 - (0x10000 >>> 10) - charBuf(i + 1) = ((cp & 0x3ff) + 0xdc00).toChar - parseEncodedString(i + 2, lim, charBuf, pos + 4) - } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos)) - } else malformedBytesError(b1, pos) - } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos)) - } else parseEncodedString(i, growCharBuf(i + 2) - 1, this.charBuf, pos) // 2 is length of surrogate pair - } - - private[this] def malformedBytesError(b1: Byte, pos: Int): Nothing = { - var i = appendString("malformed byte(s): 0x", 0) - i = appendHexByte(b1, i, hexDigits) - decodeError(i, pos, null) - } - - private[this] def malformedBytesError(b1: Byte, b2: Byte, pos: Int): Nothing = { - val ds = hexDigits - var i = appendString("malformed byte(s): 0x", 0) - i = appendHexByte(b1, i, ds) - i = appendString(", 0x", i) - i = appendHexByte(b2, i, ds) - decodeError(i, pos + 1, null) - } - - private[this] def malformedBytesError(b1: Byte, b2: Byte, b3: Byte, pos: Int): Nothing = { - val ds = hexDigits - var i = appendString("malformed byte(s): 0x", 0) - i = appendHexByte(b1, i, ds) - i = appendString(", 0x", i) - i = appendHexByte(b2, i, ds) - i = appendString(", 0x", i) - i = appendHexByte(b3, i, ds) - decodeError(i, pos + 2, null) - } - - private[this] def malformedBytesError(b1: Byte, b2: Byte, b3: Byte, b4: Byte, pos: Int): Nothing = { - val ds = hexDigits - var i = appendString("malformed byte(s): 0x", 0) - i = appendHexByte(b1, i, ds) - i = appendString(", 0x", i) - i = appendHexByte(b2, i, ds) - i = appendString(", 0x", i) - i = appendHexByte(b3, i, ds) - i = appendString(", 0x", i) - i = appendHexByte(b4, i, ds) - decodeError(i, pos + 3, null) - } - - private[this] def appendHexByte(b: Byte, i: Int, ds: Array[Char]): Int = { - ensureCharBufCapacity(i + 2) - charBuf(i) = ds(b >> 4 & 0xf) - charBuf(i + 1) = ds(b & 0xf) - i + 2 - } - - private[this] def appendString(s: String, i: Int): Int = { - val len = s.length - val required = i + len - ensureCharBufCapacity(required) - s.getChars(0, len, charBuf, i) - required - } - - private[this] def ensureCharBufCapacity(required: Int): Unit = - if charBuf.length < required then growCharBuf(required): Unit - - final private val hexDigits: Array[Char] = - Array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f') - - private[this] def decodeError(msg: String, pos: Int, cause: Throwable = null): Nothing = - decodeError(appendString(msg, 0), pos, cause) - - private[this] def decodeError(from: Int, pos: Int, cause: Throwable): Nothing = { - var i = appendString(", offset: 0x", from) - val offset = - if (bbuf eq null) && (in eq null) then 0 - else totalRead - tail - i = appendHexOffset(offset + pos, i) - // if (config.appendHexDumpToParseException) { - // i = appendString(", buf:", i) - // i = appendHexDump(pos, offset.toInt, i) - // } - throw new JsonReaderException(new String(charBuf, 0, i), cause, true) // config.throwReaderExceptionWithStackTrace) - } - - private[this] def appendHexOffset(d: Long, i: Int): Int = { - ensureCharBufCapacity(i + 16) - val ds = hexDigits - var j = i - val dl = d.toInt - if dl != d then { - val dh = (d >> 32).toInt - var shift = 32 - java.lang.Integer.numberOfLeadingZeros(dh) & 0x1c - while shift >= 0 do { - charBuf(j) = ds(dh >> shift & 0xf) - shift -= 4 - j += 1 - } - } - putHexInt(dl, j, charBuf, ds) - j + 8 - } - - private[this] def putHexInt(d: Int, i: Int, charBuf: Array[Char], ds: Array[Char]): Unit = { - charBuf(i) = ds(d >>> 28) - charBuf(i + 1) = ds(d >> 24 & 0xf) - charBuf(i + 2) = ds(d >> 20 & 0xf) - charBuf(i + 3) = ds(d >> 16 & 0xf) - charBuf(i + 4) = ds(d >> 12 & 0xf) - charBuf(i + 5) = ds(d >> 8 & 0xf) - charBuf(i + 6) = ds(d >> 4 & 0xf) - charBuf(i + 7) = ds(d & 0xf) - } - - private[this] def loadMoreOrError(pos: Int): Int = { - if (bbuf eq null) && (in eq null) then endOfInputError() - loadMore(pos, throwOnEndOfInput = true) - } - - private[this] def loadMore(pos: Int): Int = - if (bbuf eq null) && (in eq null) then pos - else loadMore(pos, throwOnEndOfInput = false) - - private[this] def loadMore(pos: Int, throwOnEndOfInput: Boolean): Int = { - var newPos = pos - val offset = - if mark < 0 then pos - else mark - if offset > 0 then { - newPos -= offset - val buf = this.buf - val remaining = tail - offset - var i = 0 - while i < remaining do { - buf(i) = buf(i + offset) - i += 1 - } - if mark > 0 then mark = 0 - tail = remaining - head = newPos - } else growBuf() - var len = buf.length - tail - if bbuf ne null then { - len = Math.min(bbuf.remaining, len) - bbuf.get(buf, tail, len) - } else len = Math.max(in.read(buf, tail, len), 0) - if throwOnEndOfInput && len == 0 then endOfInputError() - tail += len - totalRead += len - newPos - } - - private[json] def endOfInputOrError(): Unit = - if skipWhitespaces() then decodeError("expected end of input", head) - - private[this] def endOfInputError(): Nothing = decodeError("unexpected end of input", tail) - private[this] def illegalEscapeSequenceError(pos: Int): Nothing = decodeError("illegal escape sequence", pos) - private[this] def unescapedControlCharacterError(pos: Int): Nothing = decodeError("unescaped control character", pos) - @tailrec - private[this] def hexDigitError(pos: Int): Nothing = { - if nibbles(buf(pos) & 0xff) < 0 then decodeError("expected hex digit", pos) - hexDigitError(pos + 1) - } - private[this] def tokenError(t: Byte, pos: Int = head - 1): Nothing = { - var i = appendString("expected '", 0) - i = appendChar(t.toChar, i) - i = appendChar('\'', i) - decodeError(i, pos, null) - } - private[this] def tokenOrNullError(t: Byte, bs: Int, pos: Int): Nothing = tokenOrNullError( - t, { - val b0 = bs.toByte - val b1 = (bs >> 8).toByte - val b2 = (bs >> 16).toByte - pos + - (if b0 != 'n' then -1 - else if b1 != 'u' then 0 - else if b2 != 'l' then 1 - else 2) - } - ) - private[this] def tokenOrNullError(t: Byte, pos: Int = head - 1): Nothing = { - var i = appendString("expected '", 0) - i = appendChar(t.toChar, i) - i = appendString("' or null", i) - decodeError(i, pos, null) - } - private[this] def illegalTokenOperation(): Nothing = - throw new IllegalStateException("expected preceding call of 'nextToken()' or 'isNextToken()'") - - private[this] def appendChar(ch: Char, i: Int): Int = { - ensureCharBufCapacity(i + 1) - charBuf(i) = ch - i + 1 - } - - private[json] def skipWhitespaces(): Boolean = { - var pos = head - var buf = this.buf - while (pos < tail || { - pos = loadMore(pos) - buf = this.buf - pos < tail - }) && { - val b = buf(pos) - b == ' ' || b == '\n' || (b | 0x4) == '\r' - } - do pos += 1 - head = pos - pos != tail - } - - private[this] def growBuf(): Unit = { - var bufLen = buf.length - // val maxBufSize = config.maxBufSize - // if (bufLen == maxBufSize) tooLongInputError() - bufLen <<= 1 - // if (bufLen > maxBufSize || bufLen < 0) bufLen = maxBufSize - buf = java.util.Arrays.copyOf(buf, bufLen) - } - - private[this] def growCharBuf(required: Int): Int = { - var charBufLen = charBuf.length - // val maxCharBufSize = config.maxCharBufSize - // if (charBufLen == maxCharBufSize) tooLongStringError() - charBufLen = (-1 >>> Integer.numberOfLeadingZeros(charBufLen | required)) + 1 - // if (charBufLen > maxCharBufSize || charBufLen < 0) charBufLen = maxCharBufSize - charBuf = java.util.Arrays.copyOf(charBuf, charBufLen) - charBufLen - } - - private[this] def readEscapedUnicode(pos: Int, buf: Array[Byte]): Char = { - val ns = nibbles - val x = - ns(buf(pos) & 0xff) << 12 | - ns(buf(pos + 1) & 0xff) << 8 | - ns(buf(pos + 2) & 0xff) << 4 | - ns(buf(pos + 3) & 0xff) - if x < 0 then hexDigitError(pos) - x.toChar - } - - final private val nibbles: Array[Byte] = Array( - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, - 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1 - ) diff --git a/src/main/scala/co.blocke.scalajack/json/exp/JsonReaderException.scalax b/src/main/scala/co.blocke.scalajack/json/exp/JsonReaderException.scalax deleted file mode 100644 index 1125402a..00000000 --- a/src/main/scala/co.blocke.scalajack/json/exp/JsonReaderException.scalax +++ /dev/null @@ -1,5 +0,0 @@ -package co.blocke.scalajack -package json -package exp - -class JsonReaderException private[json] (msg: String, cause: Throwable, withStackTrace: Boolean) extends RuntimeException(msg, cause, true, withStackTrace) diff --git a/src/main/scala/co.blocke.scalajack/json/exp/package.scalax b/src/main/scala/co.blocke.scalajack/json/exp/package.scalax deleted file mode 100644 index 51b2a08b..00000000 --- a/src/main/scala/co.blocke.scalajack/json/exp/package.scalax +++ /dev/null @@ -1,15 +0,0 @@ -package co.blocke.scalajack -package json - -import java.nio.ByteBuffer -import scala.specialized as sp - -package object exp { - - final private[this] val readerPool: ThreadLocal[JsonReader] = new ThreadLocal[JsonReader] { - override def initialValue(): JsonReader = new JsonReader - } - - def readFromString(s: String): String = - readerPool.get.read(s) -} diff --git a/src/main/scala/co.blocke.scalajack/json/reading/ByteArrayAccess.scalax b/src/main/scala/co.blocke.scalajack/json/reading/ByteArrayAccess.scalax deleted file mode 100644 index 63e99f66..00000000 --- a/src/main/scala/co.blocke.scalajack/json/reading/ByteArrayAccess.scalax +++ /dev/null @@ -1,48 +0,0 @@ -package co.blocke.scalajack -package json -package reading - -import java.lang.invoke.MethodHandles; -import java.lang.invoke.VarHandle; -import java.nio.ByteOrder; - -class ByteArrayAccess { // FIXME: Use Java wrapper as w/a for missing support of @PolymorphicSignature methods in Scala 3, see: https://github.com/lampepfl/dotty/issues/11332 - private static final VarHandle VH_LONG = - MethodHandles.byteArrayViewVarHandle(long[].class, ByteOrder.LITTLE_ENDIAN); - private static final VarHandle VH_INT = - MethodHandles.byteArrayViewVarHandle(int[].class, ByteOrder.LITTLE_ENDIAN); - private static final VarHandle VH_SHORT = - MethodHandles.byteArrayViewVarHandle(short[].class, ByteOrder.LITTLE_ENDIAN); - private static final VarHandle VH_LONG_REVERSED = - MethodHandles.byteArrayViewVarHandle(long[].class, ByteOrder.BIG_ENDIAN); - private static final VarHandle VH_INT_REVERSED = - MethodHandles.byteArrayViewVarHandle(int[].class, ByteOrder.BIG_ENDIAN); - - static void setLong(byte[] buf, int pos, long value) { - VH_LONG.set(buf, pos, value); - } - - static long getLong(byte[] buf, int pos) { - return (long) VH_LONG.get(buf, pos); - } - - static void setInt(byte[] buf, int pos, int value) { - VH_INT.set(buf, pos, value); - } - - static int getInt(byte[] buf, int pos) { - return (int) VH_INT.get(buf, pos); - } - - static void setShort(byte[] buf, int pos, short value) { - VH_SHORT.set(buf, pos, value); - } - - static void setLongReversed(byte[] buf, int pos, long value) { - VH_LONG_REVERSED.set(buf, pos, value); - } - - static int getIntReversed(byte[] buf, int pos) { - return (int) VH_INT_REVERSED.get(buf, pos); - } -} \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json/reading/ClassDecoder.scalax b/src/main/scala/co.blocke.scalajack/json/reading/ClassDecoder.scalax deleted file mode 100644 index 00f10666..00000000 --- a/src/main/scala/co.blocke.scalajack/json/reading/ClassDecoder.scalax +++ /dev/null @@ -1,48 +0,0 @@ -package co.blocke.scalajack -package json -package reading - -import scala.annotation.* - -object ClassDecoder: - // def apply[A: scala.reflect.ClassTag](fields: Array[String], fieldDecoders: Array[JsonDecoder[_]], instantiator: Array[?] => A) = new JsonDecoder[A] { - def apply[A]( - fields: Array[String], - fieldDecoders: Array[JsonDecoder[_]], - instantiator: Array[?] => A, - fieldValues: Array[Any] - ) = new JsonDecoder[A] { - - val fieldMatrix = new StringMatrix(fields) - - def unsafeDecode(in: JsonSource): A = - JsonParser.charWithWS(in, '{') - if JsonParser.firstField(in) then - var done = false - while !done do - val fieldIdx = JsonParser.parseField(in, fieldMatrix) - if fieldIdx < 0 then JsonParser.skipValue(in) - else - val dec = fieldDecoders(fieldIdx) - fieldValues(fieldIdx) = dec.unsafeDecode(in) - if !JsonParser.nextField(in) then done = true - else throw new JsonParseError("Expected fields!", in) - - // Construct the new object - instantiator(fieldValues) - } - - /* - -ClassDecoder.apply[Friend]( - Array[String]("name", "age", "email"), - List[JsonDecoder[_]]( - JsonDecoder.string, - JsonDecoder.int, - JsonDecoder.string - ).toArray, - ((fieldValues: scala.Array[_]) => new Friend(fieldValues.apply(0).asInstanceOf[java.lang.String], fieldValues.apply(1).asInstanceOf[scala.Int], fieldValues.apply(2).asInstanceOf[java.lang.String])), - scala.List.apply[scala.Any](0, 0, 0).toArray[scala.Any](scala.reflect.ClassTag.Any) - ) - - */ diff --git a/src/main/scala/co.blocke.scalajack/json/reading/FastStringBuilder.scala b/src/main/scala/co.blocke.scalajack/json/reading/FastStringBuilder.scala deleted file mode 100644 index b7c67f25..00000000 --- a/src/main/scala/co.blocke.scalajack/json/reading/FastStringBuilder.scala +++ /dev/null @@ -1,21 +0,0 @@ -package co.blocke.scalajack -package json -package reading - -import java.nio.CharBuffer -import java.util.Arrays - -// like StringBuilder but doesn't have any encoding or range checks -final class FastStringBuilder(initial: Int = 16) { - private[this] var chars: Array[Char] = new Array[Char](initial) - private[this] var i: Int = 0 - - def append(c: Char): FastStringBuilder = { - if i == chars.length then chars = Arrays.copyOf(chars, chars.length * 2) - chars(i) = c - i += 1 - this - } - - def buffer: CharSequence = CharBuffer.wrap(chars, 0, i) -} diff --git a/src/main/scala/co.blocke.scalajack/json/reading/JsonDecoder.scalax b/src/main/scala/co.blocke.scalajack/json/reading/JsonDecoder.scalax deleted file mode 100644 index a6496204..00000000 --- a/src/main/scala/co.blocke.scalajack/json/reading/JsonDecoder.scalax +++ /dev/null @@ -1,88 +0,0 @@ -package co.blocke.scalajack -package json -package reading - -import scala.annotation.* - -trait JsonDecoder[A]: - self => - final def decodeJson(str: CharSequence): Either[JsonParseError, A] = - try Right(unsafeDecode(new JsonSource(str))) - catch { - case jpe: JsonParseError => Left(jpe) - } - - final def map[B](f: A => B): JsonDecoder[B] = - new JsonDecoder[B] { - def unsafeDecode(in: JsonSource): B = - f(self.unsafeDecode(in)) - } - - def unsafeDecode(in: JsonSource): A - -//------------------------------------------------------------ - -object JsonDecoder extends DecoderLowPriority1: - - // def apply[A](implicit a: JsonDecoder[A]): JsonDecoder[A] = a - - // Primitive support... - implicit val string: JsonDecoder[String] = new JsonDecoder[String] { - def unsafeDecode(in: JsonSource): String = - JsonParser.parseString(in).toString - } - - implicit val boolean: JsonDecoder[Boolean] = new JsonDecoder[Boolean] { - def unsafeDecode(in: JsonSource): Boolean = - JsonParser.parseBoolean(in) - } - - implicit val int: JsonDecoder[Int] = number(JsonParser.parseInt, _.intValueExact()) - private def number[A]( - f: (JsonSource) => A, - fromBigDecimal: java.math.BigDecimal => A - ): JsonDecoder[A] = - new JsonDecoder[A] { - def unsafeDecode(in: JsonSource): A = - (in.readSkipWhitespace(): @switch) match { - case '"' => - val i = f(in) - JsonParser.char(in, '"') - i - case _ => - in.retract() - f(in) - } - } - - private[json] def builder[A, T[_]]( - in: JsonSource, - elemDecoder: JsonDecoder[A], - builder: scala.collection.mutable.Builder[A, T[A]] - ): T[A] = { - JsonParser.charWithWS(in, '[') - var i: Int = 0 - if JsonParser.firstArrayElement(in) then - while { - { - builder += elemDecoder.unsafeDecode(in) - i += 1 - }; JsonParser.nextArrayElement(in) - } do () - builder.result() - } - -//------------------------------------------------------------ - -private trait DecoderLowPriority1: - this: JsonDecoder.type => - - def seq[A](elemDecoder: JsonDecoder[A]): JsonDecoder[Seq[A]] = new JsonDecoder[Seq[A]] { - def unsafeDecode(in: JsonSource): Seq[A] = - builder(in, elemDecoder, scala.collection.immutable.Seq.newBuilder[A]) - } - -// implicit def list[A: JsonDecoder]: JsonDecoder[List[A]] = new JsonDecoder[List[A]] { -// def unsafeDecode(in: JsonSource): List[A] = -// builder(in, elemDecoder, new scala.collection.mutable.ListBuffer[A]) -// } diff --git a/src/main/scala/co.blocke.scalajack/json/reading/JsonReader.scalax b/src/main/scala/co.blocke.scalajack/json/reading/JsonReader.scalax deleted file mode 100644 index a3410384..00000000 --- a/src/main/scala/co.blocke.scalajack/json/reading/JsonReader.scalax +++ /dev/null @@ -1,94 +0,0 @@ -package co.blocke.scalajack -package json -package reading - -import co.blocke.scala_reflection.{RTypeRef, TypedName} -import co.blocke.scala_reflection.rtypes.{OptionRType, ScalaFieldInfo} -import co.blocke.scala_reflection.reflect.rtypeRefs.* -import scala.quoted.* -import scala.collection.mutable.HashMap -import scala.util.{Failure, Success, Try} -import scala.collection.Factory - -/** We depart from ZIO-Json here. ZIO-Json uses implicits to marshal the right JsonDecoder. This works great for primitive types - * but I had issues trying to get it to work with macros for more complex types. Since we actually have all the necessary elements - * to explicitly provide decoders for "constructed" types (collections, classes, ...) we just provided them explicitly. - * - * The job of JsonReader is to accept an RTypeRef[T] and produce a JsonDecoder[T] for type T. - */ -object JsonReader: - - // Temporary no-op reader... - def refRead[T]( - ref: RTypeRef[T] - )(using q: Quotes, tt: Type[T]): Expr[JsonDecoder[T]] = - import quotes.reflect.* - '{ - new JsonDecoder[T] { - def unsafeDecode(in: JsonSource): T = null.asInstanceOf[T] - } - } - - def refRead2[T]( - ref: RTypeRef[T] - )(using q: Quotes, tt: Type[T]): Expr[JsonDecoder[T]] = - import quotes.reflect.* - - ref match - case r: PrimitiveRef => - r.refType match - case '[t] => - Expr - .summon[JsonDecoder[t]] - .getOrElse(throw JsonTypeError("No JsonDecoder defined for type " + TypeRepr.of[t].typeSymbol.name)) - .asInstanceOf[Expr[JsonDecoder[T]]] - - case r: SeqRef[?] => - r.refType match - case '[t] => - r.elementRef.refType match - case '[e] => - val elemDecoder = Expr.summon[JsonDecoder[e]].getOrElse(refRead2[e](r.elementRef.asInstanceOf[RTypeRef[e]])) - '{ - JsonDecoder.seq[e]($elemDecoder) map (_.to(${ Expr.summon[Factory[e, T]].get })) - } - - case r: ScalaClassRef[?] => - val fieldNames = Expr(r.fields.map(_.name).toArray) - val fieldDecoders = Expr.ofList( - r.fields.map(f => - f.fieldRef.refType match - case '[e] => - Expr.summon[JsonDecoder[e]].getOrElse(refRead2[e](f.fieldRef.asInstanceOf[RTypeRef[e]])) - ) - ) - val instantiator = JsonReaderUtil.classInstantiator[T](r.asInstanceOf[ClassRef[T]]) - val optionalFields = Expr(r.fields.zipWithIndex.collect { case (f, i) if f.fieldRef.isInstanceOf[OptionRef[_]] => i }.toArray) - val fieldsE = Expr.ofList(r.fields.asInstanceOf[List[ScalaFieldInfoRef]].map(_.expr)) - - // Constructor argument list, preloaded with optional 'None' values and any default values specified - val preloaded = Expr - .ofList(r.fields.map { f => - val scalaF = f.asInstanceOf[ScalaFieldInfoRef] - if scalaF.defaultValueAccessorName.isDefined then - r.refType match - case '[t] => - val tpe = TypeRepr.of[t].widen - val sym = tpe.typeSymbol - val companionBody = sym.companionClass.tree.asInstanceOf[ClassDef].body - val companion = Ref(sym.companionModule) - companionBody - .collect { - case defaultMethod @ DefDef(name, _, _, _) if name.startsWith("$lessinit$greater$default$" + (f.index + 1)) => - companion.select(defaultMethod.symbol).appliedToTypes(tpe.typeArgs).asExpr - } - .headOption - .getOrElse(Expr(null.asInstanceOf[Boolean])) - else if scalaF.fieldRef.isInstanceOf[OptionRef[_]] then Expr(None) - else Expr(null.asInstanceOf[Int]) - }) - - val x = '{ ClassDecoder[T]($fieldNames, $fieldDecoders.toArray, $instantiator, $preloaded.toArray) } - println(s"Class Decoder: ${x.show}") - println("---------------------") - x diff --git a/src/main/scala/co.blocke.scalajack/json/reading/JsonReader.scalax2 b/src/main/scala/co.blocke.scalajack/json/reading/JsonReader.scalax2 deleted file mode 100644 index e72bc0b1..00000000 --- a/src/main/scala/co.blocke.scalajack/json/reading/JsonReader.scalax2 +++ /dev/null @@ -1,114 +0,0 @@ -package co.blocke.scalajack -package json -package reading - -import scala.annotation.* - -object JsonReader: - protected val ull: Array[Char] = "ull".toCharArray - protected val alse: Array[Char] = "alse".toCharArray - protected val rue: Array[Char] = "rue".toCharArray - -case class JsonReader(src: JsonSource): - - private var expectFieldValue = false - - // returns false if 'null' found - def expectObjectStart(): Boolean = - src.readSkipWhitespace() match { - case '{' => true - case 'n' => - readChars(JsonReader.ull, "null") - false - case c => throw new JsonParseError(s"Expected object start '{' but found '$c'", src) - } - - def expectArrayStart(): Boolean = - src.readSkipWhitespace() match { - case '[' => true - case 'n' => - readChars(JsonReader.ull, "null") - false - case c => throw new JsonParseError(s"Expected array start '[' but found '$c'", src) - } - - // True if we got a comma, and False for ] - def nextArrayElement(): Boolean = - (src.readSkipWhitespace(): @switch) match - case ',' => true - case ']' => false - case c => throw JsonParseError(s"Expected ',' or ']' got '$c'", src) - - // True if we got a comma, and False for } - def nextField(): Boolean = - (src.readSkipWhitespace(): @switch) match { - case ',' => - expectFieldValue = false - true - case '}' if !expectFieldValue => false - case '}' => - throw JsonParseError("Expected field value but got '}' instead.", src) - case c => - throw JsonParseError(s"expected ',' or '}' got '$c'", src) - } - - inline def expectFieldName(): CharSequence = - val charseq = expectString() - expectFieldValue = true - charseq - - // need flavor of expectString that might be null for field values - - private def expectString(): CharSequence = - charWithWS(src, '"') - val sb = new FastStringBuilder(64) - while true do - val c = src.readEscapedString() - if c == END_OF_STRING then return sb.buffer // mutable thing escapes, but cannot be changed - sb.append(c.toChar) - throw JsonParseError("Invalid string value detected", src) - - inline def expectChar(): Char = - expectString() match { - case s if s.length == 1 => s.charAt(0) - case s => throw new JsonParseError(s"Expected a Char value but got '$s'", src) - } - - def expectBoolean(): Boolean = - (src.readSkipWhitespace(): @switch) match - case 't' => - readChars(JsonReader.rue, "true") - true - case 'f' => - readChars(JsonReader.alse, "false") - false - case c => throw JsonParseError(s"Expected 'true' or 'false' got '$c'", src) - - def expectInt(): Int = - checkNumber() - try { - val i = UnsafeNumbers.int_(src, false) - src.retract() - i - } catch { - case UnsafeNumbers.UnsafeNumber => throw JsonParseError("Expected an Int", src) - } - - private inline def readChars( - expect: Array[Char], - errMsg: String - ): Unit = - var i: Int = 0 - while i < expect.length do - if src.read() != expect(i) then throw JsonParseError(s"Expected $errMsg", src) - i += 1 - - private def checkNumber(): Unit = - (src.readSkipWhitespace(): @switch) match - case '-' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '.' => () - case c => throw JsonParseError(s"Expected a number, got $c", src) - src.retract() - - inline def charWithWS(in: JsonSource, c: Char): Unit = - val got = in.readSkipWhitespace() - if got != c then throw JsonParseError(s"Expected '$c' got '$got'", src) diff --git a/src/main/scala/co.blocke.scalajack/json/reading/JsonReaderUtil.scala b/src/main/scala/co.blocke.scalajack/json/reading/JsonReaderUtil.scala deleted file mode 100644 index 611f831f..00000000 --- a/src/main/scala/co.blocke.scalajack/json/reading/JsonReaderUtil.scala +++ /dev/null @@ -1,96 +0,0 @@ -package co.blocke.scalajack -package json -package reading - -import scala.quoted.* -import co.blocke.scala_reflection.reflect.rtypeRefs.* -import co.blocke.scala_reflection.{Clazzes, RTypeRef, TypedName} - -object JsonReaderUtil: - - // def classInstantiator[T: Type](ref: ClassRef[T])(using Quotes): Expr[Map[String, ?] => T] = - // import quotes.reflect.* - // val sym = TypeRepr.of[T].classSymbol.get - // '{ (fieldMap: Map[String, ?]) => - // ${ - // val tree = Apply( - // Select.unique(New(TypeIdent(sym)), ""), - // ref.fields.map { f => - // f.fieldRef.refType match - // case '[t] => - // '{ fieldMap(${ Expr(f.name) }).asInstanceOf[t] }.asTerm - // } - // ) - // tree.asExpr.asExprOf[T] - // } - // } - - // Given an array of Any, produce a T (class instance) - def classInstantiator[T: Type](ref: ClassRef[T])(using Quotes): Expr[Array[?] => T] = - import quotes.reflect.* - - val aTpr = TypeRepr.of[T] - val ctor = aTpr.typeSymbol.primaryConstructor - - '{ (fieldValues: Array[?]) => - ${ - New(Inferred(aTpr)) - .select(ctor) - .appliedToArgs({ - ref.fields.map { case f => - f.fieldRef.refType match - case '[t] => // get the Type of each field (stored in fieldRef.refType) - val idx = Expr(f.index) - '{ fieldValues($idx).asInstanceOf[t] }.asTerm - } - }) - .asExprOf[T] - } - } - - /* - def tupleInstantiator[T: Type](ref: TupleRef[T])(using Quotes): Expr[List[?] => T] = - import quotes.reflect.* - val sym = TypeRepr.of[T].classSymbol.get - '{ (untyped: List[?]) => - ${ - val tree = Apply( - // Must correctly type the tuple, both the class type params and the individual values - TypeApply( - Select.unique(New(TypeIdent(sym)), ""), - ref.tupleRefs.map { r => - r.refType match - case '[s] => - TypeTree.of[s] - } - ), - ref.tupleRefs.zipWithIndex.map { (f, j) => - f.refType match - case '[t] => - val jExpr = Expr(j) - '{ untyped($jExpr).asInstanceOf[t] }.asTerm - } - ) - tree.asExpr.asExprOf[T] - } - } - */ - - // def classParseMap[T: Type](ref: ClassRef[T], root: ReaderModule)(using q: Quotes)(using - // cache: scala.collection.mutable.HashMap[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]] - // ): Expr[JsonParser => Map[String, (JsonConfig, JsonParser) => Either[ParseError, ?]]] = - // import Clazzes.* - // '{ (parser: JsonParser) => - // val daList = ${ - // val fieldList = ref.fields.map(f => - // f.fieldRef.refType match - // case '[m] => - // val fn = root.readerFn[m](f.fieldRef.asInstanceOf[RTypeRef[m]]) - // '{ - // ${ Expr(f.name) } -> $fn - // } - // ) - // Expr.ofList(fieldList) - // } - // daList.toMap - // } diff --git a/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala b/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala index b74494aa..d6a0b2d6 100644 --- a/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala +++ b/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala @@ -3,25 +3,25 @@ package json package reading import scala.annotation.* +import scala.annotation.tailrec object JsonSource: protected val ull: Array[Char] = "ull".toCharArray protected val alse: Array[Char] = "alse".toCharArray protected val rue: Array[Char] = "rue".toCharArray + protected val falseBytes = 'f' | 'a' << 8 | 'l' << 16 | 's' << 24 | 'e' << 32 + protected val trueBytes = 't' | 'r' << 8 | 'u' << 16 | 'e' << 24 // ZIO-Json defines a series of different Readers. Not exactly sure why--maybe to support different // modes (streaming, ...)? At least for now we only need one, so merged key bits of Readers into one. -case class JsonSource(js: CharSequence, jsBytes: Array[Byte]): +case class JsonSource(js: CharSequence): var i = 0 private var expectFieldValue = false private[json] val max = js.length - // Jsoniter ParseString machinery - val ps = ParseString(jsBytes) - val cbuf = new Array[Char](4048) - def pos = i + // inline def here = js(i).toChar inline def here = js.charAt(i) private var c: Char = 0 @@ -32,22 +32,19 @@ case class JsonSource(js: CharSequence, jsBytes: Array[Byte]): c else BUFFER_EXCEEDED + // JSON definition of whitespace + private inline def isWhitespace(c: Char): Boolean = + c == ' ' || c == '\n' || (c | 0x4) == '\r' || c == '\t' + inline def readCharWS(): Char = - var c: Char = 0 - while { c = readChar(); isWhitespace(c) && c != BUFFER_EXCEEDED } do () - c + while isWhitespace(here) && i < max do i += 1 + readChar() - // inline def retract() = i -= 1 + inline def skipWS(): Unit = + while isWhitespace(here) && i < max do i += 1 + if i == max then throw new JsonParseError("Unexpected end of buffer", this) - // JSON definition of whitespace - private inline def isWhitespace(c: Char): Boolean = - (c: @switch) match { - case ' ' => true - case '\r' => true - case '\n' => true - case '\t' => true - case _ => false - } + inline def retract() = i -= 1 @inline private[this] def isNumber(c: Char): Boolean = (c: @switch) match @@ -90,64 +87,63 @@ case class JsonSource(js: CharSequence, jsBytes: Array[Byte]): //------- // returns false if 'null' found - def expectFirstObjectField(fieldNameMatrix: StringMatrix): Int = + def expectFirstObjectField(): Option[CharSequence] = readCharWS() match { case '{' => readCharWS() match { case '"' => - var fi: Int = 0 - var bs: Long = fieldNameMatrix.initial - var c: Int = -1 - while { c = readEscapedChar(); c != END_OF_STRING && c != BUFFER_EXCEEDED } do { - bs = fieldNameMatrix.update(bs, fi, c) - fi += 1 - } + val endI = parseString(i) + val fname = js.subSequence(i, endI) + i = endI + 1 if readCharWS() != ':' then throw new JsonParseError(s"Expected ':' field separator", this) - bs = fieldNameMatrix.exact(bs, fi) - fieldNameMatrix.first(bs) - case '}' => -2 // end-of-object (empty, not null) + Some(fname) + case '}' => None // end-of-object (empty, not null) case c => throw new JsonParseError(s"Expected object field name or '}' but found '$c'", this) } case 'n' => readChars(JsonSource.ull, "null") - -3 // null + null case c => throw new JsonParseError(s"Expected object start '{' but found '$c'", this) } - def expectObjectField(fieldNameMatrix: StringMatrix): Int = + def expectObjectField(): Option[CharSequence] = readCharWS() match { case ',' => readCharWS() match { case '"' => - var fi: Int = 0 - var bs: Long = fieldNameMatrix.initial - var c: Int = -1 - while { c = readEscapedChar(); c != END_OF_STRING && c != BUFFER_EXCEEDED } do { - bs = fieldNameMatrix.update(bs, fi, c) - fi += 1 - } + val endI = parseString(i) + val fname = js.subSequence(i, endI) + i = endI + 1 if readCharWS() != ':' then throw new JsonParseError(s"Expected ':' field separator", this) - bs = fieldNameMatrix.exact(bs, fi) - fieldNameMatrix.first(bs) + Some(fname) case c => throw new JsonParseError(s"Expected object field name but found '$c'", this) } - case '}' => -2 // end-of-object - case c => throw new JsonParseError(s"Expected ',' or '}' but found '$c'", this) + case '}' => None // end-of-object + case c => + throw new JsonParseError(s"Expected ',' or '}' but found '$c'", this) } - def expectArrayStart(): Boolean = - readCharWS() match { + def expectArray[E](f: () => E): scala.collection.mutable.ListBuffer[E] = + (readCharWS(): @switch) match case '[' => + val seq = scala.collection.mutable.ListBuffer.empty[E] + skipWS() + while i < max && here != ']' do + seq.addOne(f()) + readCharWS() match { + case ']' => retract() + case ',' => + case c => throw JsonParseError(s"Expected ',' or ']' got '$c'", this) + } i += 1 - true + seq case 'n' => readChars(JsonSource.ull, "null") - false - case c => throw new JsonParseError(s"Expected array start '[' but found '$c'", this) - } - - /// ------------------- Continue refresh here.... >>>>>>>> + null + // --------------- + // DEPRECATED !!! + // --------------- // True if we got anything besides a ], False for ] def firstArrayElement(): Boolean = (readCharWS(): @switch) match @@ -156,6 +152,9 @@ case class JsonSource(js: CharSequence, jsBytes: Array[Byte]): retract() true + // --------------- + // DEPRECATED !!! + // --------------- // True if we got a comma, and False for ] def nextArrayElement(): Boolean = (readCharWS(): @switch) match @@ -165,6 +164,9 @@ case class JsonSource(js: CharSequence, jsBytes: Array[Byte]): false case c => throw JsonParseError(s"Expected ',' or ']' got '$c'", this) + // --------------- + // DEPRECATED !!! + // --------------- // True if we got a string (implies a retraction), False for } def firstField(): Boolean = (readCharWS(): @switch) match { @@ -174,6 +176,9 @@ case class JsonSource(js: CharSequence, jsBytes: Array[Byte]): throw JsonParseError(s"expected string or '}' got '$c'", this) } + // --------------- + // DEPRECATED !!! + // --------------- // True if we got a comma, and False for } def nextField(): Boolean = (readCharWS(): @switch) match { @@ -189,44 +194,36 @@ case class JsonSource(js: CharSequence, jsBytes: Array[Byte]): throw JsonParseError(s"expected ',' or '}' got '$c'", this) } - inline def expectFieldName(fieldNameMatrix: StringMatrix): Int = - charWithWS('"') - expectFieldValue = true - var fi: Int = 0 - var bs: Long = fieldNameMatrix.initial - var c: Int = -1 - while { c = readEscapedChar(); c != END_OF_STRING } do { - bs = fieldNameMatrix.update(bs, fi, c) - fi += 1 - } - charWithWS(':') - bs = fieldNameMatrix.exact(bs, fi) - fieldNameMatrix.first(bs) - // Value might be null! - def expectString(): CharSequence = + inline def expectString(): CharSequence = readCharWS() match { case '"' => - val parsedCount = ps.parseString(0, max - i, cbuf, i) - i += parsedCount + 1 - new String(cbuf, 0, parsedCount) - // retract() - // parseString() + val endI = parseString(i) + val str = js.subSequence(i, endI) + i = endI + 1 + str case 'n' => readChars(JsonSource.ull, "null") null case c => throw new JsonParseError(s"Expected a String value but got '$c'", this) } - // private def parseString(): CharSequence = - // charWithWS('"') - // val sb = new FastStringBuilder(64) - // var c: Char = END_OF_STRING - // while - // c = readEscapedChar() - // c != END_OF_STRING - // do sb.append(c.toChar) - // sb.buffer + @tailrec + final def parseString(pos: Int): Int = + if pos + 3 < max then { // Based on SWAR routine of JSON string parsing: https://github.com/sirthias/borer/blob/fde9d1ce674d151b0fee1dd0c2565020c3f6633a/core/src/main/scala/io/bullet/borer/json/JsonParser.scala#L456 + val bs = (js.charAt(pos)) | (js.charAt(pos + 1) << 8) | (js.charAt(pos + 2) << 16) | js.charAt(pos + 3) << 24 + val mask = ((bs - 0x20202020 ^ 0x3c3c3c3c) - 0x1010101 | (bs ^ 0x5d5d5d5d) + 0x1010101) & 0x80808080 + if mask != 0 then { + val offset = java.lang.Integer.numberOfTrailingZeros(mask) >> 3 + if (bs >> (offset << 3)).toByte == '"' then pos + offset + else throw new Exception("special chars found 1") // else parseEncodedString(i + offset, charBuf.length - 1, charBuf, pos + offset) + } else parseString(pos + 4) + } else if pos < max then { + val b = js.charAt(pos) + if b == '"' then pos + else if (b - 0x20 ^ 0x3c) <= 0 then throw new Exception("special chars found 2") // parseEncodedString(i, charBuf.length - 1, charBuf, pos) + else parseString(pos + 1) + } else throw new Exception("Buffer exceeded--string too long") inline def expectChar(): Char = expectString() match { @@ -235,17 +232,18 @@ case class JsonSource(js: CharSequence, jsBytes: Array[Byte]): } def expectBoolean(): Boolean = - (readCharWS(): @switch) match - case 't' => - readChars(JsonSource.rue, "true") - true - case 'f' => - readChars(JsonSource.alse, "false") - false - case c => throw JsonParseError(s"Expected 'true' or 'false' got '$c'", this) + while isWhitespace(here) do i += 1 + val bs = (js.charAt(pos)) | (js.charAt(pos + 1) << 8) | (js.charAt(pos + 2) << 16) | js.charAt(pos + 3) << 24 + if (bs ^ JsonSource.trueBytes) == 0 then + i += 4 + true + else if ((bs | js.charAt(pos + 4)) ^ JsonSource.falseBytes) == 0 then + i += 5 + false + else throw JsonParseError(s"Expected 'true' or 'false'", this) def expectInt(): Int = - checkNumber() + if { skipWS(); !isNumber(here) } then throw JsonParseError(s"Expected a number, got $c", this) try { val intRead = UnsafeNumbers.int_(this, false) retract() @@ -263,12 +261,6 @@ case class JsonSource(js: CharSequence, jsBytes: Array[Byte]): if readChar() != expect(i) then throw JsonParseError(s"Expected $errMsg", this) i += 1 - private def checkNumber(): Unit = - (readCharWS(): @switch) match - case '-' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '.' => () - case c => throw JsonParseError(s"Expected a number, got $c", this) - retract() - inline def charWithWS(c: Char): Unit = val got = readCharWS() if got != c then throw JsonParseError(s"Expected '$c' got '$got'", this) @@ -308,5 +300,3 @@ case class JsonSource(js: CharSequence, jsBytes: Array[Byte]): def skipString(): Unit = var i: Int = 0 while { i = readCharWS(); i != -1 } do () - - inline def retract() = i -= 1 diff --git a/src/main/scala/co.blocke.scalajack/json/reading/JsonSource2.scala b/src/main/scala/co.blocke.scalajack/json/reading/JsonSource2.scala deleted file mode 100644 index 7d05ca66..00000000 --- a/src/main/scala/co.blocke.scalajack/json/reading/JsonSource2.scala +++ /dev/null @@ -1,150 +0,0 @@ -package co.blocke.scalajack -package json -package reading - -import scala.annotation.* - -object JsonSource2: - protected val ull: Array[Char] = "ull".toCharArray - protected val _null: Array[Char] = "null".toCharArray - protected val alse: Array[Char] = "alse".toCharArray - protected val rue: Array[Char] = "rue".toCharArray - -case class JsonSource2(js: CharSequence): - private var i = 0 // The great, omnipresent index - private[json] val max = js.length // don't overrun this, or else... - - inline def here: Char = js.charAt(i) - inline def pos = i - - // Parse philosophy: After parsing a character, always leave index (i) ready to read the next - // character - - private inline def getCharWS: Char = - while i < max && here.isWhitespace do i += 1 - if i == max then BUFFER_EXCEEDED - else here - - private inline def expectChars( - expect: Array[Char], - errMsg: String - ): Unit = - if i + expect.length >= max then throw JsonParseError2("(a) Tried to read past end of JSON buffer", this) - var j = 0 - while j < expect.length && here == expect(j) do { i += 1; j += 1 } - if j == expect.length then () - else throw JsonParseError2(s"(a) Expected $errMsg", this) - - // -- Parse Objects - - inline def expectObject: Boolean = // returns false if null - getCharWS match { - case '{' => - i += 1 - true // true -> start of object found - case 'n' => - i += 1 - expectChars(JsonSource2.ull, "'{' or null") - false // false -> null found - case BUFFER_EXCEEDED => throw JsonParseError2("(1) Tried to read past end of JSON buffer", this) - case _ => - throw JsonParseError2("(1) Expected '{' or null", this) - } - - def expectFieldIndex(fieldNameMatrix: StringMatrix): Int = - (getCharWS: @switch) match { - case '"' => - var fi: Int = 0 - var bs: Long = fieldNameMatrix.initial - var c: Int = -1 - i += 1 - while i < max && here != '"' do - bs = fieldNameMatrix.update(bs, fi, here) - i += 1 - fi += 1 - bs = fieldNameMatrix.exact(bs, fi) - if i == max then throw JsonParseError2("(2) Tried to read past end of JSON buffer", this) - else - i += 1 - (getCharWS: @switch) match { - case ':' => - i += 1 - fieldNameMatrix.first(bs) - case BUFFER_EXCEEDED => throw JsonParseError2("(2) Tried to read past end of JSON buffer", this) - case _ => throw JsonParseError2(s"(2) Expected ':' but got '$here'", this) - } - case BUFFER_EXCEEDED => throw JsonParseError2("(2) Tried to read past end of JSON buffer", this) - case _ => throw JsonParseError2(s"(2) Expected start of field name (string) '\"' but got '$here'", this) - } - - inline def hasFields: Boolean = - (getCharWS: @switch) match { - case '}' => - i += 1 - false - case _ => true - } - - def nextField: Boolean = // true means there is another field. false means '}' -- end of object - (getCharWS: @switch) match { - case '}' => - i += 1 - false - case ',' => - i += 1 - true - case _ => - throw JsonParseError2(s"(3) Malformed JSON. Expected ',' field separator", this) - } - - // -- Parse Array - - inline def expectArray: Boolean = // returns false if null - getCharWS match { - case '[' => - i += 1 - true // true -> start of object found - case 'n' => - i += 1 - expectChars(JsonSource2.ull, "'[' or null") - false // false -> null found - case BUFFER_EXCEEDED => throw JsonParseError2("(4) Tried to read past end of JSON buffer", this) - case _ => - throw JsonParseError2("(4) Expected '[' or null", this) - } - - inline def hasElements: Boolean = - (getCharWS: @switch) match { - case ']' => - i += 1 - false - case _ => true - } - - def nextElement: Boolean = // true means there is another field. false means '}' -- end of object - (getCharWS: @switch) match { - case ']' => - i += 1 - false - case ',' => - i += 1 - true - case _ => - throw JsonParseError2(s"(5) Malformed JSON. Expected ',' field separator", this) - } - - // -- Parse JSON Simple Data Types - - def expectBoolean: Boolean = - getCharWS match { - case 't' => - i += 1 - expectChars(JsonSource2.rue, "true") - true - case 'f' => - i += 1 - expectChars(JsonSource2.alse, "false") - false - case BUFFER_EXCEEDED => throw JsonParseError2("(6) Tried to read past end of JSON buffer", this) - case _ => throw JsonParseError2(s"(6) Expected boolean value", this) - } diff --git a/src/main/scala/co.blocke.scalajack/json/reading/Numbers.scala b/src/main/scala/co.blocke.scalajack/json/reading/Numbers.scala index 5bcf8b8c..30d43c29 100644 --- a/src/main/scala/co.blocke.scalajack/json/reading/Numbers.scala +++ b/src/main/scala/co.blocke.scalajack/json/reading/Numbers.scala @@ -529,27 +529,27 @@ object UnsafeNumbers { with NoStackTrace def byte(num: String): Byte = - byte_(new JsonSource(num, num.getBytes), true) + byte_(new JsonSource(num), true) def byte_(in: JsonSource, consume: Boolean): Byte = long__(in, Byte.MinValue, Byte.MaxValue, consume).toByte def short(num: String): Short = - short_(new JsonSource(num, num.getBytes), true) + short_(new JsonSource(num), true) def short_(in: JsonSource, consume: Boolean): Short = long__(in, Short.MinValue, Short.MaxValue, consume).toShort def int(num: String): Int = - int_(new JsonSource(num, num.getBytes), true) + int_(new JsonSource(num), true) def int_(in: JsonSource, consume: Boolean): Int = long__(in, Int.MinValue, Int.MaxValue, consume).toInt def long(num: String): Long = - long_(new JsonSource(num, num.getBytes), true) + long_(new JsonSource(num), true) def long_(in: JsonSource, consume: Boolean): Long = long__(in, Long.MinValue, Long.MaxValue, consume) def bigInteger(num: String, max_bits: Int): java.math.BigInteger = - bigInteger_(new JsonSource(num, num.getBytes), true, max_bits) + bigInteger_(new JsonSource(num), true, max_bits) def bigInteger_( in: JsonSource, consume: Boolean, @@ -617,7 +617,7 @@ object UnsafeNumbers { } def float(num: String, max_bits: Int): Float = - float_(new JsonSource(num, num.getBytes), true, max_bits) + float_(new JsonSource(num), true, max_bits) def float_(in: JsonSource, consume: Boolean, max_bits: Int): Float = { var current: Int = in.readChar() @@ -666,7 +666,7 @@ object UnsafeNumbers { } def double(num: String, max_bits: Int): Double = - double_(new JsonSource(num, num.getBytes), true, max_bits) + double_(new JsonSource(num), true, max_bits) def double_(in: JsonSource, consume: Boolean, max_bits: Int): Double = { var current: Int = in.readChar() @@ -719,7 +719,7 @@ object UnsafeNumbers { } def bigDecimal(num: String, max_bits: Int): java.math.BigDecimal = - bigDecimal_(new JsonSource(num, num.getBytes), true, max_bits) + bigDecimal_(new JsonSource(num), true, max_bits) def bigDecimal_( in: JsonSource, consume: Boolean, diff --git a/src/main/scala/co.blocke.scalajack/json/reading/ParseString.scala b/src/main/scala/co.blocke.scalajack/json/reading/ParseString.scala deleted file mode 100644 index f0b07b99..00000000 --- a/src/main/scala/co.blocke.scalajack/json/reading/ParseString.scala +++ /dev/null @@ -1,226 +0,0 @@ -package co.blocke.scalajack -package json -package reading - -import scala.annotation.{switch, tailrec} -import java.nio.ByteBuffer -import java.io.InputStream -import co.blocke.scalajack.util.ByteArrayAccess - -case class ParseString(var buf: Array[Byte], var charBuf: Array[Char] = new Array[Char](4096)): - - // var buf: Array[Byte] = new Array[Byte](32768) - var head: Int = 0 - var tail: Int = buf.length - // var mark: Int = -1 - // var bbuf: ByteBuffer = null - // var in: InputStream = null - val maxCharBufSize = 4194304 - - /** - * - * - * @param i - position in charBuf to start populating - * @param minLim -- ??? - * @param charBuf -- Array containing characters of the resultant parsed string - * @param pos -- position in the input buffer to start reading from (ie current parse position) - * @return number of characters read - */ - @tailrec - final def parseString(i: Int, minLim: Int, charBuf: Array[Char], pos: Int): Int = - if (i + 3 < minLim) { // Based on SWAR routine of JSON string parsing: https://github.com/sirthias/borer/blob/fde9d1ce674d151b0fee1dd0c2565020c3f6633a/core/src/main/scala/io/bullet/borer/json/JsonParser.scala#L456 - val bs = ByteArrayAccess.getInt(buf, pos) - val m = ((bs - 0x20202020 ^ 0x3C3C3C3C) - 0x1010101 | (bs ^ 0x5D5D5D5D) + 0x1010101) & 0x80808080 - charBuf(i) = (bs & 0xFF).toChar - charBuf(i + 1) = (bs >> 8 & 0xFF).toChar - charBuf(i + 2) = (bs >> 16 & 0xFF).toChar - charBuf(i + 3) = (bs >> 24).toChar - if (m != 0) { - val offset = java.lang.Integer.numberOfTrailingZeros(m) >> 3 - if ((bs >> (offset << 3)).toByte == '"') { - head = pos + offset + 1 - i + offset - } else throw new Exception("special chars found 1") //else parseEncodedString(i + offset, charBuf.length - 1, charBuf, pos + offset) - } else parseString(i + 4, minLim, charBuf, pos + 4) - } else if (i < minLim) { - val b = buf(pos) - charBuf(i) = b.toChar - if (b == '"') { - head = pos + 1 - i - } else if ((b - 0x20 ^ 0x3C) <= 0) throw new Exception("special chars found 2") //parseEncodedString(i, charBuf.length - 1, charBuf, pos) - else parseString(i + 1, minLim, charBuf, pos + 1) - } else if (pos >= tail) { - throw new Exception("Buffer overlow while parsing string") - } else - parseString(i, Math.min(growCharBuf(i + 1), i + tail - pos), this.charBuf, pos) - - /* - @tailrec - private[this] def parseEncodedString(i: Int, lim: Int, charBuf: Array[Char], pos: Int): Int = { - val remaining = tail - pos - if (i < lim) { - if (remaining > 0) { - val b1 = buf(pos) - if (b1 >= 0) { - if (b1 == '"') { - head = pos + 1 - i - } else if (b1 != '\\') { // 0aaaaaaa (UTF-8 byte) -> 000000000aaaaaaa (UTF-16 char) - if (b1 < ' ') throw new Exception(s"unescapedControlCharacterError($pos)") - charBuf(i) = b1.toChar - parseEncodedString(i + 1, lim, charBuf, pos + 1) - } else if (remaining > 1) { - val b2 = buf(pos + 1) - if (b2 != 'u') { - charBuf(i) = (b2: @switch) match { - case '"' => '"' - case 'n' => '\n' - case 'r' => '\r' - case 't' => '\t' - case 'b' => '\b' - case 'f' => '\f' - case '\\' => '\\' - case '/' => '/' - case _ => throw new Exception(s"escapeSequenceError(${pos + 1})") - } - parseEncodedString(i + 1, lim, charBuf, pos + 2) - } else if (remaining > 5) { - val ch1 = readEscapedUnicode(pos + 2, buf) - charBuf(i) = ch1 - if ((ch1 & 0xF800) != 0xD800) parseEncodedString(i + 1, lim, charBuf, pos + 6) - else if (remaining > 11) { - if (buf(pos + 6) != '\\') throw new Exception(s"escapeSequenceError(${pos + 6})") - if (buf(pos + 7) != 'u') throw new Exception(s"escapeSequenceError(${pos + 7})") - val ch2 = readEscapedUnicode(pos + 8, buf) - charBuf(i + 1) = ch2 - if (ch1 >= 0xDC00 || (ch2 & 0xFC00) != 0xDC00) throw new Exception(s"decodeError(\"illegal surrogate character pair\", ${pos + 11})") - parseEncodedString(i + 2, lim, charBuf, pos + 12) - } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos)) - } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos)) - } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos)) - } else if ((b1 & 0xE0) == 0xC0) { // 110bbbbb 10aaaaaa (UTF-8 bytes) -> 00000bbbbbaaaaaa (UTF-16 char) - if (remaining > 1) { - val b2 = buf(pos + 1) - val ch = (b1 << 6 ^ b2 ^ 0xF80).toChar // 0xF80 == 0xC0.toByte << 6 ^ 0x80.toByte - charBuf(i) = ch - if ((b2 & 0xC0) != 0x80 || ch < 0x80) throw new Exception(s"malformedBytesError($b1, $b2, $pos)") - parseEncodedString(i + 1, lim, charBuf, pos + 2) - } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos)) - } else if ((b1 & 0xF0) == 0xE0) { // 1110cccc 10bbbbbb 10aaaaaa (UTF-8 bytes) -> ccccbbbbbbaaaaaa (UTF-16 char) - if (remaining > 2) { - val b2 = buf(pos + 1) - val b3 = buf(pos + 2) - val ch = (b1 << 12 ^ b2 << 6 ^ b3 ^ 0x1F80).toChar // 0x1F80 == (0x80.toByte << 6 ^ 0x80.toByte).toChar - charBuf(i) = ch - if ((b2 & 0xC0) != 0x80 || (b3 & 0xC0) != 0x80 || ch < 0x800 || - (ch & 0xF800) == 0xD800) throw new Exception(s"malformedBytesError($b1, $b2, $b3, $pos)") - parseEncodedString(i + 1, lim, charBuf, pos + 3) - } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos)) - } else if ((b1 & 0xF8) == 0xF0) { // 11110ddd 10ddcccc 10bbbbbb 10aaaaaa (UTF-8 bytes) -> 110110uuuuccccbb 110111bbbbaaaaaa (UTF-16 chars), where uuuu = ddddd - 1 - if (remaining > 3) { - val b2 = buf(pos + 1) - val b3 = buf(pos + 2) - val b4 = buf(pos + 3) - val cp = b1 << 18 ^ b2 << 12 ^ b3 << 6 ^ b4 ^ 0x381F80 // 0x381F80 == 0xF0.toByte << 18 ^ 0x80.toByte << 12 ^ 0x80.toByte << 6 ^ 0x80.toByte - val ch1 = ((cp >>> 10) + 0xD7C0).toChar // 0xD7C0 == 0xD800 - (0x10000 >>> 10) - charBuf(i) = ch1 - charBuf(i + 1) = ((cp & 0x3FF) + 0xDC00).toChar - if ((b2 & 0xC0) != 0x80 || (b3 & 0xC0) != 0x80 || (b4 & 0xC0) != 0x80 || - (ch1 & 0xF800) != 0xD800) throw new Exception(s"malformedBytesError($b1, $b2, $b3, $b4, $pos)") - parseEncodedString(i + 2, lim, charBuf, pos + 4) - } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos)) - } else throw new Exception(s"malformedBytesError($b1, $pos)") - } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos)) - } else parseEncodedString(i, growCharBuf(i + 2) - 1, this.charBuf, pos) // 2 is length of surrogate pair - } - - private[this] def readEscapedUnicode(pos: Int, buf: Array[Byte]): Char = { - val ns = nibbles - val x = - ns(buf(pos) & 0xFF) << 12 | - ns(buf(pos + 1) & 0xFF) << 8 | - ns(buf(pos + 2) & 0xFF) << 4 | - ns(buf(pos + 3) & 0xFF) - if (x < 0) throw new Exception(s"hexDigitError($pos)") - x.toChar - } - */ - - private[this] def growCharBuf(required: Int): Int = { - var charBufLen = charBuf.length - if (charBufLen == maxCharBufSize) throw new Exception("tooLongStringError") - charBufLen = (-1 >>> Integer.numberOfLeadingZeros(charBufLen | required)) + 1 - if (charBufLen > maxCharBufSize || charBufLen < 0) charBufLen = maxCharBufSize - charBuf = java.util.Arrays.copyOf(charBuf, charBufLen) - charBufLen - } - - /* - private final val nibbles: Array[Byte] = Array( - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, - -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 - ) - */ - - /* - private[this] def loadMoreOrError(pos: Int): Int = { - if ((bbuf eq null) && (in eq null)) throw new Exception("endOfInputError") - loadMore(pos, throwOnEndOfInput = true) - } - - private[this] def loadMore(pos: Int): Int = - if ((bbuf eq null) && (in eq null)) pos - else loadMore(pos, throwOnEndOfInput = false) - - private[this] def loadMore(pos: Int, throwOnEndOfInput: Boolean): Int = { - var newPos = pos - val offset = - if (mark < 0) pos - else mark - if (offset > 0) { - newPos -= offset - val buf = this.buf - val remaining = tail - offset - var i = 0 - while (i < remaining) { - buf(i) = buf(i + offset) - i += 1 - } - if (mark > 0) mark = 0 - tail = remaining - head = newPos - } else growBuf() - var len = buf.length - tail - if (bbuf ne null) { - len = Math.min(bbuf.remaining, len) - bbuf.get(buf, tail, len) - } else len = Math.max(in.read(buf, tail, len), 0) - if (throwOnEndOfInput && len == 0) throw new Exception("endOfInputError") - tail += len - totalRead += len - newPos - } - - private[this] def growBuf(): Unit = { - var bufLen = buf.length - if (bufLen == maxBufSize) throw new Exception("tooLongInputError") - bufLen <<= 1 - if (bufLen > maxBufSize || bufLen < 0) bufLen = maxBufSize - buf = java.util.Arrays.copyOf(buf, bufLen) - } - */ diff --git a/src/main/scala/co.blocke.scalajack/json/reading/StringMatrix.scala b/src/main/scala/co.blocke.scalajack/json/reading/StringMatrix.scala deleted file mode 100644 index b2cbbe85..00000000 --- a/src/main/scala/co.blocke.scalajack/json/reading/StringMatrix.scala +++ /dev/null @@ -1,81 +0,0 @@ -package co.blocke.scalajack -package json -package reading - -// A data structure encoding a simple algorithm for Trie pruning: Given a list -// of strings, and a sequence of incoming characters, find the strings that -// match, by manually maintaining a bitset. Empty strings are not allowed. -// -// ScalaJack: Removal of ZIO's original aliases feature for speed--we don't need this. -// -final class StringMatrix(val xs: Array[String]) { - val width = xs.length - val height: Int = xs.map(_.length).max - val lengths: Array[Int] = xs.map(_.length) - val initial: Long = (0 until width).foldLeft(0L)((bs, r) => bs | (1L << r)) - - private val matrix: Array[Int] = { - val m = Array.fill[Int](width * height)(-1) - var string: Int = 0 - while string < width do { - val s = xs(string) - val len = s.length - var char: Int = 0 - while char < len do { - m(width * char + string) = s.codePointAt(char) - char += 1 - } - string += 1 - } - m - } - - private val resolve: Array[Int] = Array.tabulate[Int](xs.length)(identity) - - // must be called with increasing `char` (starting with bitset obtained from a - // call to 'initial', char = 0) - def update(bitset: Long, char: Int, c: Int): Long = - if char >= height then 0L // too long - else if bitset == 0L then 0L // everybody lost - else { - var latest: Long = bitset - val base: Int = width * char - - if bitset == initial then { // special case when it is dense since it is simple - var string: Int = 0 - while string < width do { - if matrix(base + string) != c then latest = latest ^ (1L << string) - string += 1 - } - } else { - var remaining: Long = bitset - while remaining != 0L do { - val string: Int = java.lang.Long.numberOfTrailingZeros(remaining) - val bit: Long = 1L << string - if matrix(base + string) != c then latest = latest ^ bit - remaining = remaining ^ bit - } - } - - latest - } - - // excludes entries that are not the given exact length - def exact(bitset: Long, length: Int): Long = - if length > height then 0L // too long - else { - var latest: Long = bitset - var remaining: Long = bitset - while remaining != 0L do { - val string: Int = java.lang.Long.numberOfTrailingZeros(remaining) - val bit: Long = 1L << string - if lengths(string) != length then latest = latest ^ bit - remaining = remaining ^ bit - } - latest - } - - def first(bitset: Long): Int = - if bitset == 0L then -1 - else resolve(java.lang.Long.numberOfTrailingZeros(bitset)) // never returns 64 -} diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index 8f61a1fc..173f8f84 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -57,134 +57,89 @@ object RunMe extends App: println("done.") - // def r0(in: JsonSource): Foom = { - // val fieldMatrix: StringMatrix = new StringMatrix(Array("a", "b")) - // val args: Array[Any] = Array[Any](Foom.$lessinit$greater$default$1, Foom.$lessinit$greater$default$2)(ClassTag.Any) - - // if !in.expectObjectStart() then null.asInstanceOf[Foom] - // else { - // if in.here.!=('}') then - // while { - // in.expectFieldName(fieldMatrix) match { - // case 0 => - // args(0) = in.expectInt() - // case 1 => - // args(1) = in.expectString().toString() - // case -1 => - // in.skipValue() - // } - // in.nextField() - // } do () - // else in.read() - // ((fieldValues: Array[Any]) => new Foom(fieldValues(0).asInstanceOf[scala.Int], fieldValues(1).asInstanceOf[String])).apply(args) - // } - // } - /* - val buf = Array.fill(1000)('Z') - val buf2 = new String(buf) - val buf3 = buf2.getBytes - var i = 0 - val t0 = System.nanoTime() + val msg = """This is a test"""" + + val cbuf = new Array[Char](4048) + val ta = System.nanoTime() while i < 1000 do - buf2.charAt(i) + val ps = reading.ParseString(msg.getBytes) + val z = ps.parseString(0, msg.length(), cbuf, 0) + new String(cbuf, 0, z) i += 1 - val t1 = System.nanoTime() - println("Time: " + (t1 - t0)) // 1,802,166 + val tb = System.nanoTime() + println("Time (Jsoniter) : " + (tb - ta)) // 3235208 println("------") - // Wow... so ByteArrayAccess is about 1/2 as fast as not using it. :-O i = 0 - val max = 997 // buf2.length - println("MAX: " + max) - val t2 = System.nanoTime() - while i < max do - co.blocke.scalajack.util.ByteArrayAccess.getInt(buf3, i) + val tr = System.nanoTime() + val msgX = "\"" + msg + while i < 1000 do + val jsrc = reading.JsonSource(msgX) + jsrc.expectString() i += 1 - val t3 = System.nanoTime() - println("Time: " + (t3 - t2)) // 2,267,500 - - def parseInt(s: String): Int = { - var result = 0 - var sign = 1 - var i = 0 + val tq = System.nanoTime() + println("Time (ExpectNew): " + (tq - tr)) // 4927792 --> about a 52% improvement!!! - // Handle optional sign - if s.charAt(0) == '-' then { - sign = -1 - i += 1 - } else if s.charAt(0) == '+' then { - i += 1 - } + println("------") - // Parse digits - while i < s.length do { - val digit = s.charAt(i) - '0' + i = 0 + val tr2 = System.nanoTime() + while i < 1000 do + val jsrc = reading.JsonSource(msgX) + jsrc.expectStringOld() + i += 1 + val tq2 = System.nanoTime() + println("Time (ExpectOld): " + (tq2 - tr2)) // 4927792 --> about a 52% improvement!!! - if digit < 0 || digit > 9 then { - throw new NumberFormatException(s"Invalid character in integer: ${s.charAt(i)}") - } + println("------") - result = (result << 3) + (result << 1) + digit // equivalent to result * 10 + digit + i = 0 + val te2 = System.nanoTime() + while i < 1000 do + val jsrc = reading.JsonSource(msg) + val pc = jsrc.parseString(0) + jsrc.js.subSequence(0, pc) + i += 1 + val tf2 = System.nanoTime() + println("Time (PNew) : " + (tf2 - te2)) // 4927792 --> about a 52% improvement!!! - i += 1 - } + println("------") - sign * result - } + i = 0 + val te = System.nanoTime() + val msgY = "\"" + msg + while i < 1000 do + val jsrc = reading.JsonSource(msgY) + jsrc.parseStringOld() + i += 1 + val tf = System.nanoTime() + println("Time (POld) : " + (tf - te)) // 4927792 --> about a 52% improvement!!! println("------") - */ - /* - var i = 0 - val msg = """This is a test"""" - val cbuf = new Array[Char](4048) - val ps = reading.ParseString(msg.getBytes) - val ta = System.nanoTime() + i = 0 + val ty = System.nanoTime() while i < 1000 do - val z = ps.parseString(0, msg.length(), cbuf, 0) - String(cbuf.take(z)) + blah.fromJson(jsData) i += 1 - val tb = System.nanoTime() - println("Time: " + (tb - ta)) // 3235208 + val tz = System.nanoTime() + println("Time (ParseAll) : " + (tz - ty)) + // Orig: 79298041 + // New : 58537667 + // Latest: 79713209 + // Newnew: 34919833 println("------") i = 0 - val src = reading.JsonSource(""""This is a test"""") - val tc = System.nanoTime() + val ty2 = System.nanoTime() while i < 1000 do - src.expectString() - src.i = 0 + val g = reading.JsonSource("true}") + g.expectBoolean() i += 1 - val td = System.nanoTime() - println("Time: " + (td - tc)) // 4927792 --> about a 52% improvement!!! + val tz2 = System.nanoTime() + println("Time (Boolean Old) : " + (tz2 - ty2)) */ - - val msg = """This is a test"Another test"""" - val max = msg.length() - println("max: " + max) - val cbufLen = 4048 - val cbuf = new Array[Char](cbufLen) - val ps = reading.ParseString(msg.getBytes) - var pos = 0 - val parsedCount = ps.parseString(0, max - pos, cbuf, pos) - println("Parsed: " + parsedCount) - println("cbuf: " + new String(cbuf, 0, parsedCount)) - pos = parsedCount + 1 - - println("HERE: " + msg.charAt(parsedCount)) - val pc2 = ps.parseString(0, max - pos, cbuf, 15) - println("2: " + pc2 + " : " + new String(cbuf, 0, pc2)) - - // import com.github.plokhotnyuk.jsoniter_scala.core.JsonReader as Wow - - // val jsr = new Wow(msg.getBytes) - // val a = jsr.parseString(0, cbufLen, cbuf, 0) - // println("A: " + new String(cbuf, 0, a)) - - // val b = jsr.parseString(0, cbufLen, cbuf, a + 1) - // println("B: " + new String(cbuf, 0, b)) From 54f0b4363deb613fd1ce83bfd78fe85bb9c18e6c Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Sat, 2 Mar 2024 21:38:51 -0600 Subject: [PATCH 44/65] Another massive performance improvement --- TODO.txt | 18 ++- benchmark/build.sbt | 8 +- benchmark/foo | 98 ++++++++++++++ .../src/main/scala/co.blocke/Benchmark.scala | 2 +- .../json/JsonCodecMaker.scala | 123 +++++++++++++----- .../json/reading/JsonSource.scala | 49 ++----- 6 files changed, 227 insertions(+), 71 deletions(-) create mode 100644 benchmark/foo diff --git a/TODO.txt b/TODO.txt index 0eb3ad77..5b0ade24 100644 --- a/TODO.txt +++ b/TODO.txt @@ -43,4 +43,20 @@ Path to Performance [*] [W] - @Change - field name change [ ] -- Streaming JSON write support - [ ] -- BigJSON support (eg. multi-gig file) \ No newline at end of file + [ ] -- BigJSON support (eg. multi-gig file) + + + + [ ] -- Handle required fields, incl generating mapping fns for err: + def f0(i: scala.Int): java.lang.String = i match { + case 0 => + "person" + case 1 => + "hobbies" + case 2 => + "friends" + case 3 => + "pets" + } + [ ] -- Handle special chars in strings (Json parsing) + \ No newline at end of file diff --git a/benchmark/build.sbt b/benchmark/build.sbt index 4a0aac27..d70a6107 100644 --- a/benchmark/build.sbt +++ b/benchmark/build.sbt @@ -44,10 +44,10 @@ lazy val benchmark = project "org.typelevel" %% "fabric-io" % "1.12.6", "org.typelevel" %% "jawn-parser" % "1.3.2", "org.typelevel" %% "jawn-ast" % "1.3.2", - // "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core" % "2.24.5-SNAPSHOT", - // "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-macros" % "2.24.5-SNAPSHOT" % "compile-internal", - "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core" % "2.24.4", - "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-macros" % "2.24.4" % "compile-internal", + "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core" % "2.24.5-SNAPSHOT", + "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-macros" % "2.24.5-SNAPSHOT" % "compile-internal", + // "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core" % "2.24.4", + // "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-macros" % "2.24.4" % "compile-internal", // "io.circe" %% "circe-derivation" % "0.15.0-M1", // "io.circe" %% "circe-jackson29" % "0.14.0", // "org.json4s" %% "json4s-jackson" % "4.0.4", diff --git a/benchmark/foo b/benchmark/foo new file mode 100644 index 00000000..66e44bea --- /dev/null +++ b/benchmark/foo @@ -0,0 +1,98 @@ +SCALAJACK: + + def r1(`in₁₀`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.Person = { + var _name: java.lang.String = "" + var _age: scala.Int = 0 + var _address: co.blocke.Address = null + var _email: java.lang.String = "" + var _phone_numbers: scala.collection.immutable.List[scala.Predef.String] = null + var _is_employed: scala.Boolean = false + var `maybeFname₂`: scala.Option[java.lang.CharSequence] = `in₁₀`.expectFirstObjectField() + if (`maybeFname₂`.==(null)) null.asInstanceOf[co.blocke.Person] else { + while (`maybeFname₂`.isDefined) { + `maybeFname₂`.get match { + case "name" => + _name = `in₁₀`.expectString().toString() + case "age" => + _age = `in₁₀`.expectInt() + case "address" => + _address = r2(`in₁₀`) + case "email" => + _email = `in₁₀`.expectString().toString() + case "phone_numbers" => + _phone_numbers = { + val parsedArray: scala.collection.mutable.ListBuffer[java.lang.String] = `in₁₀`.expectArray[java.lang.String]((() => `in₁₀`.expectString().toString()) + if (parsedArray.==(null)) null.asInstanceOf[scala.collection.immutable.List[scala.Predef.String]] else parsedArray.to[scala.collection.immutable.List[scala.Predef.String]](scala.collection.immutable.List.iterableFactory[java.lang.String]) + } + case "is_employed" => + _is_employed = `in₁₀`.expectBoolean() + case _ => + `in₁₀`.skipValue() + } + `maybeFname₂` = `in₁₀`.expectObjectField() + } + new co.blocke.Person(_name, _age, _address, _email, _phone_numbers, _is_employed) + } + } + + +JSONITER: + + def d6(`in`: com.github.plokhotnyuk.jsoniter_scala.core.JsonReader, `default₇`: co.blocke.Person): co.blocke.Person = if (`in`.isNextToken(123)) { + var `_name₃`: java.lang.String = (null: java.lang.String) + var `_age₃`: scala.Int = 0 + var _address: co.blocke.Address = null + var `_email₂`: java.lang.String = (null: java.lang.String) + var _phone_numbers: scala.collection.immutable.List[scala.Predef.String] = scala.Nil + var _is_employed: scala.Boolean = false + var `p0₄`: scala.Int = 63 + if (!in.isNextToken(125)) { + in.rollbackToken() + var `l₄`: scala.Int = -1 + while (l₄ < 0 || in.isNextToken(44)) { + `l₄` = `in`.readKeyAsCharBuf() + if (`in`.isCharBufEqualsTo(`l₄`, "name")) { + if (`p0₄`.&(1).!=(0)) `p0₄` = `p0₄`.^(1) else `in`.duplicatedKeyError(`l₄`) + `_name₃` = `in`.readString(`_name₃`) + } else if (`in`.isCharBufEqualsTo(`l₄`, "age")) { + if (`p0₄`.&(2).!=(0)) `p0₄` = `p0₄`.^(2) else `in`.duplicatedKeyError(`l₄`) + `_age₃` = `in`.readInt() + } else if (`in`.isCharBufEqualsTo(`l₄`, "address")) { + if (`p0₄`.&(4).!=(0)) `p0₄` = `p0₄`.^(4) else `in`.duplicatedKeyError(`l₄`) + _address = d7(`in`, _address) + } else if (`in`.isCharBufEqualsTo(`l₄`, "email")) { + if (`p0₄`.&(8).!=(0)) `p0₄` = `p0₄`.^(8) else `in`.duplicatedKeyError(`l₄`) + `_email₂` = `in`.readString(`_email₂`) + } else if (`in`.isCharBufEqualsTo(`l₄`, "phone_numbers")) { + if (`p0₄`.&(16).!=(0)) `p0₄` = `p0₄`.^(16) else `in`.duplicatedKeyError(`l₄`) + _phone_numbers = d5(`in`, _phone_numbers) + } else if (`in`.isCharBufEqualsTo(`l₄`, "is_employed")) { + if (`p0₄`.&(32).!=(0)) `p0₄` = `p0₄`.^(32) else `in`.duplicatedKeyError(`l₄`) + _is_employed = `in`.readBoolean() + } else `in`.skip() + } + if (!in.isCurrentToken(125)) in.objectEndOrCommaError() else () + } else () + if (`p0₄`.&(47).!=(0)) `in`.requiredFieldError(f3(java.lang.Integer.numberOfTrailingZeros(`p0₄`.&(47)))) else () + new co.blocke.Person(`_name₃`, `_age₃`, _address, `_email₂`, _phone_numbers, _is_employed) + } else `in`.readNullOrTokenError[co.blocke.Person](`default₇`, 123) + + // List reading thingy... Note, no Seq-type coersion happening... + def d5(in: JsonReader, default: List[String]): List[String] = + if (in.isNextToken(91)) + if (in.isNextToken(93)) + default + else { + in.rollbackToken() + val x: scala.collection.mutable.ListBuffer[String] = new scala.collection.mutable.ListBuffer[String]() + while ({ + x.addOne(in.readString((null: String))) + in.isNextToken(44) + }) () + if (in.isCurrentToken(93)) + x.toList + else + in.arrayEndOrCommaError() + } + else + in.readNullOrTokenError[List[String]](default, 91) diff --git a/benchmark/src/main/scala/co.blocke/Benchmark.scala b/benchmark/src/main/scala/co.blocke/Benchmark.scala index 7531bac8..0c8788fd 100644 --- a/benchmark/src/main/scala/co.blocke/Benchmark.scala +++ b/benchmark/src/main/scala/co.blocke/Benchmark.scala @@ -45,7 +45,7 @@ trait HandTooledWritingBenchmark { class ReadingBenchmark // extends CirceZ.CirceReadingBenchmark extends ScalaJackZ.ScalaJackReadingBenchmark - //with JsoniterZ.JsoniterReadingBenchmark + with JsoniterZ.JsoniterReadingBenchmark // with ZIOZ.ZIOJsonReadingBenchmark // with PlayZ.PlayReadingBenchmark // with ArgonautZ.ArgonautReadingBenchmark diff --git a/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala b/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala index 739d420e..ebf552a1 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala @@ -9,6 +9,7 @@ import co.blocke.scala_reflection.rtypes.{EnumRType, JavaClassRType, NonConstruc import reading.JsonSource import scala.jdk.CollectionConverters.* import scala.quoted.* +import scala.annotation.switch import scala.collection.Factory import dotty.tools.dotc.ast.Trees.EmptyTree import org.apache.commons.text.StringEscapeUtils @@ -870,13 +871,22 @@ object JsonCodecMaker: val instantiateClass = argss.tail.foldLeft(Apply(constructor, argss.head))((acc, args) => Apply(acc, args)) val parseLoop = '{ - var maybeFname = $in.expectFirstObjectField() - if maybeFname == null then null.asInstanceOf[T] + if ! $in.expectObjectOrNull() then null.asInstanceOf[T] else - while maybeFname.isDefined do - ${ Match('{ maybeFname.get }.asTerm, caseDefsWithFinal).asExprOf[Any] } - maybeFname = $in.expectObjectField() - ${ instantiateClass.asExprOf[T] } + var c = $in.readCharWS() + while c match { + case '"' => + ${ Match('{ $in.expectObjectField() }.asTerm, caseDefsWithFinal).asExprOf[Any] } + c = $in.readCharWS() + if c == ',' then + c = $in.readCharWS() + true + else true + case '}' => false + case c => throw new JsonParseError(s"Expected '\"' or '}' here but found '$c'", $in) + } + do () + ${ instantiateClass.asExprOf[T] } }.asTerm Block(varDefs, parseLoop).asExprOf[T] @@ -937,32 +947,85 @@ object JsonCodecMaker: // if cfg.escapedStrings then '{ $out.value(StringEscapeUtils.escapeJson(${ aE.asExprOf[String] })) } // else '{ $out.value(${ aE.asExprOf[String] }) } - case _ => + // -------------------- + // Collections... + // -------------------- + case t: SeqRef[?] => ref.refType match - case '[b] => - ref match - case t: SeqRef[?] => - t.elementRef.refType match - case '[e] => - val rtypeRef = t.elementRef.asInstanceOf[RTypeRef[e]] - '{ - val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in) }) - if parsedArray == null then null.asInstanceOf[T] - else parsedArray.to(${ Expr.summon[Factory[e, T]].get }) // create appropriate flavor of Seq[T] here - // if ! $in.expectArrayStart() then null.asInstanceOf[T] - // else if $in.here == ']' then // empty Seq - // $in.readChar() // skip the ']' - // List.empty[e].to(${ Expr.summon[Factory[e, T]].get }) // create appropriate Seq[T] here - // else - // val acc = scala.collection.mutable.ListBuffer.empty[e] - // acc.addOne(${ genReadVal[e](t.elementRef.asInstanceOf[RTypeRef[e]], in) }) - // while $in.nextArrayElement() do acc.addOne(${ genReadVal[e](t.elementRef.asInstanceOf[RTypeRef[e]], in) }) - // acc.to(${ Expr.summon[Factory[e, T]].get }) // create appropriate Seq[T] here - } + case '[List[?]] => + t.elementRef.refType match + case '[e] => + val rtypeRef = t.elementRef.asInstanceOf[RTypeRef[e]] + '{ + val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in) }) + if parsedArray == null then null + else parsedArray.toList + }.asExprOf[T] + case '[Vector[?]] => + t.elementRef.refType match + case '[e] => + val rtypeRef = t.elementRef.asInstanceOf[RTypeRef[e]] + '{ + val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in) }) + if parsedArray == null then null + else parsedArray.toVector + }.asExprOf[T] + case '[Seq[?]] => + t.elementRef.refType match + case '[e] => + val rtypeRef = t.elementRef.asInstanceOf[RTypeRef[e]] + '{ + val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in) }) + if parsedArray == null then null + else parsedArray.toSeq + }.asExprOf[T] + case '[IndexedSeq[?]] => + t.elementRef.refType match + case '[e] => + val rtypeRef = t.elementRef.asInstanceOf[RTypeRef[e]] + '{ + val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in) }) + if parsedArray == null then null + else parsedArray.toIndexedSeq + }.asExprOf[T] + case '[Iterable[?]] => + t.elementRef.refType match + case '[e] => + val rtypeRef = t.elementRef.asInstanceOf[RTypeRef[e]] + '{ + val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in) }) + if parsedArray == null then null + else parsedArray.toIterable + }.asExprOf[T] + // Catch all, with (slightly) slower type coersion to proper Seq flavor + case _ => + t.elementRef.refType match + case '[e] => + val rtypeRef = t.elementRef.asInstanceOf[RTypeRef[e]] + '{ + val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in) }) + if parsedArray == null then null + else parsedArray.to(${ Expr.summon[Factory[e, T]].get }) // create appropriate flavor of Seq[T] here + }.asExprOf[T] + + case t: ArrayRef[?] => + t.elementRef.refType match + case '[e] => + val rtypeRef = t.elementRef.asInstanceOf[RTypeRef[e]] + val ct = Expr.summon[ClassTag[e]].get + '{ + val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in) }) + if parsedArray == null then null + else + implicit val ctt = $ct + parsedArray.toArray[e] // (${ Expr.summon[Factory[e, T]].get }) // create appropriate flavor of Seq[T] here + }.asExprOf[T] + + case _ => + // Classes, traits, etc. + genDecFnBody[T](ref, in) // Create a new decoder function (presumably for class, trait, etc) + genReadVal(ref, in) - case _ => - genDecFnBody[T](ref, in) // Create a new decoder function (presumably for class, trait, etc) - genReadVal(ref, in) // Just-created function is present now and will be called ) diff --git a/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala b/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala index d6a0b2d6..c07d1b51 100644 --- a/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala +++ b/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala @@ -2,8 +2,7 @@ package co.blocke.scalajack package json package reading -import scala.annotation.* -import scala.annotation.tailrec +import scala.annotation.{switch, tailrec} object JsonSource: protected val ull: Array[Char] = "ull".toCharArray @@ -24,6 +23,8 @@ case class JsonSource(js: CharSequence): // inline def here = js(i).toChar inline def here = js.charAt(i) + inline def revert = i -= 1 + private var c: Char = 0 inline def readChar(): Char = if i < max then @@ -84,44 +85,21 @@ case class JsonSource(js: CharSequence): i += 1 accum.toChar -//------- - - // returns false if 'null' found - def expectFirstObjectField(): Option[CharSequence] = + inline def expectObjectOrNull(): Boolean = // false => null readCharWS() match { - case '{' => - readCharWS() match { - case '"' => - val endI = parseString(i) - val fname = js.subSequence(i, endI) - i = endI + 1 - if readCharWS() != ':' then throw new JsonParseError(s"Expected ':' field separator", this) - Some(fname) - case '}' => None // end-of-object (empty, not null) - case c => throw new JsonParseError(s"Expected object field name or '}' but found '$c'", this) - } + case '{' => true case 'n' => readChars(JsonSource.ull, "null") - null - case c => throw new JsonParseError(s"Expected object start '{' but found '$c'", this) + false + case _ => throw new JsonParseError(s"Expected object start '{' or null", this) } - def expectObjectField(): Option[CharSequence] = - readCharWS() match { - case ',' => - readCharWS() match { - case '"' => - val endI = parseString(i) - val fname = js.subSequence(i, endI) - i = endI + 1 - if readCharWS() != ':' then throw new JsonParseError(s"Expected ':' field separator", this) - Some(fname) - case c => throw new JsonParseError(s"Expected object field name but found '$c'", this) - } - case '}' => None // end-of-object - case c => - throw new JsonParseError(s"Expected ',' or '}' but found '$c'", this) - } + inline def expectObjectField(): CharSequence = + val endI = parseString(i) + val fname = js.subSequence(i, endI) + i = endI + 1 + if readCharWS() != ':' then throw new JsonParseError(s"Expected ':' separator token", this) + fname def expectArray[E](f: () => E): scala.collection.mutable.ListBuffer[E] = (readCharWS(): @switch) match @@ -195,6 +173,7 @@ case class JsonSource(js: CharSequence): } // Value might be null! + // expectString() will look for leading '"'. parseString() presumes the '"' has already been consumed. inline def expectString(): CharSequence = readCharWS() match { case '"' => From b08f70f50078648bc85fe449e98e3b54a1a90af3 Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Sun, 3 Mar 2024 16:31:20 -0600 Subject: [PATCH 45/65] checkpoint --- TODO.txt | 12 +- benchmark/build.sbt | 2 +- .../src/main/scala/co.blocke/Benchmark.scala | 33 +- .../co.blocke.scalajack/json/JsonCodec.scala | 4 - .../json/JsonCodecMaker.scala | 65 ++-- .../scala/co.blocke.scalajack/run/Foo.scalax | 351 ------------------ 6 files changed, 41 insertions(+), 426 deletions(-) delete mode 100644 src/main/scala/co.blocke.scalajack/run/Foo.scalax diff --git a/TODO.txt b/TODO.txt index 5b0ade24..7a754c6a 100644 --- a/TODO.txt +++ b/TODO.txt @@ -47,16 +47,6 @@ Path to Performance - [ ] -- Handle required fields, incl generating mapping fns for err: - def f0(i: scala.Int): java.lang.String = i match { - case 0 => - "person" - case 1 => - "hobbies" - case 2 => - "friends" - case 3 => - "pets" - } + [*] -- Handle required fields, incl generating mapping fns for err: [ ] -- Handle special chars in strings (Json parsing) \ No newline at end of file diff --git a/benchmark/build.sbt b/benchmark/build.sbt index d70a6107..18f05eb3 100644 --- a/benchmark/build.sbt +++ b/benchmark/build.sbt @@ -37,7 +37,7 @@ lazy val benchmark = project libraryDependencies ++= Seq( "org.playframework" %% "play-json" % "3.0.1", "io.argonaut" %% "argonaut" % "6.3.9", - "co.blocke" %% "scalajack" % "0ca9f0_unknown", + "co.blocke" %% "scalajack" % "54f0b4_unknown", "co.blocke" %% "scala-reflection" % "sj_fixes_f43af7", "dev.zio" %% "zio-json" % "0.6.1", "org.typelevel" %% "fabric-core" % "1.12.6", diff --git a/benchmark/src/main/scala/co.blocke/Benchmark.scala b/benchmark/src/main/scala/co.blocke/Benchmark.scala index 0c8788fd..c3eac501 100644 --- a/benchmark/src/main/scala/co.blocke/Benchmark.scala +++ b/benchmark/src/main/scala/co.blocke/Benchmark.scala @@ -43,9 +43,9 @@ trait HandTooledWritingBenchmark { @BenchmarkMode(Array(Mode.Throughput)) @OutputTimeUnit(TimeUnit.SECONDS) class ReadingBenchmark - // extends CirceZ.CirceReadingBenchmark extends ScalaJackZ.ScalaJackReadingBenchmark - with JsoniterZ.JsoniterReadingBenchmark + // with CirceZ.CirceReadingBenchmark + // with JsoniterZ.JsoniterReadingBenchmark // with ZIOZ.ZIOJsonReadingBenchmark // with PlayZ.PlayReadingBenchmark // with ArgonautZ.ArgonautReadingBenchmark @@ -62,30 +62,17 @@ class WritingBenchmark // with PlayZ.PlayWritingBenchmark // with ArgonautZ.ArgonautWritingBenchmark -// "Old-New" ScalaJack -// [info] Benchmark Mode Cnt Score Error Units -// [info] ReadingBenchmark.readRecordScalaJack thrpt 20 30113.982 ± 97.701 ops/s -// [info] New-New ScalaJack thrpt 20 50908.982 ± 97.701 ops/s - -// ScalaJack w/ZIO-based parser 635977.008 -// ZIO (Fast!!) 568123.000 <-- How do they do this?! More than 2x faster than everyone else! -// Circe 279231.646 -// Play 209756.408 - -// Jawn (parse only + AST) 336384.617 -// ScalaJack JsonParser3 (parse only + AST) 279456.523 -// Fabric (new!) (parse only + AST) 270706.567 /* LATEST RUN: -[info] Benchmark Mode Cnt Score Error Units -[info] ReadingBenchmark.readRecordArgonaut thrpt 20 177341.827 ± 7090.749 ops/s -[info] ReadingBenchmark.readRecordCirce thrpt 20 289018.255 ± 1797.188 ops/s -[info] ReadingBenchmark.readRecordJsoniter thrpt 20 1313108.400 ± 17657.823 ops/s -[info] ReadingBenchmark.readRecordPlay thrpt 20 201570.409 ± 3736.467 ops/s -[info] ReadingBenchmark.readRecordScalaJack thrpt 20 501950.975 ± 5631.196 ops/s -[info] ReadingBenchmark.readRecordZIOJson thrpt 20 587611.803 ± 4701.410 ops/s -[info] WritingBenchmark.writeRecordScalaJack thrpt 20 352825.894 ± 7772.927 ops/s +[info] Benchmark Mode Cnt Score Error Units +[info] ReadingBenchmark.readRecordArgonaut thrpt 20 185186.226 ± 16189.626 ops/s +[info] ReadingBenchmark.readRecordCirce thrpt 20 281382.258 ± 4449.888 ops/s +[info] ReadingBenchmark.readRecordJsoniter thrpt 20 1327809.608 ± 26901.514 ops/s +[info] ReadingBenchmark.readRecordPlay thrpt 20 209035.195 ± 671.300 ops/s +[info] ReadingBenchmark.readRecordScalaJack thrpt 20 740350.521 ± 190132.195 ops/s +[info] ReadingBenchmark.readRecordZIOJson thrpt 20 584964.240 ± 552.648 ops/s +[info] WritingBenchmark.writeRecordScalaJack thrpt 20 352825.894 ± 7772.927 ops/s */ diff --git a/src/main/scala/co.blocke.scalajack/json/JsonCodec.scala b/src/main/scala/co.blocke.scalajack/json/JsonCodec.scala index 200ccc45..fd42dd6d 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonCodec.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonCodec.scala @@ -5,10 +5,6 @@ import writing.* import reading.* trait JsonCodec[A] { - - // def decodeValue(in: JsonReader, default: A): A = - // ${ genReadVal(rootTpe :: Nil, 'default, cfg.isStringified, false, 'in) } - def encodeValue(in: A, out: JsonOutput): Unit def decodeValue(in: JsonSource): A } diff --git a/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala b/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala index ebf552a1..7f841519 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala @@ -9,6 +9,7 @@ import co.blocke.scala_reflection.rtypes.{EnumRType, JavaClassRType, NonConstruc import reading.JsonSource import scala.jdk.CollectionConverters.* import scala.quoted.* +import scala.reflect.ClassTag import scala.annotation.switch import scala.collection.Factory import dotty.tools.dotc.ast.Trees.EmptyTree @@ -80,31 +81,6 @@ object JsonCodecMaker: ) '{} - // Apply( - // Ref( - // readMethodSyms.getOrElse( - // methodKey, { - // val sym = Symbol.newMethod( - // Symbol.spliceOwner, - // "r" + readMethodSyms.size, - // MethodType(List("in"))(_ => List(TypeRepr.of[JsonSource]), _ => TypeRepr.of[U]) - // // (_ => List(input_params,...), _ => resultType) - // ) - // readMethodSyms.update(methodKey, sym) - // readMethodDefs += DefDef( - // sym, - // params => { - // val List(List(in)) = params - // Some(f(in.asExprOf[JsonSource]).asTerm.changeOwner(sym)) - // } - // ) - // sym - // } - // ) - // ), - // List(in.asTerm) - // ).asExprOf[Unit] - // --------------------------------------------------------------------------------------------- def maybeWrite[T](label: String, aE: Expr[T], ref: RTypeRef[T], out: Expr[JsonOutput], cfg: JsonConfig): Expr[Unit] = @@ -809,12 +785,6 @@ object JsonCodecMaker: // --------------------------------------------------------------------------------------------- - // Little rif on Expr.ofList. I need an Expr[Array] here because in the runtime code we need to update the - // array (which is pre-loaded with default values) with the parsed field values for ultimate class instantiation. - import scala.reflect.ClassTag - def ofArray[T](xs: Seq[Expr[T]])(using Type[T])(using Quotes): Expr[Array[T]] = - '{ scala.Array.apply[T](${ quoted.Varargs(xs) }*)(${ Expr.summon[ClassTag[T]].get }) } - def genDecFnBody[T: Type](r: RTypeRef[?], in: Expr[JsonSource])(using Quotes): Expr[Unit] = import quotes.reflect.* @@ -833,18 +803,38 @@ object JsonCodecMaker: val tpe = TypeRepr.of[b] val classCompanion = tpe.typeSymbol.companionClass val companionModule = tpe.typeSymbol.companionModule + val totalRequired = math.pow(2, t.fields.length).toInt - 1 + var required = 0 + val reqSym = Symbol.newVal(Symbol.spliceOwner, "required", TypeRepr.of[Int], Flags.Mutable, Symbol.noSymbol) + val allFieldNames = Expr(t.fields.map(_.name).toArray) // Used for missing required field error val together = t.fields.map { oneField => oneField.fieldRef.refType match { case '[f] => val dvMembers = classCompanion.methodMember("$lessinit$greater$default$" + (oneField.index + 1)) val sym = Symbol.newVal(Symbol.spliceOwner, "_" + oneField.name, TypeRepr.of[f], Flags.Mutable, Symbol.noSymbol) val fieldSymRef = Ident(sym.termRef) + val reqBit = Expr(math.pow(2, oneField.index).toInt) + val fieldName = Expr(oneField.name) val caseDef = CaseDef( Literal(StringConstant(oneField.name)), None, - Assign(fieldSymRef, genReadVal[f](oneField.fieldRef.asInstanceOf[RTypeRef[f]], in).asTerm) + // Assign(fieldSymRef, genReadVal[f](oneField.fieldRef.asInstanceOf[RTypeRef[f]], in).asTerm) + '{ + if (${ Ref(reqSym).asExprOf[Int] } & $reqBit) != 0 then + ${ Assign(Ref(reqSym), '{ ${ Ref(reqSym).asExprOf[Int] } ^ $reqBit }.asTerm).asExprOf[Unit] } + ${ Assign(fieldSymRef, genReadVal[f](oneField.fieldRef.asInstanceOf[RTypeRef[f]], in).asTerm).asExprOf[Unit] } + else throw new JsonParseError("Duplicate field " + $fieldName, $in) + }.asTerm ) - if dvMembers.isEmpty then (ValDef(sym, Some(oneField.fieldRef.unitVal.asTerm)), caseDef, fieldSymRef) + // if dvMembers.isEmpty then (ValDef(sym, Some(oneField.fieldRef.unitVal.asTerm)), caseDef, fieldSymRef) + if dvMembers.isEmpty then + // no default... required? Not if Option/Optional, or a collection + oneField.fieldRef match { + case _: OptionRef[?] => // not required + case _: CollectionRef[?] => // not required + case _ => required = required | math.pow(2, oneField.index).toInt // required + } + (ValDef(sym, Some(oneField.fieldRef.unitVal.asTerm)), caseDef, fieldSymRef) else val methodSymbol = dvMembers.head val dvSelectNoTArgs = Ref(companionModule).select(methodSymbol) @@ -855,10 +845,10 @@ object JsonCodecMaker: case Nil => ??? // throw JsonParseError("Expected an applied type", ???) case typeArgs => TypeApply(dvSelectNoTArgs, typeArgs.map(Inferred(_))) case _ => ??? // fail(s"Default method for ${symbol.name} of class ${tpe.show} have a complex " + - // s"parameter list: ${methodSymbol.paramSymss}") (ValDef(sym, Some(dvSelect)), caseDef, fieldSymRef) } } + val reqVarDef = ValDef(reqSym, Some(Literal(IntConstant(totalRequired)))) val (varDefs, caseDefs, idents) = together.unzip3 val caseDefsWithFinal = caseDefs :+ CaseDef(Wildcard(), None, '{ $in.skipValue() }.asTerm) @@ -870,6 +860,7 @@ object JsonCodecMaker: case typeArgs => TypeApply(constructorNoTypes, typeArgs.map(Inferred(_))) val instantiateClass = argss.tail.foldLeft(Apply(constructor, argss.head))((acc, args) => Apply(acc, args)) + val exprRequired = Expr(required) val parseLoop = '{ if ! $in.expectObjectOrNull() then null.asInstanceOf[T] else @@ -886,10 +877,12 @@ object JsonCodecMaker: case c => throw new JsonParseError(s"Expected '\"' or '}' here but found '$c'", $in) } do () + if (${ Ref(reqSym).asExprOf[Int] } & ${ exprRequired }) != 0 then throw new JsonParseError("Missing required field(s) " + ${ allFieldNames }(Integer.numberOfTrailingZeros(${ Ref(reqSym).asExprOf[Int] } & ${ exprRequired })), $in) ${ instantiateClass.asExprOf[T] } }.asTerm - Block(varDefs, parseLoop).asExprOf[T] + // Block(varDefs, parseLoop).asExprOf[T] + Block(varDefs :+ reqVarDef, parseLoop).asExprOf[T] ) case _ => ??? @@ -959,7 +952,7 @@ object JsonCodecMaker: '{ val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in) }) if parsedArray == null then null - else parsedArray.toList + else parsedArray.toList }.asExprOf[T] case '[Vector[?]] => t.elementRef.refType match diff --git a/src/main/scala/co.blocke.scalajack/run/Foo.scalax b/src/main/scala/co.blocke.scalajack/run/Foo.scalax deleted file mode 100644 index 28413353..00000000 --- a/src/main/scala/co.blocke.scalajack/run/Foo.scalax +++ /dev/null @@ -1,351 +0,0 @@ -package `co.blocke.scalajack`.run - -Codec: { - - //------------------------- Don't care about these--they appear to map index->field name, which we do effectively with StringMatrix - - def f0(i: scala.Int): java.lang.String = i match { - case 0 => - "person" - case 1 => - "hobbies" - case 2 => - "friends" - case 3 => - "pets" - } - def f1(`i₂`: scala.Int): java.lang.String = `i₂` match { - case 0 => - "name" - case 1 => - "species" - case 2 => - "age" - } - def f2(`i₃`: scala.Int): java.lang.String = `i₃` match { - case 0 => - "name" - case 1 => - "age" - case 2 => - "email" - } - def f3(`i₄`: scala.Int): java.lang.String = `i₄` match { - case 0 => - "name" - case 1 => - "age" - case 2 => - "address" - case 3 => - "email" - case 4 => - "phone_numbers" - case 5 => - "is_employed" - } - def f4(`i₅`: scala.Int): java.lang.String = `i₅` match { - case 0 => - "street" - case 1 => - "city" - case 2 => - "state" - case 3 => - "postal_code" - } - - //------------------------- Read (decode) Pet - - def d2(in: com.github.plokhotnyuk.jsoniter_scala.core.JsonReader, default: co.blocke.Pet): co.blocke.Pet = if (in.isNextToken(123)) { - var _name: java.lang.String = (null: java.lang.String) // <-- Secret revealed! They create local vars for each field then "new" at the end... no Array[Any] - var _species: java.lang.String = (null: java.lang.String) - var _age: scala.Int = 0 - var p0: scala.Int = 7 // <-- This seems to be a bit field used to ensure all required fields are set - if (in.isNextToken(125).unary_!) { - in.rollbackToken() - var l: scala.Int = -1 - while (l.<(0).||(in.isNextToken(44))) { - l = in.readKeyAsCharBuf() - if (in.isCharBufEqualsTo(l, "name")) { - if (p0.&(1).!=(0)) p0 = p0.^(1) else in.duplicatedKeyError(l) - _name = in.readString(_name) - } else if (in.isCharBufEqualsTo(l, "species")) { - if (p0.&(2).!=(0)) p0 = p0.^(2) else in.duplicatedKeyError(l) - _species = in.readString(_species) - } else if (in.isCharBufEqualsTo(l, "age")) { - if (p0.&(4).!=(0)) p0 = p0.^(4) else in.duplicatedKeyError(l) - _age = in.readInt() - } else in.skip() - } - if (in.isCurrentToken(125).unary_!) in.objectEndOrCommaError() else () - } else () - // Here's where we check the bit field to ensure all fields were set. We change the value from 0 (all fields required) - // to some other Int representation of bitmap based on which fields were either optional or had pre-supplied default values. - if (p0.!=(0)) in.requiredFieldError(f1(java.lang.Integer.numberOfTrailingZeros(p0))) else () - new co.blocke.Pet(_name, _species, _age) - } else in.readNullOrTokenError[co.blocke.Pet](default, 123) - - //------------------------- List/Seq done this way... (note structure, not necessarily that it's a separate function) - - def d1(`in₂`: com.github.plokhotnyuk.jsoniter_scala.core.JsonReader, `default₂`: scala.collection.immutable.List[co.blocke.Pet]): scala.collection.immutable.List[co.blocke.Pet] = if (`in₂`.isNextToken(91)) if (`in₂`.isNextToken(93)) `default₂` else { - `in₂`.rollbackToken() - val x: scala.collection.mutable.ListBuffer[co.blocke.Pet] = new scala.collection.mutable.ListBuffer[co.blocke.Pet]() - while ({ - x.addOne(d2(`in₂`, null)) - `in₂`.isNextToken(44) - }) () - if (`in₂`.isCurrentToken(93)) x.toList else `in₂`.arrayEndOrCommaError() - } else `in₂`.readNullOrTokenError[scala.collection.immutable.List[co.blocke.Pet]](`default₂`, 91) - - //------------------------- - - def d4(`in₃`: com.github.plokhotnyuk.jsoniter_scala.core.JsonReader, `default₃`: co.blocke.Friend): co.blocke.Friend = if (`in₃`.isNextToken(123)) { - var `_name₂`: java.lang.String = (null: java.lang.String) - var `_age₂`: scala.Int = 0 - var _email: java.lang.String = (null: java.lang.String) - var `p0₂`: scala.Int = 7 - if (`in₃`.isNextToken(125).unary_!) { - `in₃`.rollbackToken() - var `l₂`: scala.Int = -1 - while (`l₂`.<(0).||(`in₃`.isNextToken(44))) { - `l₂` = `in₃`.readKeyAsCharBuf() - if (`in₃`.isCharBufEqualsTo(`l₂`, "name")) { - if (`p0₂`.&(1).!=(0)) `p0₂` = `p0₂`.^(1) else `in₃`.duplicatedKeyError(`l₂`) - `_name₂` = `in₃`.readString(`_name₂`) - } else if (`in₃`.isCharBufEqualsTo(`l₂`, "age")) { - if (`p0₂`.&(2).!=(0)) `p0₂` = `p0₂`.^(2) else `in₃`.duplicatedKeyError(`l₂`) - `_age₂` = `in₃`.readInt() - } else if (`in₃`.isCharBufEqualsTo(`l₂`, "email")) { - if (`p0₂`.&(4).!=(0)) `p0₂` = `p0₂`.^(4) else `in₃`.duplicatedKeyError(`l₂`) - _email = `in₃`.readString(_email) - } else `in₃`.skip() - } - if (`in₃`.isCurrentToken(125).unary_!) `in₃`.objectEndOrCommaError() else () - } else () - if (`p0₂`.!=(0)) `in₃`.requiredFieldError(f2(java.lang.Integer.numberOfTrailingZeros(`p0₂`))) else () - new co.blocke.Friend(`_name₂`, `_age₂`, _email) - } else `in₃`.readNullOrTokenError[co.blocke.Friend](`default₃`, 123) - def d3(`in₄`: com.github.plokhotnyuk.jsoniter_scala.core.JsonReader, `default₄`: scala.collection.immutable.List[co.blocke.Friend]): scala.collection.immutable.List[co.blocke.Friend] = if (`in₄`.isNextToken(91)) if (`in₄`.isNextToken(93)) `default₄` else { - `in₄`.rollbackToken() - val `x₂`: scala.collection.mutable.ListBuffer[co.blocke.Friend] = new scala.collection.mutable.ListBuffer[co.blocke.Friend]() - while ({ - `x₂`.addOne(d4(`in₄`, null)) - `in₄`.isNextToken(44) - }) () - if (`in₄`.isCurrentToken(93)) `x₂`.toList else `in₄`.arrayEndOrCommaError() - } else `in₄`.readNullOrTokenError[scala.collection.immutable.List[co.blocke.Friend]](`default₄`, 91) - def d5(`in₅`: com.github.plokhotnyuk.jsoniter_scala.core.JsonReader, `default₅`: scala.collection.immutable.List[scala.Predef.String]): scala.collection.immutable.List[scala.Predef.String] = if (`in₅`.isNextToken(91)) if (`in₅`.isNextToken(93)) `default₅` else { - `in₅`.rollbackToken() - val `x₃`: scala.collection.mutable.ListBuffer[java.lang.String] = new scala.collection.mutable.ListBuffer[java.lang.String]() - while ({ - `x₃`.addOne(`in₅`.readString((null: java.lang.String))) - `in₅`.isNextToken(44) - }) () - if (`in₅`.isCurrentToken(93)) `x₃`.toList else `in₅`.arrayEndOrCommaError() - } else `in₅`.readNullOrTokenError[scala.collection.immutable.List[java.lang.String]](`default₅`, 91) - def d7(`in₆`: com.github.plokhotnyuk.jsoniter_scala.core.JsonReader, `default₆`: co.blocke.Address): co.blocke.Address = if (`in₆`.isNextToken(123)) { - var _street: java.lang.String = (null: java.lang.String) - var _city: java.lang.String = (null: java.lang.String) - var _state: java.lang.String = (null: java.lang.String) - var _postal_code: java.lang.String = (null: java.lang.String) - var `p0₃`: scala.Int = 15 - if (`in₆`.isNextToken(125).unary_!) { - `in₆`.rollbackToken() - var `l₃`: scala.Int = -1 - while (`l₃`.<(0).||(`in₆`.isNextToken(44))) { - `l₃` = `in₆`.readKeyAsCharBuf() - if (`in₆`.isCharBufEqualsTo(`l₃`, "street")) { - if (`p0₃`.&(1).!=(0)) `p0₃` = `p0₃`.^(1) else `in₆`.duplicatedKeyError(`l₃`) - _street = `in₆`.readString(_street) - } else if (`in₆`.isCharBufEqualsTo(`l₃`, "city")) { - if (`p0₃`.&(2).!=(0)) `p0₃` = `p0₃`.^(2) else `in₆`.duplicatedKeyError(`l₃`) - _city = `in₆`.readString(_city) - } else if (`in₆`.isCharBufEqualsTo(`l₃`, "state")) { - if (`p0₃`.&(4).!=(0)) `p0₃` = `p0₃`.^(4) else `in₆`.duplicatedKeyError(`l₃`) - _state = `in₆`.readString(_state) - } else if (`in₆`.isCharBufEqualsTo(`l₃`, "postal_code")) { - if (`p0₃`.&(8).!=(0)) `p0₃` = `p0₃`.^(8) else `in₆`.duplicatedKeyError(`l₃`) - _postal_code = `in₆`.readString(_postal_code) - } else `in₆`.skip() - } - if (`in₆`.isCurrentToken(125).unary_!) `in₆`.objectEndOrCommaError() else () - } else () - if (`p0₃`.!=(0)) `in₆`.requiredFieldError(f4(java.lang.Integer.numberOfTrailingZeros(`p0₃`))) else () - new co.blocke.Address(_street, _city, _state, _postal_code) - } else `in₆`.readNullOrTokenError[co.blocke.Address](`default₆`, 123) - def d6(`in₇`: com.github.plokhotnyuk.jsoniter_scala.core.JsonReader, `default₇`: co.blocke.Person): co.blocke.Person = if (`in₇`.isNextToken(123)) { - var `_name₃`: java.lang.String = (null: java.lang.String) - var `_age₃`: scala.Int = 0 - var _address: co.blocke.Address = null - var `_email₂`: java.lang.String = (null: java.lang.String) - var _phone_numbers: scala.collection.immutable.List[scala.Predef.String] = scala.Nil - var _is_employed: scala.Boolean = false - var `p0₄`: scala.Int = 63 - if (`in₇`.isNextToken(125).unary_!) { - `in₇`.rollbackToken() - var `l₄`: scala.Int = -1 - while (`l₄`.<(0).||(`in₇`.isNextToken(44))) { - `l₄` = `in₇`.readKeyAsCharBuf() - if (`in₇`.isCharBufEqualsTo(`l₄`, "name")) { - if (`p0₄`.&(1).!=(0)) `p0₄` = `p0₄`.^(1) else `in₇`.duplicatedKeyError(`l₄`) - `_name₃` = `in₇`.readString(`_name₃`) - } else if (`in₇`.isCharBufEqualsTo(`l₄`, "age")) { - if (`p0₄`.&(2).!=(0)) `p0₄` = `p0₄`.^(2) else `in₇`.duplicatedKeyError(`l₄`) - `_age₃` = `in₇`.readInt() - } else if (`in₇`.isCharBufEqualsTo(`l₄`, "address")) { - if (`p0₄`.&(4).!=(0)) `p0₄` = `p0₄`.^(4) else `in₇`.duplicatedKeyError(`l₄`) - _address = d7(`in₇`, _address) - } else if (`in₇`.isCharBufEqualsTo(`l₄`, "email")) { - if (`p0₄`.&(8).!=(0)) `p0₄` = `p0₄`.^(8) else `in₇`.duplicatedKeyError(`l₄`) - `_email₂` = `in₇`.readString(`_email₂`) - } else if (`in₇`.isCharBufEqualsTo(`l₄`, "phone_numbers")) { - if (`p0₄`.&(16).!=(0)) `p0₄` = `p0₄`.^(16) else `in₇`.duplicatedKeyError(`l₄`) - _phone_numbers = d5(`in₇`, _phone_numbers) - } else if (`in₇`.isCharBufEqualsTo(`l₄`, "is_employed")) { - if (`p0₄`.&(32).!=(0)) `p0₄` = `p0₄`.^(32) else `in₇`.duplicatedKeyError(`l₄`) - _is_employed = `in₇`.readBoolean() - } else `in₇`.skip() - } - if (`in₇`.isCurrentToken(125).unary_!) `in₇`.objectEndOrCommaError() else () - } else () - if (`p0₄`.&(47).!=(0)) `in₇`.requiredFieldError(f3(java.lang.Integer.numberOfTrailingZeros(`p0₄`.&(47)))) else () - new co.blocke.Person(`_name₃`, `_age₃`, _address, `_email₂`, _phone_numbers, _is_employed) - } else `in₇`.readNullOrTokenError[co.blocke.Person](`default₇`, 123) - def d0(`in₈`: com.github.plokhotnyuk.jsoniter_scala.core.JsonReader, `default₈`: co.blocke.Record): co.blocke.Record = if (`in₈`.isNextToken(123)) { - var _person: co.blocke.Person = null - var _hobbies: scala.collection.immutable.List[scala.Predef.String] = scala.Nil - var _friends: scala.collection.immutable.List[co.blocke.Friend] = scala.Nil - var _pets: scala.collection.immutable.List[co.blocke.Pet] = scala.Nil - var `p0₅`: scala.Int = 15 - if (`in₈`.isNextToken(125).unary_!) { - `in₈`.rollbackToken() - var `l₅`: scala.Int = -1 - while (`l₅`.<(0).||(`in₈`.isNextToken(44))) { - `l₅` = `in₈`.readKeyAsCharBuf() - if (`in₈`.isCharBufEqualsTo(`l₅`, "person")) { - if (`p0₅`.&(1).!=(0)) `p0₅` = `p0₅`.^(1) else `in₈`.duplicatedKeyError(`l₅`) - _person = d6(`in₈`, _person) - } else if (`in₈`.isCharBufEqualsTo(`l₅`, "hobbies")) { - if (`p0₅`.&(2).!=(0)) `p0₅` = `p0₅`.^(2) else `in₈`.duplicatedKeyError(`l₅`) - _hobbies = d5(`in₈`, _hobbies) - } else if (`in₈`.isCharBufEqualsTo(`l₅`, "friends")) { - if (`p0₅`.&(4).!=(0)) `p0₅` = `p0₅`.^(4) else `in₈`.duplicatedKeyError(`l₅`) - _friends = d3(`in₈`, _friends) - } else if (`in₈`.isCharBufEqualsTo(`l₅`, "pets")) { - if (`p0₅`.&(8).!=(0)) `p0₅` = `p0₅`.^(8) else `in₈`.duplicatedKeyError(`l₅`) - _pets = d1(`in₈`, _pets) - } else `in₈`.skip() - } - if (`in₈`.isCurrentToken(125).unary_!) `in₈`.objectEndOrCommaError() else () - } else () - if (`p0₅`.&(1).!=(0)) `in₈`.requiredFieldError(f0(java.lang.Integer.numberOfTrailingZeros(`p0₅`.&(1)))) else () - new co.blocke.Record(_person, _hobbies, _friends, _pets) - } else `in₈`.readNullOrTokenError[co.blocke.Record](`default₈`, 123) - def e2(`x₄`: co.blocke.Address, out: com.github.plokhotnyuk.jsoniter_scala.core.JsonWriter): scala.Unit = { - out.writeObjectStart() - out.writeNonEscapedAsciiKey("street") - out.writeVal(`x₄`.street) - out.writeNonEscapedAsciiKey("city") - out.writeVal(`x₄`.city) - out.writeNonEscapedAsciiKey("state") - out.writeVal(`x₄`.state) - out.writeNonEscapedAsciiKey("postal_code") - out.writeVal(`x₄`.postal_code) - out.writeObjectEnd() - } - def e3(`x₅`: scala.collection.immutable.List[scala.Predef.String] & scala.collection.Iterable[_ >: scala.Nothing <: scala.Any], `out₂`: com.github.plokhotnyuk.jsoniter_scala.core.JsonWriter): scala.Unit = { - `out₂`.writeArrayStart() - var `l₆`: scala.collection.immutable.List[java.lang.String] = `x₅` - while (`l₆`.ne(scala.Nil)) { - `out₂`.writeVal(`l₆`.head) - `l₆` = `l₆`.tail - } - `out₂`.writeArrayEnd() - } - def e1(`x₆`: co.blocke.Person, `out₃`: com.github.plokhotnyuk.jsoniter_scala.core.JsonWriter): scala.Unit = { - `out₃`.writeObjectStart() - `out₃`.writeNonEscapedAsciiKey("name") - `out₃`.writeVal(`x₆`.name) - `out₃`.writeNonEscapedAsciiKey("age") - `out₃`.writeVal(`x₆`.age) - `out₃`.writeNonEscapedAsciiKey("address") - e2(`x₆`.address, `out₃`) - `out₃`.writeNonEscapedAsciiKey("email") - `out₃`.writeVal(`x₆`.email) - val v: scala.collection.immutable.List[scala.Predef.String] & scala.collection.Iterable[_ >: scala.Nothing <: scala.Any] = `x₆`.phone_numbers - if (v.isEmpty.unary_!) { - `out₃`.writeNonEscapedAsciiKey("phone_numbers") - e3(v, `out₃`) - } else () - `out₃`.writeNonEscapedAsciiKey("is_employed") - `out₃`.writeVal(`x₆`.is_employed) - `out₃`.writeObjectEnd() - } - def e5(`x₇`: co.blocke.Friend, `out₄`: com.github.plokhotnyuk.jsoniter_scala.core.JsonWriter): scala.Unit = { - `out₄`.writeObjectStart() - `out₄`.writeNonEscapedAsciiKey("name") - `out₄`.writeVal(`x₇`.name) - `out₄`.writeNonEscapedAsciiKey("age") - `out₄`.writeVal(`x₇`.age) - `out₄`.writeNonEscapedAsciiKey("email") - `out₄`.writeVal(`x₇`.email) - `out₄`.writeObjectEnd() - } - def e4(`x₈`: scala.collection.immutable.List[co.blocke.Friend] & scala.collection.Iterable[_ >: scala.Nothing <: scala.Any], `out₅`: com.github.plokhotnyuk.jsoniter_scala.core.JsonWriter): scala.Unit = { - `out₅`.writeArrayStart() - var `l₇`: scala.collection.immutable.List[co.blocke.Friend] = `x₈` - while (`l₇`.ne(scala.Nil)) { - e5(`l₇`.head, `out₅`) - `l₇` = `l₇`.tail - } - `out₅`.writeArrayEnd() - } - def e7(`x₉`: co.blocke.Pet, `out₆`: com.github.plokhotnyuk.jsoniter_scala.core.JsonWriter): scala.Unit = { - `out₆`.writeObjectStart() - `out₆`.writeNonEscapedAsciiKey("name") - `out₆`.writeVal(`x₉`.name) - `out₆`.writeNonEscapedAsciiKey("species") - `out₆`.writeVal(`x₉`.species) - `out₆`.writeNonEscapedAsciiKey("age") - `out₆`.writeVal(`x₉`.age) - `out₆`.writeObjectEnd() - } - def e6(`x₁₀`: scala.collection.immutable.List[co.blocke.Pet] & scala.collection.Iterable[_ >: scala.Nothing <: scala.Any], `out₇`: com.github.plokhotnyuk.jsoniter_scala.core.JsonWriter): scala.Unit = { - `out₇`.writeArrayStart() - var `l₈`: scala.collection.immutable.List[co.blocke.Pet] = `x₁₀` - while (`l₈`.ne(scala.Nil)) { - e7(`l₈`.head, `out₇`) - `l₈` = `l₈`.tail - } - `out₇`.writeArrayEnd() - } - def e0(`x₁₁`: co.blocke.Record, `out₈`: com.github.plokhotnyuk.jsoniter_scala.core.JsonWriter): scala.Unit = { - `out₈`.writeObjectStart() - `out₈`.writeNonEscapedAsciiKey("person") - e1(`x₁₁`.person, `out₈`) - val `v₂`: scala.collection.immutable.List[scala.Predef.String] & scala.collection.Iterable[_ >: scala.Nothing <: scala.Any] = `x₁₁`.hobbies - if (`v₂`.isEmpty.unary_!) { - `out₈`.writeNonEscapedAsciiKey("hobbies") - e3(`v₂`, `out₈`) - } else () - val `v₃`: scala.collection.immutable.List[co.blocke.Friend] & scala.collection.Iterable[_ >: scala.Nothing <: scala.Any] = `x₁₁`.friends - if (`v₃`.isEmpty.unary_!) { - `out₈`.writeNonEscapedAsciiKey("friends") - e4(`v₃`, `out₈`) - } else () - val `v₄`: scala.collection.immutable.List[co.blocke.Pet] & scala.collection.Iterable[_ >: scala.Nothing <: scala.Any] = `x₁₁`.pets - if (`v₄`.isEmpty.unary_!) { - `out₈`.writeNonEscapedAsciiKey("pets") - e6(`v₄`, `out₈`) - } else () - `out₈`.writeObjectEnd() - } - final class $anon() extends com.github.plokhotnyuk.jsoniter_scala.core.JsonValueCodec[co.blocke.Record] { - def nullValue: co.blocke.Record = null - def decodeValue(`in₉`: com.github.plokhotnyuk.jsoniter_scala.core.JsonReader, `default₉`: co.blocke.Record): co.blocke.Record = d0(`in₉`, `default₉`) - def encodeValue(`x₁₂`: co.blocke.Record, `out₉`: com.github.plokhotnyuk.jsoniter_scala.core.JsonWriter): scala.Unit = e0(`x₁₂`, `out₉`) - } - - (new $anon(): com.github.plokhotnyuk.jsoniter_scala.core.JsonValueCodec[co.blocke.Record]) -} \ No newline at end of file From b19ecd8620e3a0b1cfe423f764892291f26e12cc Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Sun, 3 Mar 2024 23:20:59 -0600 Subject: [PATCH 46/65] more perf tweaks --- benchmark/build.sbt | 2 +- .../src/main/scala/co.blocke/Argonaut.scala | 22 +-- .../src/main/scala/co.blocke/Benchmark.scala | 6 +- .../src/main/scala/co.blocke/Circe.scala | 22 +-- .../src/main/scala/co.blocke/Jsoniter.scala | 4 +- .../src/main/scala/co.blocke/PlayJson.scala | 42 ++--- .../src/main/scala/co.blocke/Record.scala | 20 +-- .../scala/co.blocke/{Run.scala => Run.scalax} | 0 .../src/main/scala/co.blocke/ScalaJack.scala | 6 +- .../src/main/scala/co.blocke/ZIOJson.scala | 22 +-- .../json/JsonCodecMaker.scala | 99 ++++++------ .../json/reading/JsonSource.scala | 148 +++++++++++------- .../json/reading/Numbers.scala | 2 +- 13 files changed, 220 insertions(+), 175 deletions(-) rename benchmark/src/main/scala/co.blocke/{Run.scala => Run.scalax} (100%) diff --git a/benchmark/build.sbt b/benchmark/build.sbt index 18f05eb3..cb7d6d5f 100644 --- a/benchmark/build.sbt +++ b/benchmark/build.sbt @@ -37,7 +37,7 @@ lazy val benchmark = project libraryDependencies ++= Seq( "org.playframework" %% "play-json" % "3.0.1", "io.argonaut" %% "argonaut" % "6.3.9", - "co.blocke" %% "scalajack" % "54f0b4_unknown", + "co.blocke" %% "scalajack" % "b08f70_unknown", "co.blocke" %% "scala-reflection" % "sj_fixes_f43af7", "dev.zio" %% "zio-json" % "0.6.1", "org.typelevel" %% "fabric-core" % "1.12.6", diff --git a/benchmark/src/main/scala/co.blocke/Argonaut.scala b/benchmark/src/main/scala/co.blocke/Argonaut.scala index 7ca40696..83ba6d17 100644 --- a/benchmark/src/main/scala/co.blocke/Argonaut.scala +++ b/benchmark/src/main/scala/co.blocke/Argonaut.scala @@ -6,25 +6,25 @@ import org.openjdk.jmh.annotations._ object ArgonautZ: import argonaut._, Argonaut._ - implicit val CodecPet: CodecJson[Pet] = - casecodec3(Pet.apply, (a: Pet) => Option((a.name, a.species, a.age)))("name","species","age") + implicit val CodecPet: CodecJson[Pet2] = + casecodec3(Pet2.apply, (a: Pet2) => Option((a.name, a.species, a.age)))("name","species","age") - implicit val CodecFriend: CodecJson[Friend] = - casecodec3(Friend.apply, (a: Friend) => Option((a.name, a.age, a.email)))("name","age","email") + implicit val CodecFriend: CodecJson[Friend2] = + casecodec3(Friend2.apply, (a: Friend2) => Option((a.name, a.age, a.email)))("name","age","email") - implicit val CodecAddress: CodecJson[Address] = - casecodec4(Address.apply, (a: Address) => Option((a.street, a.city, a.state, a.postal_code)))("street","city","state","postal_code") + implicit val CodecAddress: CodecJson[Address2] = + casecodec4(Address2.apply, (a: Address2) => Option((a.street, a.city, a.state, a.postal_code)))("street","city","state","postal_code") - implicit val CodecPerson: CodecJson[Person] = - casecodec6(Person.apply, (a: Person) => Option((a.name, a.age, a.address, a.email, a.phone_numbers, a.is_employed)))("name", "age","address","email","phone_numbers","is_employed") + implicit val CodecPerson: CodecJson[Person2] = + casecodec6(Person2.apply, (a: Person2) => Option((a.name, a.age, a.address, a.email, a.phone_numbers, a.is_employed)))("name", "age","address","email","phone_numbers","is_employed") - implicit val CodecRecord: CodecJson[Record] = - casecodec4(Record.apply, (a: Record) => Option((a.person, a.hobbies, a.friends, a.pets)))("person", "hobbies", "friends", "pets") + implicit val CodecRecord: CodecJson[Record2] = + casecodec4(Record2.apply, (a: Record2) => Option((a.person, a.hobbies, a.friends, a.pets)))("person", "hobbies", "friends", "pets") trait ArgonautReadingBenchmark { @Benchmark - def readRecordArgonaut = Parse.decodeEither[Record](jsData) + def readRecordArgonaut = Parse.decodeEither[Record2](jsData2) } trait ArgonautWritingBenchmark { diff --git a/benchmark/src/main/scala/co.blocke/Benchmark.scala b/benchmark/src/main/scala/co.blocke/Benchmark.scala index c3eac501..a82ad557 100644 --- a/benchmark/src/main/scala/co.blocke/Benchmark.scala +++ b/benchmark/src/main/scala/co.blocke/Benchmark.scala @@ -5,9 +5,9 @@ import java.util.concurrent.TimeUnit import ZIOZ.* import zio.json._ -val record = jsData.fromJson[Record] match +val record = jsData2.fromJson[Record2] match case Right(r) => r - case Left(_) => null.asInstanceOf[Record] + case Left(_) => null.asInstanceOf[Record2] trait HandTooledWritingBenchmark { @@ -45,7 +45,7 @@ trait HandTooledWritingBenchmark { class ReadingBenchmark extends ScalaJackZ.ScalaJackReadingBenchmark // with CirceZ.CirceReadingBenchmark - // with JsoniterZ.JsoniterReadingBenchmark + // extends JsoniterZ.JsoniterReadingBenchmark // with ZIOZ.ZIOJsonReadingBenchmark // with PlayZ.PlayReadingBenchmark // with ArgonautZ.ArgonautReadingBenchmark diff --git a/benchmark/src/main/scala/co.blocke/Circe.scala b/benchmark/src/main/scala/co.blocke/Circe.scala index ebb88705..58ec31a5 100644 --- a/benchmark/src/main/scala/co.blocke/Circe.scala +++ b/benchmark/src/main/scala/co.blocke/Circe.scala @@ -8,24 +8,24 @@ object CirceZ: import io.circe.generic.semiauto.* import io.circe.parser.* - implicit val recordDecoder: Decoder[Record] = deriveDecoder[Record] - implicit val recordEncoder: Encoder[Record] = deriveEncoder[Record] + implicit val recordDecoder: Decoder[Record2] = deriveDecoder[Record2] + implicit val recordEncoder: Encoder[Record2] = deriveEncoder[Record2] - implicit val personDecoder: Decoder[Person] = deriveDecoder[Person] - implicit val personEncoder: Encoder[Person] = deriveEncoder[Person] + implicit val personDecoder: Decoder[Person2] = deriveDecoder[Person2] + implicit val personEncoder: Encoder[Person2] = deriveEncoder[Person2] - implicit val addressDecoder: Decoder[Address] = deriveDecoder[Address] - implicit val addressEncoder: Encoder[Address] = deriveEncoder[Address] + implicit val addressDecoder: Decoder[Address2] = deriveDecoder[Address2] + implicit val addressEncoder: Encoder[Address2] = deriveEncoder[Address2] - implicit val friendDecoder: Decoder[Friend] = deriveDecoder[Friend] - implicit val friendEncoder: Encoder[Friend] = deriveEncoder[Friend] + implicit val friendDecoder: Decoder[Friend2] = deriveDecoder[Friend2] + implicit val friendEncoder: Encoder[Friend2] = deriveEncoder[Friend2] - implicit val petDecoder: Decoder[Pet] = deriveDecoder[Pet] - implicit val petEncoder: Encoder[Pet] = deriveEncoder[Pet] + implicit val petDecoder: Decoder[Pet2] = deriveDecoder[Pet2] + implicit val petEncoder: Encoder[Pet2] = deriveEncoder[Pet2] trait CirceReadingBenchmark{ @Benchmark - def readRecordCirce = parse(jsData).flatMap(_.as[Record]) + def readRecordCirce = parse(jsData2).flatMap(_.as[Record2]) } trait CirceWritingBenchmark { diff --git a/benchmark/src/main/scala/co.blocke/Jsoniter.scala b/benchmark/src/main/scala/co.blocke/Jsoniter.scala index f045dd15..e591e425 100644 --- a/benchmark/src/main/scala/co.blocke/Jsoniter.scala +++ b/benchmark/src/main/scala/co.blocke/Jsoniter.scala @@ -7,10 +7,10 @@ object JsoniterZ: import com.github.plokhotnyuk.jsoniter_scala.core._ import com.github.plokhotnyuk.jsoniter_scala.macros._ - given codec: JsonValueCodec[Record] = JsonCodecMaker.make + given codec: JsonValueCodec[Record2] = JsonCodecMaker.make trait JsoniterReadingBenchmark{ @Benchmark - def readRecordJsoniter = readFromString[Record](jsData) + def readRecordJsoniter = readFromString[Record2](jsData2) } trait JsoniterWritingBenchmark{ diff --git a/benchmark/src/main/scala/co.blocke/PlayJson.scala b/benchmark/src/main/scala/co.blocke/PlayJson.scala index 0eb58a60..a3a84eb5 100644 --- a/benchmark/src/main/scala/co.blocke/PlayJson.scala +++ b/benchmark/src/main/scala/co.blocke/PlayJson.scala @@ -7,46 +7,46 @@ object PlayZ: import play.api.libs.json.Reads._ import play.api.libs.functional.syntax._ - implicit val friendWrites: Writes[Friend] = ( + implicit val friendWrites: Writes[Friend2] = ( (JsPath \ "name").write[String] and (JsPath \ "age").write[Int] and (JsPath \ "email").write[String] - )(unlift((a: Friend) => Option((a.name, a.age, a.email)))) + )(unlift((a: Friend2) => Option((a.name, a.age, a.email)))) - implicit val petWrites: Writes[Pet] = ( + implicit val petWrites: Writes[Pet2] = ( (JsPath \ "name").write[String] and (JsPath \ "species").write[String] and (JsPath \ "age").write[Int] - )(unlift((a: Pet) => Option((a.name, a.species, a.age)))) + )(unlift((a: Pet2) => Option((a.name, a.species, a.age)))) - implicit val addressWrites: Writes[Address] = ( + implicit val addressWrites: Writes[Address2] = ( (JsPath \ "street").write[String] and (JsPath \ "city").write[String] and (JsPath \ "state").write[String] and (JsPath \ "postal_code").write[String] - )(unlift((a: Address) => Option((a.street, a.city, a.state, a.postal_code)))) + )(unlift((a: Address2) => Option((a.street, a.city, a.state, a.postal_code)))) - implicit val personWrites: Writes[Person] = ( - (JsPath \ "namet").write[String] and + implicit val personWrites: Writes[Person2] = ( + (JsPath \ "name").write[String] and (JsPath \ "age").write[Int] and - (JsPath \ "address").write[Address] and + (JsPath \ "address").write[Address2] and (JsPath \ "email").write[String] and (JsPath \ "phone_numbers").write[List[String]] and (JsPath \ "is_employed").write[Boolean] - )(unlift((a: Person) => Option((a.name, a.age, a.address, a.email, a.phone_numbers, a.is_employed)))) + )(unlift((a: Person2) => Option((a.name, a.age, a.address, a.email, a.phone_numbers, a.is_employed)))) - implicit val recordWrites: Writes[Record] = ( - (JsPath \ "person").write[Person] and + implicit val recordWrites: Writes[Record2] = ( + (JsPath \ "person").write[Person2] and (JsPath \ "hobbies").write[List[String]] and - (JsPath \ "friends").write[List[Friend]] and - (JsPath \ "pets").write[List[Pet]] - )(unlift((a: Record) => Option((a.person, a.hobbies, a.friends, a.pets)))) + (JsPath \ "friends").write[List[Friend2]] and + (JsPath \ "pets").write[List[Pet2]] + )(unlift((a: Record2) => Option((a.person, a.hobbies, a.friends, a.pets)))) - implicit val friendReads: play.api.libs.json.Reads[co.blocke.Friend] = Json.reads[Friend] - implicit val petReads: play.api.libs.json.Reads[co.blocke.Pet] = Json.reads[Pet] - implicit val addressReads: play.api.libs.json.Reads[co.blocke.Address] = Json.reads[Address] - implicit val personReads: play.api.libs.json.Reads[co.blocke.Person] = Json.reads[Person] - implicit val recordReads: play.api.libs.json.Reads[co.blocke.Record] = Json.reads[Record] + implicit val friendReads: play.api.libs.json.Reads[co.blocke.Friend2] = Json.reads[Friend2] + implicit val petReads: play.api.libs.json.Reads[co.blocke.Pet2] = Json.reads[Pet2] + implicit val addressReads: play.api.libs.json.Reads[co.blocke.Address2] = Json.reads[Address2] + implicit val personReads: play.api.libs.json.Reads[co.blocke.Person2] = Json.reads[Person2] + implicit val recordReads: play.api.libs.json.Reads[co.blocke.Record2] = Json.reads[Record2] trait PlayWritingBenchmark { @Benchmark @@ -56,5 +56,5 @@ object PlayZ: // val playJS = Json.toJson(record) trait PlayReadingBenchmark { @Benchmark - def readRecordPlay = Json.fromJson[Record](Json.parse(jsData)) //Json.fromJson[Record](playJS) + def readRecordPlay = Json.fromJson[Record2](Json.parse(jsData2)) //Json.fromJson[Record](playJS) } diff --git a/benchmark/src/main/scala/co.blocke/Record.scala b/benchmark/src/main/scala/co.blocke/Record.scala index dc8a9e60..4ad76eab 100644 --- a/benchmark/src/main/scala/co.blocke/Record.scala +++ b/benchmark/src/main/scala/co.blocke/Record.scala @@ -1,42 +1,42 @@ package co.blocke -case class Person( +case class Person2( name: String, age: Int, - address: Address, + address: Address2, email: String, phone_numbers: List[String], is_employed: Boolean ) -case class Address( +case class Address2( street: String, city: String, state: String, postal_code: String ) -case class Friend( +case class Friend2( name: String, age: Int, email: String ) -case class Pet( +case class Pet2( name: String, species: String, age: Int ) -case class Record( - person: Person, +case class Record2( + person: Person2, hobbies: List[String], - friends: List[Friend], - pets: List[Pet] + friends: List[Friend2], + pets: List[Pet2] ) -val jsData = +val jsData2 = """{ "person": { "name": "John Doe", diff --git a/benchmark/src/main/scala/co.blocke/Run.scala b/benchmark/src/main/scala/co.blocke/Run.scalax similarity index 100% rename from benchmark/src/main/scala/co.blocke/Run.scala rename to benchmark/src/main/scala/co.blocke/Run.scalax diff --git a/benchmark/src/main/scala/co.blocke/ScalaJack.scala b/benchmark/src/main/scala/co.blocke/ScalaJack.scala index 132466d5..aefac286 100644 --- a/benchmark/src/main/scala/co.blocke/ScalaJack.scala +++ b/benchmark/src/main/scala/co.blocke/ScalaJack.scala @@ -6,16 +6,16 @@ object ScalaJackZ: import co.blocke.scalajack.ScalaJack.* import co.blocke.scalajack.* - implicit val blah: ScalaJack[Record] = sj[Record] + implicit val blah: ScalaJack[co.blocke.Record2] = sj[co.blocke.Record2] trait ScalaJackReadingBenchmark{ @Benchmark // def readRecordScalaJack = sj[Record].fromJson(jsData) // 500K - def readRecordScalaJack = ScalaJack[Record].fromJson(jsData) // 515K :-( + def readRecordScalaJack = ScalaJack[co.blocke.Record2].fromJson(jsData2) // 515K :-( } trait ScalaJackWritingBenchmark { @Benchmark // def writeRecordScalaJack = sj[Record].toJson(record) // 677K score - def writeRecordScalaJack = ScalaJack[Record].toJson(record) // 1.7M score <- faster + def writeRecordScalaJack = ScalaJack[co.blocke.Record2].toJson(record) // 1.7M score <- faster } diff --git a/benchmark/src/main/scala/co.blocke/ZIOJson.scala b/benchmark/src/main/scala/co.blocke/ZIOJson.scala index 9a5b2d97..9162ecdf 100644 --- a/benchmark/src/main/scala/co.blocke/ZIOJson.scala +++ b/benchmark/src/main/scala/co.blocke/ZIOJson.scala @@ -5,16 +5,16 @@ import org.openjdk.jmh.annotations._ object ZIOZ: import zio.json._ - implicit val decoder1: JsonDecoder[Address] = DeriveJsonDecoder.gen[Address] - implicit val decoder2: JsonDecoder[Pet] = DeriveJsonDecoder.gen[Pet] - implicit val decoder3: JsonDecoder[Friend] = DeriveJsonDecoder.gen[Friend] - implicit val decoder4: JsonDecoder[Person] = DeriveJsonDecoder.gen[Person] - implicit val decoder5: JsonDecoder[Record] = DeriveJsonDecoder.gen[Record] - implicit val encoder1: JsonEncoder[Address] = DeriveJsonEncoder.gen[Address] - implicit val encoder2: JsonEncoder[Pet] = DeriveJsonEncoder.gen[Pet] - implicit val encoder3: JsonEncoder[Friend] = DeriveJsonEncoder.gen[Friend] - implicit val encoder4: JsonEncoder[Person] = DeriveJsonEncoder.gen[Person] - implicit val encoder5: JsonEncoder[Record] = DeriveJsonEncoder.gen[Record] + implicit val decoder1: JsonDecoder[Address2] = DeriveJsonDecoder.gen[Address2] + implicit val decoder2: JsonDecoder[Pet2] = DeriveJsonDecoder.gen[Pet2] + implicit val decoder3: JsonDecoder[Friend2] = DeriveJsonDecoder.gen[Friend2] + implicit val decoder4: JsonDecoder[Person2] = DeriveJsonDecoder.gen[Person2] + implicit val decoder5: JsonDecoder[Record2] = DeriveJsonDecoder.gen[Record2] + implicit val encoder1: JsonEncoder[Address2] = DeriveJsonEncoder.gen[Address2] + implicit val encoder2: JsonEncoder[Pet2] = DeriveJsonEncoder.gen[Pet2] + implicit val encoder3: JsonEncoder[Friend2] = DeriveJsonEncoder.gen[Friend2] + implicit val encoder4: JsonEncoder[Person2] = DeriveJsonEncoder.gen[Person2] + implicit val encoder5: JsonEncoder[Record2] = DeriveJsonEncoder.gen[Record2] trait ZIOJsonWritingBenchmark { @Benchmark @@ -23,5 +23,5 @@ object ZIOZ: trait ZIOJsonReadingBenchmark { @Benchmark - def readRecordZIOJson = jsData.fromJson[Record] + def readRecordZIOJson = jsData2.fromJson[Record2] } \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala b/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala index 7f841519..26a69479 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala @@ -818,21 +818,20 @@ object JsonCodecMaker: val caseDef = CaseDef( Literal(StringConstant(oneField.name)), None, - // Assign(fieldSymRef, genReadVal[f](oneField.fieldRef.asInstanceOf[RTypeRef[f]], in).asTerm) - '{ - if (${ Ref(reqSym).asExprOf[Int] } & $reqBit) != 0 then - ${ Assign(Ref(reqSym), '{ ${ Ref(reqSym).asExprOf[Int] } ^ $reqBit }.asTerm).asExprOf[Unit] } - ${ Assign(fieldSymRef, genReadVal[f](oneField.fieldRef.asInstanceOf[RTypeRef[f]], in).asTerm).asExprOf[Unit] } - else throw new JsonParseError("Duplicate field " + $fieldName, $in) - }.asTerm + Assign(fieldSymRef, genReadVal[f](oneField.fieldRef.asInstanceOf[RTypeRef[f]], in).asTerm) + // '{ + // if (${ Ref(reqSym).asExprOf[Int] } & $reqBit) != 0 then + // ${ Assign(Ref(reqSym), '{ ${ Ref(reqSym).asExprOf[Int] } ^ $reqBit }.asTerm).asExprOf[Unit] } + // ${ Assign(fieldSymRef, genReadVal[f](oneField.fieldRef.asInstanceOf[RTypeRef[f]], in).asTerm).asExprOf[Unit] } + // else throw new JsonParseError("Duplicate field " + $fieldName, $in) + // }.asTerm ) - // if dvMembers.isEmpty then (ValDef(sym, Some(oneField.fieldRef.unitVal.asTerm)), caseDef, fieldSymRef) + if dvMembers.isEmpty then (ValDef(sym, Some(oneField.fieldRef.unitVal.asTerm)), caseDef, fieldSymRef) if dvMembers.isEmpty then // no default... required? Not if Option/Optional, or a collection oneField.fieldRef match { - case _: OptionRef[?] => // not required - case _: CollectionRef[?] => // not required - case _ => required = required | math.pow(2, oneField.index).toInt // required + case _: OptionRef[?] => // not required + case _ => required = required | math.pow(2, oneField.index).toInt // required } (ValDef(sym, Some(oneField.fieldRef.unitVal.asTerm)), caseDef, fieldSymRef) else @@ -861,28 +860,40 @@ object JsonCodecMaker: val instantiateClass = argss.tail.foldLeft(Apply(constructor, argss.head))((acc, args) => Apply(acc, args)) val exprRequired = Expr(required) + val parseLoop = '{ - if ! $in.expectObjectOrNull() then null.asInstanceOf[T] + var maybeFname = $in.expectFirstObjectField() + if maybeFname == null then null.asInstanceOf[T] else - var c = $in.readCharWS() - while c match { - case '"' => - ${ Match('{ $in.expectObjectField() }.asTerm, caseDefsWithFinal).asExprOf[Any] } - c = $in.readCharWS() - if c == ',' then - c = $in.readCharWS() - true - else true - case '}' => false - case c => throw new JsonParseError(s"Expected '\"' or '}' here but found '$c'", $in) - } - do () - if (${ Ref(reqSym).asExprOf[Int] } & ${ exprRequired }) != 0 then throw new JsonParseError("Missing required field(s) " + ${ allFieldNames }(Integer.numberOfTrailingZeros(${ Ref(reqSym).asExprOf[Int] } & ${ exprRequired })), $in) - ${ instantiateClass.asExprOf[T] } + while maybeFname.isDefined do + ${ Match('{ maybeFname.get }.asTerm, caseDefsWithFinal).asExprOf[Any] } + maybeFname = $in.expectObjectField() + ${ instantiateClass.asExprOf[T] } }.asTerm - // Block(varDefs, parseLoop).asExprOf[T] - Block(varDefs :+ reqVarDef, parseLoop).asExprOf[T] + // val parseLoop = '{ + // if ! $in.expectObjectOrNull() then null.asInstanceOf[T] + // else + // var c = $in.readCharWS() + // while c match { + // case '"' => + // ${ Match('{ $in.expectObjectField() }.asTerm, caseDefsWithFinal).asExprOf[Any] } + // c = $in.readCharWS() + // if c == ',' then + // c = $in.readCharWS() + // true + // else true + // case '}' => false + // case c => throw new JsonParseError(s"Expected '\"' or '}' here but found '$c'", $in) + // } + // do () + // ${ instantiateClass.asExprOf[T] } + // // if (${ Ref(reqSym).asExprOf[Int] } & ${ exprRequired }) == 0 then ${ instantiateClass.asExprOf[T] } + // // else throw new JsonParseError("Missing required field(s) " + ${ allFieldNames }(Integer.numberOfTrailingZeros(${ Ref(reqSym).asExprOf[Int] } & ${ exprRequired })), $in) + // }.asTerm + + Block(varDefs, parseLoop).asExprOf[T] + // Block(varDefs :+ reqVarDef, parseLoop).asExprOf[T] ) case _ => ??? @@ -951,8 +962,8 @@ object JsonCodecMaker: val rtypeRef = t.elementRef.asInstanceOf[RTypeRef[e]] '{ val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in) }) - if parsedArray == null then null - else parsedArray.toList + if parsedArray != null then parsedArray.toList + else null }.asExprOf[T] case '[Vector[?]] => t.elementRef.refType match @@ -960,8 +971,8 @@ object JsonCodecMaker: val rtypeRef = t.elementRef.asInstanceOf[RTypeRef[e]] '{ val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in) }) - if parsedArray == null then null - else parsedArray.toVector + if parsedArray != null then parsedArray.toVector + else null }.asExprOf[T] case '[Seq[?]] => t.elementRef.refType match @@ -969,8 +980,8 @@ object JsonCodecMaker: val rtypeRef = t.elementRef.asInstanceOf[RTypeRef[e]] '{ val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in) }) - if parsedArray == null then null - else parsedArray.toSeq + if parsedArray != null then parsedArray.toSeq + else null }.asExprOf[T] case '[IndexedSeq[?]] => t.elementRef.refType match @@ -978,8 +989,8 @@ object JsonCodecMaker: val rtypeRef = t.elementRef.asInstanceOf[RTypeRef[e]] '{ val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in) }) - if parsedArray == null then null - else parsedArray.toIndexedSeq + if parsedArray != null then parsedArray.toIndexedSeq + else null }.asExprOf[T] case '[Iterable[?]] => t.elementRef.refType match @@ -987,8 +998,8 @@ object JsonCodecMaker: val rtypeRef = t.elementRef.asInstanceOf[RTypeRef[e]] '{ val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in) }) - if parsedArray == null then null - else parsedArray.toIterable + if parsedArray != null then parsedArray.toIterable + else null }.asExprOf[T] // Catch all, with (slightly) slower type coersion to proper Seq flavor case _ => @@ -997,8 +1008,8 @@ object JsonCodecMaker: val rtypeRef = t.elementRef.asInstanceOf[RTypeRef[e]] '{ val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in) }) - if parsedArray == null then null - else parsedArray.to(${ Expr.summon[Factory[e, T]].get }) // create appropriate flavor of Seq[T] here + if parsedArray != null then parsedArray.to(${ Expr.summon[Factory[e, T]].get }) // create appropriate flavor of Seq[T] here + else null }.asExprOf[T] case t: ArrayRef[?] => @@ -1008,10 +1019,10 @@ object JsonCodecMaker: val ct = Expr.summon[ClassTag[e]].get '{ val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in) }) - if parsedArray == null then null - else + if parsedArray != null then implicit val ctt = $ct - parsedArray.toArray[e] // (${ Expr.summon[Factory[e, T]].get }) // create appropriate flavor of Seq[T] here + parsedArray.toArray[e] + else null }.asExprOf[T] case _ => diff --git a/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala b/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala index c07d1b51..81065b47 100644 --- a/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala +++ b/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala @@ -3,6 +3,7 @@ package json package reading import scala.annotation.{switch, tailrec} +import co.blocke.scalajack.json.reading.SafeNumbers.double object JsonSource: protected val ull: Array[Char] = "ull".toCharArray @@ -37,9 +38,14 @@ case class JsonSource(js: CharSequence): private inline def isWhitespace(c: Char): Boolean = c == ' ' || c == '\n' || (c | 0x4) == '\r' || c == '\t' - inline def readCharWS(): Char = - while isWhitespace(here) && i < max do i += 1 - readChar() + @tailrec + final private[this] def readToken(): Char = + if i == max then throw new JsonParseError("Unexpected end of buffer", this) + else + val b = here + i += 1 + if !(b == ' ' || b == '\n' || b == '\t' || (b | 0x4) == '\r') then b + else readToken() inline def skipWS(): Unit = while isWhitespace(here) && i < max do i += 1 @@ -75,7 +81,7 @@ case class JsonSource(js: CharSequence): var accum: Int = 0 while i < 4 do var c = readChar().toInt - if c == BUFFER_EXCEEDED then throw JsonParseError("Unexpected EOB in string", this) + if c == BUFFER_EXCEEDED then throw JsonParseError("Unexpected end of buffer", this) c = if '0' <= c && c <= '9' then c - '0' else if 'A' <= c && c <= 'F' then c - 'A' + 10 @@ -85,39 +91,61 @@ case class JsonSource(js: CharSequence): i += 1 accum.toChar - inline def expectObjectOrNull(): Boolean = // false => null - readCharWS() match { - case '{' => true - case 'n' => - readChars(JsonSource.ull, "null") - false - case _ => throw new JsonParseError(s"Expected object start '{' or null", this) - } + // returns false if 'null' found + def expectFirstObjectField(): Option[CharSequence] = + val t = readToken() + if t == '{' then + val tt = readToken() + if tt == '"' then + val endI = parseString(i) + val fname = js.subSequence(i, endI) + i = endI + 1 + if readToken() != ':' then throw new JsonParseError(s"Expected ':' field separator", this) + Some(fname) + else if tt == '}' then None + else throw new JsonParseError(s"Expected object field name or '}' but found '$tt'", this) + else if t == 'n' then + readChars(JsonSource.ull, "null") + null + else throw new JsonParseError(s"Expected object start '{' or null", this) - inline def expectObjectField(): CharSequence = - val endI = parseString(i) - val fname = js.subSequence(i, endI) - i = endI + 1 - if readCharWS() != ':' then throw new JsonParseError(s"Expected ':' separator token", this) - fname + def expectObjectField(): Option[CharSequence] = + val t = readToken() + if t == ',' then + val tt = readToken() + if tt == '"' then + val endI = parseString(i) + val fname = js.subSequence(i, endI) + i = endI + 1 + if readToken() != ':' then throw new JsonParseError(s"Expected ':' field separator", this) + Some(fname) + else throw new JsonParseError(s"Expected object field name but found '$tt'", this) + else if t == '}' then None + else throw new JsonParseError(s"Expected ',' or '}' but found $t", this) + + @tailrec + final private[this] def addAllArray[E](s: scala.collection.mutable.ListBuffer[E], f: () => E): scala.collection.mutable.ListBuffer[E] = + if i == max then throw JsonParseError("Unexpected end of buffer", this) + s.addOne(f()) + val tt = readToken() + if tt == ']' then + retract() + s + else if tt != ',' then throw JsonParseError(s"Expected ',' or ']' got '$tt'", this) + else addAllArray(s, f) def expectArray[E](f: () => E): scala.collection.mutable.ListBuffer[E] = - (readCharWS(): @switch) match - case '[' => - val seq = scala.collection.mutable.ListBuffer.empty[E] - skipWS() - while i < max && here != ']' do - seq.addOne(f()) - readCharWS() match { - case ']' => retract() - case ',' => - case c => throw JsonParseError(s"Expected ',' or ']' got '$c'", this) - } - i += 1 - seq - case 'n' => - readChars(JsonSource.ull, "null") - null + val t = readToken() + if t == '[' then + val seq = scala.collection.mutable.ListBuffer.empty[E] + skipWS() + addAllArray(seq, f) + i += 1 + seq + else if t == 'n' then + readChars(JsonSource.ull, "null") + null + else throw JsonParseError(s"Expected array start '[' or null but got '$t'", this) // --------------- // DEPRECATED !!! @@ -175,21 +203,20 @@ case class JsonSource(js: CharSequence): // Value might be null! // expectString() will look for leading '"'. parseString() presumes the '"' has already been consumed. inline def expectString(): CharSequence = - readCharWS() match { - case '"' => - val endI = parseString(i) - val str = js.subSequence(i, endI) - i = endI + 1 - str - case 'n' => - readChars(JsonSource.ull, "null") - null - case c => throw new JsonParseError(s"Expected a String value but got '$c'", this) - } + val t = readToken() + if t == '"' then + val endI = parseString(i) + val str = js.subSequence(i, endI) + i = endI + 1 + str + else if t == 'n' then + readChars(JsonSource.ull, "null") + null + else throw new JsonParseError(s"Expected a String value but got '$t'", this) @tailrec final def parseString(pos: Int): Int = - if pos + 3 < max then { // Based on SWAR routine of JSON string parsing: https://github.com/sirthias/borer/blob/fde9d1ce674d151b0fee1dd0c2565020c3f6633a/core/src/main/scala/io/bullet/borer/json/JsonParser.scala#L456 + if pos + 3 < max then // Based on SWAR routine of JSON string parsing: https://github.com/sirthias/borer/blob/fde9d1ce674d151b0fee1dd0c2565020c3f6633a/core/src/main/scala/io/bullet/borer/json/JsonParser.scala#L456 val bs = (js.charAt(pos)) | (js.charAt(pos + 1) << 8) | (js.charAt(pos + 2) << 16) | js.charAt(pos + 3) << 24 val mask = ((bs - 0x20202020 ^ 0x3c3c3c3c) - 0x1010101 | (bs ^ 0x5d5d5d5d) + 0x1010101) & 0x80808080 if mask != 0 then { @@ -197,12 +224,12 @@ case class JsonSource(js: CharSequence): if (bs >> (offset << 3)).toByte == '"' then pos + offset else throw new Exception("special chars found 1") // else parseEncodedString(i + offset, charBuf.length - 1, charBuf, pos + offset) } else parseString(pos + 4) - } else if pos < max then { + else if pos == max then throw new Exception("Unterminated string value") + else val b = js.charAt(pos) if b == '"' then pos else if (b - 0x20 ^ 0x3c) <= 0 then throw new Exception("special chars found 2") // parseEncodedString(i, charBuf.length - 1, charBuf, pos) else parseString(pos + 1) - } else throw new Exception("Buffer exceeded--string too long") inline def expectChar(): Char = expectString() match { @@ -211,7 +238,7 @@ case class JsonSource(js: CharSequence): } def expectBoolean(): Boolean = - while isWhitespace(here) do i += 1 + skipWS() val bs = (js.charAt(pos)) | (js.charAt(pos + 1) << 8) | (js.charAt(pos + 2) << 16) | js.charAt(pos + 3) << 24 if (bs ^ JsonSource.trueBytes) == 0 then i += 4 @@ -222,14 +249,11 @@ case class JsonSource(js: CharSequence): else throw JsonParseError(s"Expected 'true' or 'false'", this) def expectInt(): Int = - if { skipWS(); !isNumber(here) } then throw JsonParseError(s"Expected a number, got $c", this) - try { - val intRead = UnsafeNumbers.int_(this, false) - retract() - intRead - } catch { - case UnsafeNumbers.UnsafeNumber => throw JsonParseError("Expected an Int", this) - } + skipWS() + if !isNumber(here) then throw JsonParseError(s"Expected a number, got $c", this) + val intRead = UnsafeNumbers.int_(this, false) + retract() + intRead private inline def readChars( expect: Array[Char], @@ -240,6 +264,16 @@ case class JsonSource(js: CharSequence): if readChar() != expect(i) then throw JsonParseError(s"Expected $errMsg", this) i += 1 + // --------------- + // DEPRECATED !!! + // --------------- + inline def readCharWS(): Char = + while isWhitespace(here) && i < max do i += 1 + readChar() + + // --------------- + // DEPRECATED !!! + // --------------- inline def charWithWS(c: Char): Unit = val got = readCharWS() if got != c then throw JsonParseError(s"Expected '$c' got '$got'", this) diff --git a/src/main/scala/co.blocke.scalajack/json/reading/Numbers.scala b/src/main/scala/co.blocke.scalajack/json/reading/Numbers.scala index 30d43c29..b6821be1 100644 --- a/src/main/scala/co.blocke.scalajack/json/reading/Numbers.scala +++ b/src/main/scala/co.blocke.scalajack/json/reading/Numbers.scala @@ -588,7 +588,7 @@ object UnsafeNumbers { if current == -1 then throw UnsafeNumber } - if !isDigit(current) then throw UnsafeNumber + if !isDigit(current) then throw JsonParseError("Unexpected character in Int/Long value: " + current.toChar, in) // throw UnsafeNumber var accum: Long = 0L while { From 19ca46911408882573b96b291eabbeff0625cfe6 Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Thu, 7 Mar 2024 00:21:23 -0600 Subject: [PATCH 47/65] lastest tweaks --- benchmark/README.md | 60 ++++++----- benchmark/ReadingPerformance.png | Bin 0 -> 69457 bytes benchmark/WritingPerformance.png | Bin 78787 -> 81142 bytes benchmark/build.sbt | 2 +- benchmark/foo | 98 ------------------ .../src/main/scala/co.blocke/Benchmark.scala | 15 ++- .../json/JsonCodecMaker.scala | 82 ++++++++------- .../json/StringMatrix.scala | 84 +++++++++++++++ .../json/reading/JsonSource.scala | 78 +++++++++----- .../scala/co.blocke.scalajack/run/Play.scala | 5 + 10 files changed, 228 insertions(+), 196 deletions(-) create mode 100644 benchmark/ReadingPerformance.png delete mode 100644 benchmark/foo create mode 100644 src/main/scala/co.blocke.scalajack/json/StringMatrix.scala diff --git a/benchmark/README.md b/benchmark/README.md index a7420b02..be73924b 100644 --- a/benchmark/README.md +++ b/benchmark/README.md @@ -1,9 +1,8 @@ # Performance JSON serialization benchmarks I found in various project repos often measured (IMO) silly things like how fast -a parser could handle a small list of Int. For this benchmark I used a slightly more substantial model. -It's still a small model, but it does have some nested objects and collections that make it a more -interesting test. +a parser could handle a small list of Int. For this benchmark I used a small, but slightly more representative model. +It has some nested objects and collections that make it a more interesting test. The test is run via jmh. The JVM is **stock**--not tuned to within an inch of its life, to be a more realistic use case. @@ -15,15 +14,16 @@ sbt "jmh:run -i 10 -wi 10 -f 2 -t 1 co.blocke.*" ## Reading Performance: +![image info](./ReadingPerformance.png) + | Benchmark | Mode | Count | Score | Error | Units | |------------------|-------|-------:|----------------:|-------------:|-------| -| Jsoniter | thrpt | 20 | 987991.329 | ± 6645.992 | ops/s | -| **ScalaJack 8 (fast mode)** | thrpt | 20 | **642235.553**| ± 10394.860 | ops/s | -| ZIOJson | thrpt | 20 | 586716.228 | ± 2542.783 | ops/s | -| **ScalaJack 8 (easy mode)** | thrpt | 20 | **426718.318**| ± 692.828 | ops/s | -| Circe | thrpt | 20 | 266568.198 | ± 5695.754 | ops/s | -| Play | thrpt | 20 | 207737.560 | ± 842.108 | ops/s | -| Argonaut | thrpt | 20 | 197876.777 | ± 11181.751 | ops/s | +| Jsoniter | thrpt | 20 | 1346388.345 | ± 17028.863 | ops/s | +| **ScalaJack 8** | thrpt | 20 | **986597.070**| ± 7473.148 | ops/s | +| ZIOJson | thrpt | 20 | 590995.917 | ± 817.817 | ops/s | +| Circe | thrpt | 20 | 210805.946 | ± 32488.564 | ops/s | +| Play | thrpt | 20 | 198747.067 | ± 7253.896 | ops/s | +| Argonaut | thrpt | 20 | 183670.032 | ± 8981.485 | ops/s | ## Writing Performance: @@ -31,11 +31,10 @@ sbt "jmh:run -i 10 -wi 10 -f 2 -t 1 co.blocke.*" | Benchmark | Mode | Count | Score | Error | Units | |------------------|-------|-------:|----------------:|-------------:|-------| -|**ScalaJack 8 (fast mode)** | thrpt | 20 | **3039273.222** | ± 14952.932 | ops/s | +|**ScalaJack 8** | thrpt | 20 | **3039273.222** | ± 14952.932 | ops/s | | Jsoniter | thrpt | 20 | 2843150.452 | ± 21478.503 | ops/s | | Hand-Tooled | thrpt | 20 | 2732571.374 | ± 15129.007 | ops/s | | Circe | thrpt | 20 | 1958244.437 | ± 23965.817 | ops/s | -|**ScalaJack 8 (easy mode)** | thrpt | 20 | **846484.100** | ± 1123.204 | ops/s | | ZIO JSON | thrpt | 20 | 794352.301 | ± 32336.852 | ops/s | | Argonaut | thrpt | 20 | 690269.697 | ± 6348.882 | ops/s | | Play JSON | thrpt | 20 | 438650.022 | ± 23800.221 | ops/s | @@ -46,13 +45,13 @@ were performed on the same platform. ### Interpretation -Performance for ScalaJack has been a journey. ScalaJack is a mature product--over 10 yrs old, -can you believe it?. Long ago it was quite fast vs its competition. Over the years though, its -performance has lagged considerably, to the point that it was one of the slower serialization +Performance for ScalaJack has been a journey. ScalaJack is a mature product--over 10 yrs old. +Long ago it was quite fast vs its competition, but over the years its performance lagged +considerably as its peers improved, to the point that it was one of the slower serialization libraries. ScalaJack 8 changes that! I was sampling and testing against a collection of popular serializers for Scala util -something quite unexpected happend. When I tested Jsoniter, its performance was through +something quite unexpected happend: I discovered Jsoniter. Its performance was through the roof! It far outpaced all competitors for raw speed. This was a shock. I had to learn how this worked. @@ -71,17 +70,9 @@ generation. They also use a lot of low level byte arrays and bitwise operators, expect to see in a C program, to improve on the standard library functions everyone else uses. It works. -ScalaJack's focus is first and foremost to be frictionless--no drama to the user. ScalaJack requires -zero boilerplate--you can throw any Scala object (or even a Java object) at it with no pre-preparation -and it will serialize it. For its intended use-cases, out-of-the-box ScalaJack 8 offers excellent -performance, equal to or exceeding a number of widely-used alternative choices. - -If you're willing to suffer just 1 single line of boilerplate, ScalaJack 8 will reward you with -speed that's in the top one or two of its class ("fast mode" in the results). - ### Technical Notes -Achieving extreme speed for ScalaJack 8 was several weeks of learning, trial, error, +Achieving extreme speed for ScalaJack 8 was weeks of learning, trial, error, and re-writes. I studied Jsoniter, Circe, ZIO Json, and others to learn optimizations. The tough news for anyone wanting to duplicate this kind of performance in your own code is that there isn't one magic trick to achieve maximum performance. It's a basket @@ -96,6 +87,12 @@ enough is enough for you. Here's a partial list of learnings incorporated into * Lots of specific typing. Don't make the compiler think--provide detailed types wherever you can +* In some cases (though not all) a series of if/else if/else statements is faster than + match/case statements + +* A carefully-crafted @tailrec function is often faster than a function driven by a + while loop. + * For macro-based software like this--find every opportunity to do hard work at compile-time @@ -103,9 +100,20 @@ enough is enough for you. Here's a partial list of learnings incorporated into splices, like the documentaion and blogs suggest, and you will get something working. When you examine the code a "stock" macro use produces, you may be disappointed if ultimate runtime speed is your goal. Then generated code might look a litle kludgy, and - it will not necessarily be speed optimized. Rework your macros carefully until the generated code + it will likely not be speed-optimized. Rework your macros carefully until the generated code is as smooth as you might write by hand. Remember: your macro code doesn't have to win awards for style or beauty--your generated code does! For the fastest performance you'll actually have to generate custom functions as shown in ScalaJack's code (look at JsonCodecMaker.scala) This isn't for the faint of heart. If it all looks like Greek, step back and layer yourself into macros slowly a piece at a time. + +After all the performance tunings and learnings, I was able to meet or beat Jsoniter for writing +speed, putting ScalaJack at the top of the pile. For reading I made a number of substantial +improvements that placed ScalaJack a strong 2nd place under Jsoniter. There is still a +substantial performance gap between Jsoniter's reads and ScalaJack's, and for the life of me I can't +figure out what's driving those gains. The generated code is very similar. Json parsing should +be similar--in fact in some ways ScalaJack's should be faster. Although micro-benchmarks indicate +match/case is significantly slower, in practice replacing these with if/else didn't gain ScalaJack +a thing--which I find vexing. Jsoniter has some tricky ByteArrayAccess code that looks very +low-level and clever, but when I benchmarked it, the gains seemed moninal to none in my use case. +I dunno--If anyone has any ideas, please drop a comment in the Issues in repo! \ No newline at end of file diff --git a/benchmark/ReadingPerformance.png b/benchmark/ReadingPerformance.png new file mode 100644 index 0000000000000000000000000000000000000000..263a26c21e4be797c1bbf2ab265d61b90bcc2205 GIT binary patch literal 69457 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 K&t;ucLK6U_3b2a+ literal 0 HcmV?d00001 diff --git a/benchmark/WritingPerformance.png b/benchmark/WritingPerformance.png index db8770c5f74e8729e33c395a91eaa138f728c9bf..1ef505c966450367b97c4241eb9dc97f92e4d975 100644 GIT binary patch literal 81142 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 ecIAu8EPrS-97dYr<;Q(cIPn<;08L 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 diff --git a/benchmark/build.sbt b/benchmark/build.sbt index cb7d6d5f..c3028a6d 100644 --- a/benchmark/build.sbt +++ b/benchmark/build.sbt @@ -37,7 +37,7 @@ lazy val benchmark = project libraryDependencies ++= Seq( "org.playframework" %% "play-json" % "3.0.1", "io.argonaut" %% "argonaut" % "6.3.9", - "co.blocke" %% "scalajack" % "b08f70_unknown", + "co.blocke" %% "scalajack" % "b19ecd_unknown", "co.blocke" %% "scala-reflection" % "sj_fixes_f43af7", "dev.zio" %% "zio-json" % "0.6.1", "org.typelevel" %% "fabric-core" % "1.12.6", diff --git a/benchmark/foo b/benchmark/foo deleted file mode 100644 index 66e44bea..00000000 --- a/benchmark/foo +++ /dev/null @@ -1,98 +0,0 @@ -SCALAJACK: - - def r1(`in₁₀`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.Person = { - var _name: java.lang.String = "" - var _age: scala.Int = 0 - var _address: co.blocke.Address = null - var _email: java.lang.String = "" - var _phone_numbers: scala.collection.immutable.List[scala.Predef.String] = null - var _is_employed: scala.Boolean = false - var `maybeFname₂`: scala.Option[java.lang.CharSequence] = `in₁₀`.expectFirstObjectField() - if (`maybeFname₂`.==(null)) null.asInstanceOf[co.blocke.Person] else { - while (`maybeFname₂`.isDefined) { - `maybeFname₂`.get match { - case "name" => - _name = `in₁₀`.expectString().toString() - case "age" => - _age = `in₁₀`.expectInt() - case "address" => - _address = r2(`in₁₀`) - case "email" => - _email = `in₁₀`.expectString().toString() - case "phone_numbers" => - _phone_numbers = { - val parsedArray: scala.collection.mutable.ListBuffer[java.lang.String] = `in₁₀`.expectArray[java.lang.String]((() => `in₁₀`.expectString().toString()) - if (parsedArray.==(null)) null.asInstanceOf[scala.collection.immutable.List[scala.Predef.String]] else parsedArray.to[scala.collection.immutable.List[scala.Predef.String]](scala.collection.immutable.List.iterableFactory[java.lang.String]) - } - case "is_employed" => - _is_employed = `in₁₀`.expectBoolean() - case _ => - `in₁₀`.skipValue() - } - `maybeFname₂` = `in₁₀`.expectObjectField() - } - new co.blocke.Person(_name, _age, _address, _email, _phone_numbers, _is_employed) - } - } - - -JSONITER: - - def d6(`in`: com.github.plokhotnyuk.jsoniter_scala.core.JsonReader, `default₇`: co.blocke.Person): co.blocke.Person = if (`in`.isNextToken(123)) { - var `_name₃`: java.lang.String = (null: java.lang.String) - var `_age₃`: scala.Int = 0 - var _address: co.blocke.Address = null - var `_email₂`: java.lang.String = (null: java.lang.String) - var _phone_numbers: scala.collection.immutable.List[scala.Predef.String] = scala.Nil - var _is_employed: scala.Boolean = false - var `p0₄`: scala.Int = 63 - if (!in.isNextToken(125)) { - in.rollbackToken() - var `l₄`: scala.Int = -1 - while (l₄ < 0 || in.isNextToken(44)) { - `l₄` = `in`.readKeyAsCharBuf() - if (`in`.isCharBufEqualsTo(`l₄`, "name")) { - if (`p0₄`.&(1).!=(0)) `p0₄` = `p0₄`.^(1) else `in`.duplicatedKeyError(`l₄`) - `_name₃` = `in`.readString(`_name₃`) - } else if (`in`.isCharBufEqualsTo(`l₄`, "age")) { - if (`p0₄`.&(2).!=(0)) `p0₄` = `p0₄`.^(2) else `in`.duplicatedKeyError(`l₄`) - `_age₃` = `in`.readInt() - } else if (`in`.isCharBufEqualsTo(`l₄`, "address")) { - if (`p0₄`.&(4).!=(0)) `p0₄` = `p0₄`.^(4) else `in`.duplicatedKeyError(`l₄`) - _address = d7(`in`, _address) - } else if (`in`.isCharBufEqualsTo(`l₄`, "email")) { - if (`p0₄`.&(8).!=(0)) `p0₄` = `p0₄`.^(8) else `in`.duplicatedKeyError(`l₄`) - `_email₂` = `in`.readString(`_email₂`) - } else if (`in`.isCharBufEqualsTo(`l₄`, "phone_numbers")) { - if (`p0₄`.&(16).!=(0)) `p0₄` = `p0₄`.^(16) else `in`.duplicatedKeyError(`l₄`) - _phone_numbers = d5(`in`, _phone_numbers) - } else if (`in`.isCharBufEqualsTo(`l₄`, "is_employed")) { - if (`p0₄`.&(32).!=(0)) `p0₄` = `p0₄`.^(32) else `in`.duplicatedKeyError(`l₄`) - _is_employed = `in`.readBoolean() - } else `in`.skip() - } - if (!in.isCurrentToken(125)) in.objectEndOrCommaError() else () - } else () - if (`p0₄`.&(47).!=(0)) `in`.requiredFieldError(f3(java.lang.Integer.numberOfTrailingZeros(`p0₄`.&(47)))) else () - new co.blocke.Person(`_name₃`, `_age₃`, _address, `_email₂`, _phone_numbers, _is_employed) - } else `in`.readNullOrTokenError[co.blocke.Person](`default₇`, 123) - - // List reading thingy... Note, no Seq-type coersion happening... - def d5(in: JsonReader, default: List[String]): List[String] = - if (in.isNextToken(91)) - if (in.isNextToken(93)) - default - else { - in.rollbackToken() - val x: scala.collection.mutable.ListBuffer[String] = new scala.collection.mutable.ListBuffer[String]() - while ({ - x.addOne(in.readString((null: String))) - in.isNextToken(44) - }) () - if (in.isCurrentToken(93)) - x.toList - else - in.arrayEndOrCommaError() - } - else - in.readNullOrTokenError[List[String]](default, 91) diff --git a/benchmark/src/main/scala/co.blocke/Benchmark.scala b/benchmark/src/main/scala/co.blocke/Benchmark.scala index a82ad557..cfb05f5a 100644 --- a/benchmark/src/main/scala/co.blocke/Benchmark.scala +++ b/benchmark/src/main/scala/co.blocke/Benchmark.scala @@ -66,13 +66,12 @@ class WritingBenchmark /* LATEST RUN: -[info] Benchmark Mode Cnt Score Error Units -[info] ReadingBenchmark.readRecordArgonaut thrpt 20 185186.226 ± 16189.626 ops/s -[info] ReadingBenchmark.readRecordCirce thrpt 20 281382.258 ± 4449.888 ops/s -[info] ReadingBenchmark.readRecordJsoniter thrpt 20 1327809.608 ± 26901.514 ops/s -[info] ReadingBenchmark.readRecordPlay thrpt 20 209035.195 ± 671.300 ops/s -[info] ReadingBenchmark.readRecordScalaJack thrpt 20 740350.521 ± 190132.195 ops/s -[info] ReadingBenchmark.readRecordZIOJson thrpt 20 584964.240 ± 552.648 ops/s -[info] WritingBenchmark.writeRecordScalaJack thrpt 20 352825.894 ± 7772.927 ops/s +[info] Benchmark Mode Cnt Score Error Units +[info] ReadingBenchmark.readRecordJsoniter thrpt 20 1346388.345 ± 17028.863 ops/s +[info] ReadingBenchmark.readRecordScalaJack thrpt 20 941679.824 ± 58208.523 ops/s +[info] ReadingBenchmark.readRecordZIOJson thrpt 20 590995.917 ± 817.817 ops/s +[info] ReadingBenchmark.readRecordCirce thrpt 20 210805.946 ± 32488.564 ops/s +[info] ReadingBenchmark.readRecordPlay thrpt 20 198747.067 ± 7253.896 ops/s +[info] ReadingBenchmark.readRecordArgonaut thrpt 20 183670.032 ± 8981.485 ops/s */ diff --git a/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala b/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala index 26a69479..de57cb06 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala @@ -81,6 +81,26 @@ object JsonCodecMaker: ) '{} + val classFieldMatrixSyms = new scala.collection.mutable.HashMap[MethodKey, Symbol] + val classFieldMatrixValDefs = new scala.collection.mutable.ArrayBuffer[ValDef] + + def makeClassFieldMatrixValDef(methodKey: MethodKey, className: String, fieldNames: Array[String])(using Quotes): Expr[Unit] = + classFieldMatrixSyms.getOrElse( + methodKey, { + val sym = Symbol.newVal( + Symbol.spliceOwner, + s"__$className" + "_fields", + TypeRepr.of[StringMatrix], + Flags.EmptyFlags, + Symbol.noSymbol + ) + classFieldMatrixSyms.update(methodKey, sym) + val names = Expr(fieldNames) + classFieldMatrixValDefs += ValDef(sym, Some('{ new StringMatrix($names) }.asTerm)) + } + ) + '{} + // --------------------------------------------------------------------------------------------- def maybeWrite[T](label: String, aE: Expr[T], ref: RTypeRef[T], out: Expr[JsonOutput], cfg: JsonConfig): Expr[Unit] = @@ -807,6 +827,7 @@ object JsonCodecMaker: var required = 0 val reqSym = Symbol.newVal(Symbol.spliceOwner, "required", TypeRepr.of[Int], Flags.Mutable, Symbol.noSymbol) val allFieldNames = Expr(t.fields.map(_.name).toArray) // Used for missing required field error + val together = t.fields.map { oneField => oneField.fieldRef.refType match { case '[f] => @@ -815,16 +836,17 @@ object JsonCodecMaker: val fieldSymRef = Ident(sym.termRef) val reqBit = Expr(math.pow(2, oneField.index).toInt) val fieldName = Expr(oneField.name) + val caseDef = CaseDef( - Literal(StringConstant(oneField.name)), + Literal(IntConstant(oneField.index)), None, - Assign(fieldSymRef, genReadVal[f](oneField.fieldRef.asInstanceOf[RTypeRef[f]], in).asTerm) - // '{ - // if (${ Ref(reqSym).asExprOf[Int] } & $reqBit) != 0 then - // ${ Assign(Ref(reqSym), '{ ${ Ref(reqSym).asExprOf[Int] } ^ $reqBit }.asTerm).asExprOf[Unit] } - // ${ Assign(fieldSymRef, genReadVal[f](oneField.fieldRef.asInstanceOf[RTypeRef[f]], in).asTerm).asExprOf[Unit] } - // else throw new JsonParseError("Duplicate field " + $fieldName, $in) - // }.asTerm + // Assign(fieldSymRef, genReadVal[f](oneField.fieldRef.asInstanceOf[RTypeRef[f]], in).asTerm) + '{ + if (${ Ref(reqSym).asExprOf[Int] } & $reqBit) != 0 then + ${ Assign(Ref(reqSym), '{ ${ Ref(reqSym).asExprOf[Int] } ^ $reqBit }.asTerm).asExprOf[Unit] } + ${ Assign(fieldSymRef, genReadVal[f](oneField.fieldRef.asInstanceOf[RTypeRef[f]], in).asTerm).asExprOf[Unit] } + else throw new JsonParseError("Duplicate field " + $fieldName, $in) + }.asTerm ) if dvMembers.isEmpty then (ValDef(sym, Some(oneField.fieldRef.unitVal.asTerm)), caseDef, fieldSymRef) if dvMembers.isEmpty then @@ -861,39 +883,23 @@ object JsonCodecMaker: val exprRequired = Expr(required) + makeClassFieldMatrixValDef(MethodKey(t, false), t.name.replaceAll("\\.", "_"), t.fields.map(_.name).toArray) + val fieldMatrixSym = classFieldMatrixSyms(MethodKey(t, false)).asInstanceOf[Symbol] + val parseLoop = '{ - var maybeFname = $in.expectFirstObjectField() - if maybeFname == null then null.asInstanceOf[T] + var maybeFieldNum = $in.expectFirstObjectField(${ Ref(fieldMatrixSym).asExprOf[StringMatrix] }) + if maybeFieldNum == null then null.asInstanceOf[T] else - while maybeFname.isDefined do - ${ Match('{ maybeFname.get }.asTerm, caseDefsWithFinal).asExprOf[Any] } - maybeFname = $in.expectObjectField() - ${ instantiateClass.asExprOf[T] } + while maybeFieldNum.isDefined do + ${ Match('{ maybeFieldNum.get }.asTerm, caseDefsWithFinal).asExprOf[Any] } + maybeFieldNum = $in.expectObjectField(${ Ref(fieldMatrixSym).asExprOf[StringMatrix] }) + if (${ Ref(reqSym).asExprOf[Int] } & ${ exprRequired }) == 0 then ${ instantiateClass.asExprOf[T] } + else throw new JsonParseError("Missing required field(s) " + ${ allFieldNames }(Integer.numberOfTrailingZeros(${ Ref(reqSym).asExprOf[Int] } & ${ exprRequired })), $in) + // ${ instantiateClass.asExprOf[T] } }.asTerm - // val parseLoop = '{ - // if ! $in.expectObjectOrNull() then null.asInstanceOf[T] - // else - // var c = $in.readCharWS() - // while c match { - // case '"' => - // ${ Match('{ $in.expectObjectField() }.asTerm, caseDefsWithFinal).asExprOf[Any] } - // c = $in.readCharWS() - // if c == ',' then - // c = $in.readCharWS() - // true - // else true - // case '}' => false - // case c => throw new JsonParseError(s"Expected '\"' or '}' here but found '$c'", $in) - // } - // do () - // ${ instantiateClass.asExprOf[T] } - // // if (${ Ref(reqSym).asExprOf[Int] } & ${ exprRequired }) == 0 then ${ instantiateClass.asExprOf[T] } - // // else throw new JsonParseError("Missing required field(s) " + ${ allFieldNames }(Integer.numberOfTrailingZeros(${ Ref(reqSym).asExprOf[Int] } & ${ exprRequired })), $in) - // }.asTerm - - Block(varDefs, parseLoop).asExprOf[T] - // Block(varDefs :+ reqVarDef, parseLoop).asExprOf[T] + // Block(varDefs, parseLoop).asExprOf[T] + Block(varDefs :+ reqVarDef, parseLoop).asExprOf[T] ) case _ => ??? @@ -1056,7 +1062,7 @@ object JsonCodecMaker: }.asTerm val neededDefs = // others here??? Refer to Jsoniter file JsonCodecMaker.scala - writeMethodDefs ++ readMethodDefs + classFieldMatrixValDefs ++ writeMethodDefs ++ readMethodDefs val codec = Block(neededDefs.toList, codecDef).asExprOf[JsonCodec[T]] println(s"Codec: ${codec.show}") codec diff --git a/src/main/scala/co.blocke.scalajack/json/StringMatrix.scala b/src/main/scala/co.blocke.scalajack/json/StringMatrix.scala new file mode 100644 index 00000000..2d0ee485 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/StringMatrix.scala @@ -0,0 +1,84 @@ +package co.blocke.scalajack +package json + +// A data structure encoding a simple algorithm for Trie pruning: Given a list +// of strings, and a sequence of incoming characters, find the strings that +// match, by manually maintaining a bitset. Empty strings are not allowed. +// +// ScalaJack: Removal of ZIO's original aliases feature for speed--we don't need this. +// +final class StringMatrix(val xs: Array[String]) { + require(xs.forall(_.nonEmpty)) + require(xs.nonEmpty) + require(xs.length < 64) + + val width = xs.length + val height: Int = xs.map(_.length).max + val lengths: Array[Int] = xs.map(_.length) + val initial: Long = (0 until width).foldLeft(0L)((bs, r) => bs | (1L << r)) + + private val matrix: Array[Int] = { + val m = Array.fill[Int](width * height)(-1) + var string: Int = 0 + while string < width do { + val s = xs(string) + val len = s.length + var char: Int = 0 + while char < len do { + m(width * char + string) = s.codePointAt(char) + char += 1 + } + string += 1 + } + m + } + + private val resolve: Array[Int] = Array.tabulate[Int](xs.length)(identity) + + // must be called with increasing `char` (starting with bitset obtained from a + // call to 'initial', char = 0) + def update(bitset: Long, char: Int, c: Int): Long = + if char >= height then 0L // too long + else if bitset == 0L then 0L // everybody lost + else { + var latest: Long = bitset + val base: Int = width * char + + if bitset == initial then { // special case when it is dense since it is simple + var string: Int = 0 + while string < width do { + if matrix(base + string) != c then latest = latest ^ (1L << string) + string += 1 + } + } else { + var remaining: Long = bitset + while remaining != 0L do { + val string: Int = java.lang.Long.numberOfTrailingZeros(remaining) + val bit: Long = 1L << string + if matrix(base + string) != c then latest = latest ^ bit + remaining = remaining ^ bit + } + } + + latest + } + + // excludes entries that are not the given exact length + def exact(bitset: Long, length: Int): Long = + if length > height then 0L // too long + else { + var latest: Long = bitset + var remaining: Long = bitset + while remaining != 0L do { + val string: Int = java.lang.Long.numberOfTrailingZeros(remaining) + val bit: Long = 1L << string + if lengths(string) != length then latest = latest ^ bit + remaining = remaining ^ bit + } + latest + } + + def first(bitset: Long): Int = + if bitset == 0L then -1 + else resolve(java.lang.Long.numberOfTrailingZeros(bitset)) // never returns 64 +} \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala b/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala index 81065b47..2cdd497d 100644 --- a/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala +++ b/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala @@ -6,7 +6,7 @@ import scala.annotation.{switch, tailrec} import co.blocke.scalajack.json.reading.SafeNumbers.double object JsonSource: - protected val ull: Array[Char] = "ull".toCharArray + val ull: Array[Char] = "ull".toCharArray protected val alse: Array[Char] = "alse".toCharArray protected val rue: Array[Char] = "rue".toCharArray protected val falseBytes = 'f' | 'a' << 8 | 'l' << 16 | 's' << 24 | 'e' << 32 @@ -39,7 +39,7 @@ case class JsonSource(js: CharSequence): c == ' ' || c == '\n' || (c | 0x4) == '\r' || c == '\t' @tailrec - final private[this] def readToken(): Char = + final def readToken(): Char = if i == max then throw new JsonParseError("Unexpected end of buffer", this) else val b = here @@ -92,16 +92,13 @@ case class JsonSource(js: CharSequence): accum.toChar // returns false if 'null' found - def expectFirstObjectField(): Option[CharSequence] = + def expectFirstObjectField(fieldNameMatrix: StringMatrix): Option[Int] = val t = readToken() if t == '{' then val tt = readToken() if tt == '"' then - val endI = parseString(i) - val fname = js.subSequence(i, endI) - i = endI + 1 - if readToken() != ':' then throw new JsonParseError(s"Expected ':' field separator", this) - Some(fname) + val foundIndex = parseFieldName(fieldNameMatrix) + Some(foundIndex) else if tt == '}' then None else throw new JsonParseError(s"Expected object field name or '}' but found '$tt'", this) else if t == 'n' then @@ -109,19 +106,17 @@ case class JsonSource(js: CharSequence): null else throw new JsonParseError(s"Expected object start '{' or null", this) - def expectObjectField(): Option[CharSequence] = + def expectObjectField(fieldNameMatrix: StringMatrix): Option[Int] = val t = readToken() if t == ',' then val tt = readToken() if tt == '"' then - val endI = parseString(i) - val fname = js.subSequence(i, endI) - i = endI + 1 - if readToken() != ':' then throw new JsonParseError(s"Expected ':' field separator", this) - Some(fname) + val foundIndex = parseFieldName(fieldNameMatrix) + Some(foundIndex) else throw new JsonParseError(s"Expected object field name but found '$tt'", this) else if t == '}' then None else throw new JsonParseError(s"Expected ',' or '}' but found $t", this) + // returns false if 'null' found @tailrec final private[this] def addAllArray[E](s: scala.collection.mutable.ListBuffer[E], f: () => E): scala.collection.mutable.ListBuffer[E] = @@ -206,9 +201,11 @@ case class JsonSource(js: CharSequence): val t = readToken() if t == '"' then val endI = parseString(i) - val str = js.subSequence(i, endI) - i = endI + 1 - str + if endI >= 0 then + val str = js.subSequence(i, endI) + i = endI + 1 + str + else ??? // slower-parseString looking for escaped special chars else if t == 'n' then readChars(JsonSource.ull, "null") null @@ -222,15 +219,30 @@ case class JsonSource(js: CharSequence): if mask != 0 then { val offset = java.lang.Integer.numberOfTrailingZeros(mask) >> 3 if (bs >> (offset << 3)).toByte == '"' then pos + offset - else throw new Exception("special chars found 1") // else parseEncodedString(i + offset, charBuf.length - 1, charBuf, pos + offset) + else -1 // special char found } else parseString(pos + 4) else if pos == max then throw new Exception("Unterminated string value") else val b = js.charAt(pos) if b == '"' then pos - else if (b - 0x20 ^ 0x3c) <= 0 then throw new Exception("special chars found 2") // parseEncodedString(i, charBuf.length - 1, charBuf, pos) + else if (b - 0x20 ^ 0x3c) <= 0 then -1 // special char found else parseString(pos + 1) + final def parseFieldName(fieldNameMatrix: StringMatrix): Int = // returns index of field name or -1 if not found + var fi: Int = 0 + var bs: Long = fieldNameMatrix.initial + var c: Int = here + while c != '"' do { + bs = fieldNameMatrix.update(bs, fi, c) + fi += 1 + i += 1 + c = here + } + i += 1 + bs = fieldNameMatrix.exact(bs, fi) + if readToken() != ':' then throw new JsonParseError(s"Expected ':' field separator but found $here", this) + fieldNameMatrix.first(bs) + inline def expectChar(): Char = expectString() match { case s if s.length == 1 => s.charAt(0) @@ -248,14 +260,30 @@ case class JsonSource(js: CharSequence): false else throw JsonParseError(s"Expected 'true' or 'false'", this) - def expectInt(): Int = - skipWS() - if !isNumber(here) then throw JsonParseError(s"Expected a number, got $c", this) - val intRead = UnsafeNumbers.int_(this, false) + def expectInt(): Int = { + var b = readCharWS() + var s = -1 + if here == '-' then { + readChar() + s = 0 + } + if b < '0' || b > '9' then throw new Exception("Boom 1") + var x = '0' - b + while { b = readChar(); b >= '0' && b <= '9' } do + if x < -214748364 || { + x = x * 10 + ('0' - b) + x > 0 + } + then throw new Exception("Boom 2") + x ^= s + x -= s + if (s & x) == -2147483648 then throw new Exception("Boom 3") + if (b | 0x20) == 'e' || b == '.' then throw new Exception("Boom 4") retract() - intRead + x + } - private inline def readChars( + inline def readChars( expect: Array[Char], errMsg: String ): Unit = diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index 173f8f84..5f2838f1 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -55,6 +55,11 @@ object RunMe extends App: implicit val blah: ScalaJack[Record] = sj[Record] println(blah.fromJson(jsData)) + println("===================") + + // val c = new co.blocke.scalajack.run.Codec() + // println(c.decodeValue(co.blocke.scalajack.json.reading.JsonSource(jsData))) + println("done.") /* From 5dbb0ce8b16b61194a7e2ff88878e09e61d7a3e8 Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Thu, 14 Mar 2024 13:59:05 -0500 Subject: [PATCH 48/65] More write performance improvements --- benchmark/README.md | 48 +-- benchmark/WritingPerformance.png | Bin 81142 -> 86164 bytes benchmark/build.sbt | 3 +- .../src/main/scala/co.blocke/Record.scala | 2 +- benchmark/src/main/scala/co.blocke/Run.scala | 21 ++ benchmark/src/main/scala/co.blocke/Run.scalax | 44 --- .../src/main/scala/co.blocke/ScalaJack.scala | 9 +- build.sbt | 4 +- .../schema/additionalProperties.java | 9 + .../blocke/scalajack/schema/description.java | 9 + .../scalajack/schema/exclusiveMaximum.java | 9 + .../scalajack/schema/exclusiveMinimum.java | 9 + .../co/blocke/scalajack/schema/format.java | 9 + .../java/co/blocke/scalajack/schema/id.java | 9 + .../co/blocke/scalajack/schema/items.java | 9 + .../co/blocke/scalajack/schema/maxItems.java | 9 + .../co/blocke/scalajack/schema/maxLength.java | 9 + .../co/blocke/scalajack/schema/maximum.java | 9 + .../co/blocke/scalajack/schema/minItems.java | 9 + .../co/blocke/scalajack/schema/minLength.java | 9 + .../co/blocke/scalajack/schema/minimum.java | 9 + .../blocke/scalajack/schema/multipleOf.java | 9 + .../co/blocke/scalajack/schema/pattern.java | 9 + .../co/blocke/scalajack/schema/title.java | 9 + .../blocke/scalajack/schema/uniqueItems.java | 9 + .../scala/co.blocke.scalajack/ScalaJack.scala | 29 -- .../json/FastStringBuilder.scala | 119 +++++++ .../json/JsonCodecMaker.scala | 47 +-- .../co.blocke.scalajack/json/JsonConfig.scala | 32 +- .../co.blocke.scalajack/json/JsonError.scala | 1 + .../json/StringMatrix.scala | 2 +- .../co.blocke.scalajack/json/package.scala | 2 + .../json/schema/JsonSchema.scala | 293 ++++++++++++++++++ .../json/schema/Schema.scala | 115 +++++++ .../json/writing/JsonOutput.scala | 235 +++++++++++--- .../scala/co.blocke.scalajack/run/Play.scala | 110 +------ .../co.blocke.scalajack/run/Record.scala | 36 ++- .../json/misc/MiscTests.scala | 19 +- .../json/primitives/Model.scala | 2 + .../json/primitives/SimpleSpec.scala | 13 + 40 files changed, 1045 insertions(+), 294 deletions(-) create mode 100644 benchmark/src/main/scala/co.blocke/Run.scala delete mode 100644 benchmark/src/main/scala/co.blocke/Run.scalax create mode 100644 src/main/java/co/blocke/scalajack/schema/additionalProperties.java create mode 100644 src/main/java/co/blocke/scalajack/schema/description.java create mode 100644 src/main/java/co/blocke/scalajack/schema/exclusiveMaximum.java create mode 100644 src/main/java/co/blocke/scalajack/schema/exclusiveMinimum.java create mode 100644 src/main/java/co/blocke/scalajack/schema/format.java create mode 100644 src/main/java/co/blocke/scalajack/schema/id.java create mode 100644 src/main/java/co/blocke/scalajack/schema/items.java create mode 100644 src/main/java/co/blocke/scalajack/schema/maxItems.java create mode 100644 src/main/java/co/blocke/scalajack/schema/maxLength.java create mode 100644 src/main/java/co/blocke/scalajack/schema/maximum.java create mode 100644 src/main/java/co/blocke/scalajack/schema/minItems.java create mode 100644 src/main/java/co/blocke/scalajack/schema/minLength.java create mode 100644 src/main/java/co/blocke/scalajack/schema/minimum.java create mode 100644 src/main/java/co/blocke/scalajack/schema/multipleOf.java create mode 100644 src/main/java/co/blocke/scalajack/schema/pattern.java create mode 100644 src/main/java/co/blocke/scalajack/schema/title.java create mode 100644 src/main/java/co/blocke/scalajack/schema/uniqueItems.java create mode 100644 src/main/scala/co.blocke.scalajack/json/FastStringBuilder.scala create mode 100644 src/main/scala/co.blocke.scalajack/json/schema/JsonSchema.scala create mode 100644 src/main/scala/co.blocke.scalajack/json/schema/Schema.scala diff --git a/benchmark/README.md b/benchmark/README.md index be73924b..0961ff03 100644 --- a/benchmark/README.md +++ b/benchmark/README.md @@ -31,6 +31,7 @@ sbt "jmh:run -i 10 -wi 10 -f 2 -t 1 co.blocke.*" | Benchmark | Mode | Count | Score | Error | Units | |------------------|-------|-------:|----------------:|-------------:|-------| +|**ScalaJack 8 no escaped chars in String** | thrpt | 20 | **5200691.37** | ± 114219.728 | ops/s | |**ScalaJack 8** | thrpt | 20 | **3039273.222** | ± 14952.932 | ops/s | | Jsoniter | thrpt | 20 | 2843150.452 | ± 21478.503 | ops/s | | Hand-Tooled | thrpt | 20 | 2732571.374 | ± 15129.007 | ops/s | @@ -43,12 +44,20 @@ sbt "jmh:run -i 10 -wi 10 -f 2 -t 1 co.blocke.*" used. The important thing is the relative relationship between libraries given all tests were performed on the same platform. +**Note:** That extreme write speed for ScalaJack 8 is achieved by disabling escaped/special character +processing using +```scala +sj[Foo](JsonConfig.withSuppressEscapedStrings()) +``` +Its for when you're 100% sure there's 0 chance of any double-quotes, newlines, tabs, or +any other non-character/digit unicode special characters in your String values, and you need the +maximum possible performance. + ### Interpretation Performance for ScalaJack has been a journey. ScalaJack is a mature product--over 10 yrs old. -Long ago it was quite fast vs its competition, but over the years its performance lagged -considerably as its peers improved, to the point that it was one of the slower serialization -libraries. ScalaJack 8 changes that! +Long ago it was quite fast vs its competition, but its performance lagged as its peers improved, +to the point that it became one of the slower serialization libraries. ScalaJack 8 changes that! I was sampling and testing against a collection of popular serializers for Scala util something quite unexpected happend: I discovered Jsoniter. Its performance was through @@ -66,9 +75,8 @@ Of course ScalaJack utilizes our own macro-driven scala-reflection library to gr which Jsoniter does not. Jsoniter achieves its neck-breaking speed by going deep--very deep into macro code -generation. They also use a lot of low level byte arrays and bitwise operators, much as you'd -expect to see in a C program, to improve on the standard library functions everyone else uses. -It works. +generation. They also use a lot of low-level byte arrays and bitwise operators +to improve on the standard library functions everyone else uses. It works. ### Technical Notes @@ -96,24 +104,18 @@ enough is enough for you. Here's a partial list of learnings incorporated into * For macro-based software like this--find every opportunity to do hard work at compile-time -* Be mindful of what code your macros generate! You can paint by the numbers with quotes and - splices, like the documentaion and blogs suggest, and you will get something working. - When you examine the code a "stock" macro use produces, you may be disappointed - if ultimate runtime speed is your goal. Then generated code might look a litle kludgy, and - it will likely not be speed-optimized. Rework your macros carefully until the generated code - is as smooth as you might write by hand. Remember: your macro code doesn't have to win awards for - style or beauty--your generated code does! For the fastest performance you'll actually have - to generate custom functions as shown in ScalaJack's code (look at JsonCodecMaker.scala) This - isn't for the faint of heart. If it all looks like Greek, step back and layer yourself into - macros slowly a piece at a time. +* Be mindful of what code your macros generate! You can write a macro straight from + a blog example and get something working, but you may be disappointed if maximizing runtime + speed is your goal. The generated code might look kludgy. Rework your macros carefully + until the generated code is as smooth as you might write by hand. After all the performance tunings and learnings, I was able to meet or beat Jsoniter for writing -speed, putting ScalaJack at the top of the pile. For reading I made a number of substantial -improvements that placed ScalaJack a strong 2nd place under Jsoniter. There is still a +speed. For reading I made a number of very substantial improvements, but there is still a substantial performance gap between Jsoniter's reads and ScalaJack's, and for the life of me I can't -figure out what's driving those gains. The generated code is very similar. Json parsing should +figure out what's driving those gains! The generated code is very similar. Json parsing should be similar--in fact in some ways ScalaJack's should be faster. Although micro-benchmarks indicate -match/case is significantly slower, in practice replacing these with if/else didn't gain ScalaJack -a thing--which I find vexing. Jsoniter has some tricky ByteArrayAccess code that looks very -low-level and clever, but when I benchmarked it, the gains seemed moninal to none in my use case. -I dunno--If anyone has any ideas, please drop a comment in the Issues in repo! \ No newline at end of file +match/case is significantly slower in generated code than if/than/else, but in practice replacing +these with if/else didn't gain ScalaJack a thing--which I find vexing. Jsoniter has some tricky +ByteArrayAccess code that looks very low-level and clever, but when I benchmarked it, the gains +seemed nominal to none in my use case. I dunno--If anyone has any ideas, please drop a comment +in the Issues in repo! \ No newline at end of file diff --git a/benchmark/WritingPerformance.png b/benchmark/WritingPerformance.png index 1ef505c966450367b97c4241eb9dc97f92e4d975..34e685bc71f3ce991d8e5a1bde1c2e170bed09f6 100644 GIT binary patch literal 86164 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%0A$Bbf_ziR5f6%ycCh}929grzQ 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 Z<`YKg>7*H&O@apeLFHBCN@NWD{}=25>IVP- literal 81142 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 8) + // val d2 = ds(ch & 0xFF) << 16 + // ByteArrayAccess.setInt(buf, pos + 2, d1 | d2) + // pos + 6 + // } + + private[this] def appendEscapedUnicode(c: Char): Unit = { + append('\\') + append('u') + append("%04x".format(c.toInt)) + i += 6 + } + + @tailrec + final def appendEscaped(s: String, from: Int, to: Int): Unit = + ensureCapacity(2) + if (from >= to) () + else + val ch1 = s.charAt(from) + if ch1 < 0x80 then + val esc = escapedChars(ch1) + if (esc == 0) + chars(i) = ch1 + i += 1 + appendEscaped(s, from + 1, to) + else if esc > 0 then + chars(i) = 0x5C + chars(i+1) = esc.toChar + i += 2 + appendEscaped(s, from + 1, to) + else + appendEscapedUnicode(ch1) + else if ((ch1 & 0xF800) != 0xD800) then + appendEscapedUnicode(ch1) + else + var ch2 = 0 + if (ch1 >= 0xDC00 || from + 1 >= to || { + ch2 = s.charAt(from + 1).toInt + (ch2 & 0xFC00) != 0xDC00 + }) throw new JsonIllegalCharacterError("Illegal encoded text character in string value: "+ch2) + appendEscapedUnicode(ch2.toChar) + + def append(s: String): Unit = + ensureCapacity(s.length) + s.getChars(0, s.length, chars, i) + i += s.length + + def append(v: scala.math.BigDecimal): Unit = append( v.toString ) + def append(v: scala.math.BigInt): Unit = append( v.toString ) + def append(v: Boolean): Unit = append( v.toString ) + def append(v: Double): Unit = append( v.toString ) + def append(v: Float): Unit = append( v.toString ) + def append(v: Int): Unit = append( v.toString ) + def append(v: Long): Unit = append( v.toString ) + def append(v: Short): Unit = append( v.toString ) + def append(v: java.lang.Number): Unit = append( v.toString ) + + def result = CharBuffer.wrap(chars, 0, i).toString +} \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala b/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala index de57cb06..ca6234cf 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala @@ -488,15 +488,17 @@ object JsonCodecMaker: // generate a match/case statement that in turn generates render functions for each // child of the sealed trait. // Refer to Jsoniter: JsonCodecMaker.scala around line 920 for example how to do this, incl a wildcard. - val cases = t.sealedChildren.map { child => - child.refType match - case '[c] => - val subtype = TypeIdent(TypeRepr.of[c].typeSymbol) - val sym = Symbol.newBind(Symbol.spliceOwner, "t", Flags.EmptyFlags, subtype.tpe) - CaseDef(Bind(sym, Typed(Ref(sym), subtype)), None, genEncFnBody[c](child, Ref(sym).asExprOf[c], out, true).asTerm) - } :+ CaseDef(Literal(NullConstant()), None, '{ $out.burpNull() }.asTerm) - val matchExpr = Match(aE.asTerm, cases).asExprOf[Unit] - matchExpr + // (f: (Expr[U], Expr[JsonOutput]) => Expr[Unit]) + makeWriteFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => + val cases = t.sealedChildren.map { child => + child.refType match + case '[c] => + val subtype = TypeIdent(TypeRepr.of[c].typeSymbol) + val sym = Symbol.newBind(Symbol.spliceOwner, "t", Flags.EmptyFlags, subtype.tpe) + CaseDef(Bind(sym, Typed(Wildcard(), Inferred(subtype.tpe))), None, genEncFnBody[c](child, Ref(sym).asExprOf[c], out, cfg.suppressTypeHints).asTerm) + } :+ CaseDef(Literal(NullConstant()), None, '{ $out.burpNull() }.asTerm) + Match(in.asTerm, cases).asExprOf[Unit] + } // No makeWriteFn here--Option is just a wrapper to the real thingy case t: OptionRef[?] => @@ -522,7 +524,8 @@ object JsonCodecMaker: case t: SelfRefRef[?] => t.refType match case '[e] => - val key = MethodKey(ReflectOnType[e](q)(TypeRepr.of[e])(using scala.collection.mutable.Map.empty[TypedName, Boolean]), false) + val ref = ReflectOnType[e](q)(TypeRepr.of[e])(using scala.collection.mutable.Map.empty[TypedName, Boolean]) + val key = MethodKey(ref, false) val sym = writeMethodSyms(key) val tin = aE.asExprOf[b] '{ @@ -693,8 +696,8 @@ object JsonCodecMaker: if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Short] }) } else '{ $out.value(${ aE.asExprOf[Short] }) } case t: StringRef => - if cfg.escapedStrings then '{ $out.value(StringEscapeUtils.escapeJson(${ aE.asExprOf[String] })) } - else '{ $out.value(${ aE.asExprOf[String] }) } + if cfg.suppressEscapedStrings then '{ $out.value(${ aE.asExprOf[String] }) } + else '{ $out.valueEscaped(${ aE.asExprOf[String] }) } case t: JBigDecimalRef => if isStringified then '{ $out.valueStringified(${ aE.asExprOf[java.math.BigDecimal] }) } @@ -745,13 +748,18 @@ object JsonCodecMaker: case t: ZoneIdRef => '{ $out.value(${ aE.asExprOf[java.time.ZoneId] }) } case t: ZoneOffsetRef => '{ $out.value(${ aE.asExprOf[java.time.ZoneOffset] }) } + case t: URLRef => '{ $out.value(${ aE.asExprOf[java.net.URL] }) } + case t: URIRef => '{ $out.value(${ aE.asExprOf[java.net.URI] }) } case t: UUIDRef => '{ $out.value(${ aE.asExprOf[java.util.UUID] }) } case t: ObjectRef => '{ $out.value(${ Expr(t.name) }) } case t: AliasRef[?] => - t.unwrappedType.refType match - case '[e] => - genWriteVal[e](aE.asInstanceOf[Expr[e]], t.unwrappedType.asInstanceOf[RTypeRef[e]], out, inTuple = inTuple) + // Special check for RawJson pseudo-type + if lastPart(t.definedType) == "RawJson" then '{ $out.valueRaw(${ aE.asExprOf[RawJson] }) } + else + t.unwrappedType.refType match + case '[e] => + genWriteVal[e](aE.asInstanceOf[Expr[e]], t.unwrappedType.asInstanceOf[RTypeRef[e]], out, inTuple = inTuple) // These one's here becaue Enums and their various flavors can be Map keys // (EnumRef handles: Scala 3 enum, Scala 2 Enumeration, Java Enumeration) @@ -771,7 +779,7 @@ object JsonCodecMaker: // the argument of method validate$retainedBody. It happened to have the correctly-typed parameter. // With the correct type, we can correct write out the value. case t: NeoTypeRef[?] => // in Quotes context - Symbol.requiredModule(t.name).methodMember("validate$retainedBody").head.paramSymss.head.head.tree match + Symbol.requiredModule(t.typedName.toString).methodMember("validate$retainedBody").head.paramSymss.head.head.tree match case ValDef(_, tt, _) => tt.tpe.asType match case '[u] => @@ -840,7 +848,6 @@ object JsonCodecMaker: val caseDef = CaseDef( Literal(IntConstant(oneField.index)), None, - // Assign(fieldSymRef, genReadVal[f](oneField.fieldRef.asInstanceOf[RTypeRef[f]], in).asTerm) '{ if (${ Ref(reqSym).asExprOf[Int] } & $reqBit) != 0 then ${ Assign(Ref(reqSym), '{ ${ Ref(reqSym).asExprOf[Int] } ^ $reqBit }.asTerm).asExprOf[Unit] } @@ -848,7 +855,7 @@ object JsonCodecMaker: else throw new JsonParseError("Duplicate field " + $fieldName, $in) }.asTerm ) - if dvMembers.isEmpty then (ValDef(sym, Some(oneField.fieldRef.unitVal.asTerm)), caseDef, fieldSymRef) + // if dvMembers.isEmpty then (ValDef(sym, Some(oneField.fieldRef.unitVal.asTerm)), caseDef, fieldSymRef) if dvMembers.isEmpty then // no default... required? Not if Option/Optional, or a collection oneField.fieldRef match { @@ -1057,12 +1064,12 @@ object JsonCodecMaker: // } def encodeValue(in: T, out: JsonOutput): Unit = ${ genWriteVal('in, ref, 'out) } - def decodeValue(in: JsonSource): T = ${ genReadVal(ref, 'in) } + def decodeValue(in: JsonSource): T = null.asInstanceOf[T] // ${ genReadVal(ref, 'in) } } }.asTerm val neededDefs = // others here??? Refer to Jsoniter file JsonCodecMaker.scala classFieldMatrixValDefs ++ writeMethodDefs ++ readMethodDefs val codec = Block(neededDefs.toList, codecDef).asExprOf[JsonCodec[T]] - println(s"Codec: ${codec.show}") + // println(s"Codec: ${codec.show}") codec diff --git a/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala b/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala index 5831ee2d..2d44c69e 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala @@ -19,7 +19,8 @@ class JsonConfig private[scalajack] ( val typeHintPolicy: TypeHintPolicy, // -------------------------- val enumsAsIds: Option[List[String]], // None=no enums as ids, Some(Nil)=all enums as ids, Some(List(...))=specified classes enums as ids - val escapedStrings: Boolean + val suppressEscapedStrings: Boolean, + val suppressTypeHints: Boolean ): def withNoneAsNull(): JsonConfig = copy(noneAsNull = true) def withTryFailureHandling(tryPolicy: TryPolicy): JsonConfig = copy(tryFailureHandling = tryPolicy) @@ -28,7 +29,8 @@ class JsonConfig private[scalajack] ( def withTypeHintLabel(label: String): JsonConfig = copy(typeHintLabel = label) def withTypeHintPolicy(hintPolicy: TypeHintPolicy): JsonConfig = copy(typeHintPolicy = hintPolicy) def withEnumsAsIds(asIds: Option[List[String]]): JsonConfig = copy(enumsAsIds = asIds) - def withEscapedStrings(): JsonConfig = copy(escapedStrings = false) + def withSuppressEscapedStrings(): JsonConfig = copy(suppressEscapedStrings = true) + def withSuppressTypeHints(): JsonConfig = copy(suppressTypeHints = true) private[this] def copy( noneAsNull: Boolean = noneAsNull, @@ -38,7 +40,8 @@ class JsonConfig private[scalajack] ( typeHintLabel: String = typeHintLabel, typeHintPolicy: TypeHintPolicy = typeHintPolicy, enumsAsIds: Option[List[String]] = enumsAsIds, - escapedStrings: Boolean = escapedStrings + suppressEscapedStrings: Boolean = suppressEscapedStrings, + suppressTypeHints: Boolean = suppressTypeHints ): JsonConfig = new JsonConfig( noneAsNull, tryFailureHandling, @@ -47,7 +50,8 @@ class JsonConfig private[scalajack] ( typeHintLabel, typeHintPolicy, enumsAsIds, - escapedStrings + suppressEscapedStrings, + suppressTypeHints ) enum TryPolicy: @@ -71,7 +75,8 @@ object JsonConfig typeHintLabel = "_hint", typeHintPolicy = TypeHintPolicy.SIMPLE_CLASSNAME, enumsAsIds = None, - escapedStrings = false + suppressEscapedStrings = false, + suppressTypeHints = false ): import scala.quoted.FromExpr.* @@ -90,10 +95,14 @@ object JsonConfig else '{ jc } } val jc3 = ${ - if !x.escapedStrings then '{ jc2.withEscapedStrings() } + if !x.suppressEscapedStrings then '{ jc2.withSuppressEscapedStrings() } else '{ jc2 } } - jc3 + val jc4 = ${ + if !x.suppressTypeHints then '{ jc2.withSuppressTypeHints() } + else '{ jc3 } + } + jc4 } } @@ -119,7 +128,8 @@ object JsonConfig $typeHintLabelE, $typeHintPolicyE, $enumsAsIdsE, - $escapedStringsE + $suppressEscapedStringsE, + $suppressTypeHintsE ) } => try @@ -136,7 +146,8 @@ object JsonConfig extract("typeHintLabel", typeHintLabelE), extract("typeHintPolicy", typeHintPolicyE), extract("enumsAsIds", enumsAsIdsE), - extract("escapedStrings", escapedStringsE) + extract("suppressEscapedStrings", suppressEscapedStringsE), + extract("suppressTypeHints", suppressTypeHintsE) ) ) catch { @@ -152,7 +163,8 @@ object JsonConfig case '{ ($x: JsonConfig).withTypeHintLabel($v) } => Some(x.valueOrAbort.withTypeHintLabel(v.valueOrAbort)) case '{ ($x: JsonConfig).withTypeHintPolicy($v) } => Some(x.valueOrAbort.withTypeHintPolicy(v.valueOrAbort)) case '{ ($x: JsonConfig).withEnumsAsIds($v) } => Some(x.valueOrAbort.withEnumsAsIds(v.valueOrAbort)) - case '{ ($x: JsonConfig).withEscapedStrings() } => Some(x.valueOrAbort.withEscapedStrings()) + case '{ ($x: JsonConfig).withSuppressEscapedStrings() } => Some(x.valueOrAbort.withSuppressEscapedStrings()) + case '{ ($x: JsonConfig).withSuppressTypeHints() } => Some(x.valueOrAbort.withSuppressTypeHints()) } private[scalajack] given ToExpr[TryPolicy] with { diff --git a/src/main/scala/co.blocke.scalajack/json/JsonError.scala b/src/main/scala/co.blocke.scalajack/json/JsonError.scala index f790f1d0..c071996c 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonError.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonError.scala @@ -8,6 +8,7 @@ class JsonNullKeyValue(msg: String) extends Throwable(msg) with NoStackTrace class JsonUnsupportedType(msg: String) extends Throwable(msg) with NoStackTrace class JsonConfigError(msg: String) extends Throwable(msg) with NoStackTrace class JsonEitherLeftError(msg: String) extends Throwable(msg) with NoStackTrace +class JsonIllegalCharacterError(msg: String) extends Throwable(msg) with NoStackTrace class ParseError(val msg: String) extends Throwable(msg) with NoStackTrace: val show: String = "" diff --git a/src/main/scala/co.blocke.scalajack/json/StringMatrix.scala b/src/main/scala/co.blocke.scalajack/json/StringMatrix.scala index 2d0ee485..ffbb799f 100644 --- a/src/main/scala/co.blocke.scalajack/json/StringMatrix.scala +++ b/src/main/scala/co.blocke.scalajack/json/StringMatrix.scala @@ -81,4 +81,4 @@ final class StringMatrix(val xs: Array[String]) { def first(bitset: Long): Int = if bitset == 0L then -1 else resolve(java.lang.Long.numberOfTrailingZeros(bitset)) // never returns 64 -} \ No newline at end of file +} diff --git a/src/main/scala/co.blocke.scalajack/json/package.scala b/src/main/scala/co.blocke.scalajack/json/package.scala index 692942f9..6f3b0e23 100644 --- a/src/main/scala/co.blocke.scalajack/json/package.scala +++ b/src/main/scala/co.blocke.scalajack/json/package.scala @@ -3,6 +3,8 @@ package json import scala.util.Failure +opaque type RawJson = String + val BUFFER_EXCEEDED: Char = 7 // Old "BELL" ASCII value, used as a marker when we've run off the end of the known world val END_OF_STRING: Char = 3 diff --git a/src/main/scala/co.blocke.scalajack/json/schema/JsonSchema.scala b/src/main/scala/co.blocke.scalajack/json/schema/JsonSchema.scala new file mode 100644 index 00000000..5c6d4e59 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/schema/JsonSchema.scala @@ -0,0 +1,293 @@ +package co.blocke.scalajack +package json +package schema + +import java.net.URL +import co.blocke.scala_reflection.* +import co.blocke.scala_reflection.reflect.ReflectOnType +import co.blocke.scala_reflection.reflect.rtypeRefs.* +import org.apache.commons.text.StringEscapeUtils + +// Macro... +import scala.quoted.* + +object JsonSchema: + + inline def of[T]: Schema = ${ ofImpl[T]() } + def ofImpl[T]()(using t: Type[T])(using quotes: Quotes): Expr[Schema] = + import quotes.reflect.* + val classRef = ReflectOnType[T](quotes)(TypeRepr.of[T], true)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) + genSchema(quotes)( classRef, None) + + private def genSchema[T](quotes: Quotes)(rt: RTypeRef[T], context: Option[ScalaFieldInfoRef] = None, defaultValue: Option[quotes.reflect.Term] = None): Expr[Schema] = + import quotes.reflect.* + implicit val q: Quotes = quotes + + def ofOption[T](xs: Option[Expr[T]])(using Type[T])(using Quotes): Expr[Option[T]] = + if xs.isEmpty then Expr(None) else '{ Some(${xs.get}) } + + def typeArgs(tpe: TypeRepr): List[TypeRepr] = tpe match + case AppliedType(_, typeArgs) => typeArgs.map(_.dealias) + case _ => Nil + + rt match + case t: AliasRef[_] => + t.unwrappedType.refType match + case '[e] => + genSchema[e](quotes)(t.unwrappedType.asInstanceOf[RTypeRef[e]], context, defaultValue) + case t: ArrayRef[_] => + t.refType match + case '[u] => + t.elementRef.refType match + case '[e] => + '{ + ArraySchema( + ${ genSchema[e](quotes)(t.elementRef.asInstanceOf[RTypeRef[e]], context, None) }, + ${ Expr(context.flatMap(_.annotations.get("minItems")).flatMap(_.get("value")).map(_.toInt)) }, + ${ Expr(context.flatMap(_.annotations.get("maxItems")).flatMap(_.get("value")).map(_.toInt)) }, + ${ Expr(context.flatMap(_.annotations.get("uniqueItems")).flatMap(_.get("value")).map(_.toBoolean)) }, + ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, + ${ + if defaultValue.isDefined then + val codec = JsonCodecMaker.generateCodecFor[u](t.refType.asInstanceOf[RTypeRef[u]], json.JsonConfig) + '{ + val out = new writing.JsonOutput() + $codec.encodeValue(${defaultValue.get.asExprOf[u]}, out) + Some(out.toString.asInstanceOf[RawJson]) + } + else + Expr(None) + } + ) + } + case t: EnumRef[_] => '{ EnumSchema( ${Expr(t.values)} ) } + case t: OptionRef[_] => + // Go ahead and gen schema for body of Option. Higher level (ie class) is resposible for tracking if a + // value is required or not... + t.optionParamType.refType match + case '[e] => + defaultValue.map{ dv => + val dve = dv.asExprOf[Option[e]] + '{ + $dve match + case Some(a) => ${ genSchema[e](quotes)(t.optionParamType.asInstanceOf[RTypeRef[e]], context, Some('{a}.asTerm)) } + case None => ${ genSchema[e](quotes)(t.optionParamType.asInstanceOf[RTypeRef[e]], context, None) } + } + }.getOrElse( genSchema[e](quotes)(t.optionParamType.asInstanceOf[RTypeRef[e]], context, None) ) + case t: TryRef[_] => + // Go ahead and gen schema for body of Try. Higher level (ie class) is resposible for tracking if a + // value is required or not... + t.tryRef.refType match + case '[e] => + genSchema[e](quotes)(t.tryRef.asInstanceOf[RTypeRef[e]], context, None) + case t: SeqRef[_] => + t.refType match + case '[u] => + t.elementRef.refType match + case '[e] => + '{ + ArraySchema( + ${ genSchema[e](quotes)(t.elementRef.asInstanceOf[RTypeRef[e]], context, None) }, + ${ Expr(context.flatMap(_.annotations.get("minItems")).flatMap(_.get("value")).map(_.toInt)) }, + ${ Expr(context.flatMap(_.annotations.get("maxItems")).flatMap(_.get("value")).map(_.toInt)) }, + ${ Expr(context.flatMap(_.annotations.get("uniqueItems")).flatMap(_.get("value")).map(_.toBoolean)) }, + ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, + ${ + if defaultValue.isDefined then + val codec = JsonCodecMaker.generateCodecFor[u](t.asInstanceOf[RTypeRef[u]], json.JsonConfig) + '{ + val out = new writing.JsonOutput() + $codec.encodeValue(${defaultValue.get.asExprOf[u]}, out) + Some(out.result.asInstanceOf[RawJson]) + } + else + Expr(None) + } + ) + } + case t: SetRef[_] => + t.refType match + case '[u] => + t.elementRef.refType match + case '[e] => + '{ + ArraySchema( + ${ genSchema[e](quotes)(t.elementRef.asInstanceOf[RTypeRef[e]], context, None) }, + ${ Expr(context.flatMap(_.annotations.get("minItems")).flatMap(_.get("value")).map(_.toInt)) }, + ${ Expr(context.flatMap(_.annotations.get("maxItems")).flatMap(_.get("value")).map(_.toInt)) }, + ${ Expr(context.flatMap(_.annotations.get("uniqueItems")).flatMap(_.get("value")).map(_.toBoolean)) }, + ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, + ${ + if defaultValue.isDefined then + val codec = JsonCodecMaker.generateCodecFor[u](t.asInstanceOf[RTypeRef[u]], json.JsonConfig) + '{ + val out = new writing.JsonOutput() + $codec.encodeValue(${defaultValue.get.asExprOf[u]}, out) + Some(out.result.asInstanceOf[RawJson]) + } + else + Expr(None) + } + ) + } + case t: TupleRef[_] => + t.refType match + case '[u] => + '{ + TupleSchema( + ${ + Expr.ofList(t.tupleRefs.map{ tr => + tr.refType match + case '[w] => + genSchema[w](quotes)(tr.asInstanceOf[RTypeRef[w]], context, None) + }) + }, + ${ Expr(context.flatMap(_.annotations.get("items")).flatMap(_.get("value")).map(_.toBoolean)) }, + ${ Expr(context.flatMap(_.annotations.get("minItems")).flatMap(_.get("value")).map(_.toInt)) }, + ${ Expr(context.flatMap(_.annotations.get("maxItems")).flatMap(_.get("value")).map(_.toInt)) }, + ${ Expr(context.flatMap(_.annotations.get("uniqueItems")).flatMap(_.get("value")).map(_.toBoolean)) }, + ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, + ${ + if defaultValue.isDefined then + val codec = JsonCodecMaker.generateCodecFor[u](t.asInstanceOf[RTypeRef[u]], json.JsonConfig) + '{ + val out = new writing.JsonOutput() + $codec.encodeValue(${defaultValue.get.asExprOf[u]}, out) + Some(out.result.asInstanceOf[RawJson]) + } + else + Expr(None) + } + ) + } + case t: BooleanRef => + '{ + BooleanSchema( + ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, + ${ + ofOption(defaultValue.map(v => '{ ${v.asExprOf[Boolean]}.toString.asInstanceOf[RawJson] })) + } + ) + } + case t: DoubleRef => + '{ + NumberSchema( + ${ Expr(context.flatMap(_.annotations.get("minimum")).flatMap(_.get("value")).map(_.toDouble)) }, + ${ Expr(context.flatMap(_.annotations.get("maximum")).flatMap(_.get("value")).map(_.toDouble)) }, + ${ Expr(context.flatMap(_.annotations.get("exclusiveMinimum")).flatMap(_.get("value")).map(_.toDouble)) }, + ${ Expr(context.flatMap(_.annotations.get("exclusiveMaximum")).flatMap(_.get("value")).map(_.toDouble)) }, + ${ Expr(context.flatMap(_.annotations.get("multipleOf")).flatMap(_.get("value")).map(_.toInt)) }, + ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, + ${ + ofOption(defaultValue.map(v => '{ ${v.asExprOf[Double]}.toString.asInstanceOf[RawJson] })) + } + ) + } + case t: IntRef => + '{ + IntegerSchema( + ${ Expr(context.flatMap(_.annotations.get("minimum")).flatMap(_.get("value")).map(_.toInt)) }, + ${ Expr(context.flatMap(_.annotations.get("maximum")).flatMap(_.get("value")).map(_.toInt)) }, + ${ Expr(context.flatMap(_.annotations.get("exclusiveMinimum")).flatMap(_.get("value")).map(_.toInt)) }, + ${ Expr(context.flatMap(_.annotations.get("exclusiveMaximum")).flatMap(_.get("value")).map(_.toInt)) }, + ${ Expr(context.flatMap(_.annotations.get("multipleOf")).flatMap(_.get("value")).map(_.toInt)) }, + ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, + ${ + ofOption(defaultValue.map(v => '{ ${v.asExprOf[Int]}.toString.asInstanceOf[RawJson] })) + } + ) + } + case t: LongRef => + '{ + IntegerSchema( + ${ Expr(context.flatMap(_.annotations.get("minimum")).flatMap(_.get("value")).map(_.toLong)) }, + ${ Expr(context.flatMap(_.annotations.get("maximum")).flatMap(_.get("value")).map(_.toLong)) }, + ${ Expr(context.flatMap(_.annotations.get("exclusiveMinimum")).flatMap(_.get("value")).map(_.toLong)) }, + ${ Expr(context.flatMap(_.annotations.get("exclusiveMaximum")).flatMap(_.get("value")).map(_.toLong)) }, + ${ Expr(context.flatMap(_.annotations.get("multipleOf")).flatMap(_.get("value")).map(_.toInt)) }, + ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, + ${ + ofOption(defaultValue.map(v => '{ ${v.asExprOf[Long]}.toString.asInstanceOf[RawJson] })) + } + ) + } + case t: StringRef => + '{ + StringSchema( + ${ Expr(context.flatMap(_.annotations.get("minLength")).flatMap(_.get("value")).map(_.toInt)) }, + ${ Expr(context.flatMap(_.annotations.get("maxLength")).flatMap(_.get("value")).map(_.toInt)) }, + ${ Expr(context.flatMap(_.annotations.get("pattern")).flatMap(_.get("value")).map(v => StringEscapeUtils.escapeJson(v))) }, + ${ Expr(context.flatMap(_.annotations.get("format")).flatMap(_.get("value"))) }.map(v => StringFormat.valueOf(v)), + ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, + ${ + ofOption(defaultValue.map(v => '{ ("\"" + StringEscapeUtils.escapeJson(${v.asExprOf[String]}) + "\"").asInstanceOf[RawJson] })) + } + ) + } + case t: ZonedDateTimeRef => + '{ + StringSchema( + ${ Expr(context.flatMap(_.annotations.get("minLength")).flatMap(_.get("value")).map(_.toInt)) }, + ${ Expr(context.flatMap(_.annotations.get("maxLength")).flatMap(_.get("value")).map(_.toInt)) }, + ${ Expr(context.flatMap(_.annotations.get("pattern")).flatMap(_.get("value")).map(v => StringEscapeUtils.escapeJson(v))) }, + Some(StringFormat.`date-time`), + ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, + ${ + ofOption(defaultValue.map(v => '{ ${v.asExprOf[java.time.ZonedDateTime]}.toString.asInstanceOf[RawJson] })) + } + ) + } + case t: ScalaClassRef[_] => + t.refType match + case '[u] => + val requiredFields = Expr(t.fields.collect{ + case f: FieldInfoRef if !f.fieldRef.isInstanceOf[OptionRef[_]] => f.name + }) + '{ + ObjectSchema( + ${ + Expr.ofList(t.fields.map(f => + f.fieldRef.refType match + case '[b] => + // Get default value if any + val tpe = TypeRepr.of[u] + val classCompanion = tpe.typeSymbol.companionClass + val companionModule = tpe.typeSymbol.companionModule + val dvMembers = classCompanion.methodMember("$lessinit$greater$default$" + (f.index + 1)) + val fieldDefault = if dvMembers.isEmpty then + None + else + val methodSymbol = dvMembers.head + val dvSelectNoTArgs = Ref(companionModule).select(methodSymbol) + val dvSelect = methodSymbol.paramSymss match + case Nil => dvSelectNoTArgs + case List(params) if (params.exists(_.isTypeParam)) => + typeArgs(tpe) match + case Nil => ??? // throw JsonParseError("Expected an applied type", ???) + case typeArgs => TypeApply(dvSelectNoTArgs, typeArgs.map(Inferred(_))) + case _ => ??? // fail(s"Default method for ${symbol.name} of class ${tpe.show} have a complex " + + Some(dvSelect) + Expr.ofTuple((Expr(f.name), genSchema[b](quotes)(f.fieldRef.asInstanceOf[RTypeRef[b]], Some(f.asInstanceOf[ScalaFieldInfoRef]), fieldDefault))) + ) + ) + }.toMap, + $requiredFields, + ${ Expr(t.annotations.get("co.blocke.scalajack.schema.additionalProperties").flatMap(_.get("value")).map(_.toBoolean).getOrElse(false)) }, + new URL("http://jsons-schema.org/draft-04/schema#"), + ${ Expr(t.annotations.get("co.blocke.scalajack.schema.id").flatMap(_.get("value"))) }, + ${ Expr(t.annotations.get("co.blocke.scalajack.schema.title").flatMap(_.get("value"))) }, + ${ Expr(t.annotations.get("co.blocke.scalajack.schema.description").flatMap(_.get("value"))) }, + ${ + if defaultValue.isDefined then + val codec = JsonCodecMaker.generateCodecFor[u](t.asInstanceOf[RTypeRef[u]], json.JsonConfig.withSuppressTypeHints()) + '{ + val out = new writing.JsonOutput() + $codec.encodeValue(${defaultValue.get.asExprOf[u]}, out) + Some(out.result.asInstanceOf[RawJson]) + } + else + Expr(None) + } + ) + } + case _ => throw new Exception(s"Unsupported type ${rt.name} for JSON schema generation") diff --git a/src/main/scala/co.blocke.scalajack/json/schema/Schema.scala b/src/main/scala/co.blocke.scalajack/json/schema/Schema.scala new file mode 100644 index 00000000..d6d62e58 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/schema/Schema.scala @@ -0,0 +1,115 @@ +package co.blocke.scalajack +package json +package schema + +import java.net.URL + +/** + * A *very* sparse implementation of JSON Schema Draft 4 (model only). It is full of holes, but is just + * enough for what I needed at the time--and had the advantage of leveraging scala-reflection + * to generate the schema from a type. At the time of this writing, no other Scala 3-capable JSON + * library generated a JSON Schema document. (ZIO-Json genrated their own Schema structure, however.) + * + * If there is strong utility for a full-blown JSON Schema utility, that might be something I look + * at later, unless someone wants to take it up. I would suggest development of "helper" objects + * for the more advanced schema operaitons (allOf, anyOf, if/then/else, etc) vs trying to do all that + * in annotations. + */ + +// Reference: https://json-schema.org/UnderstandingJSONSchema.pdf + +enum SchemaType: + case `null`, `boolean`, `object`, `array`, `string`, `number`, `integer` + +enum StringFormat: + case `date-time`, email, hostname, ipv4, ipv6, uuid, uri, url + +// opaque type JSON_LITERAL = String + +type Schema = StdSchema | EnumSchema + +sealed trait StdSchema: + val `type`: SchemaType + val description: Option[String] + val default: Option[RawJson] + +// Formats: Dates & Times, Email, Hostnames +case class StringSchema( + minLength: Option[Int] = None, + maxLength: Option[Int] = None, + pattern: Option[String] = None, + format: Option[StringFormat] = None, + description: Option[String] = None, + default: Option[RawJson] = None, + `type`: SchemaType = SchemaType.`string` + ) extends StdSchema + +case class IntegerSchema( + minimum: Option[Long] = None, + maximum: Option[Long] = None, + exclusiveMinimum: Option[Long] = None, + exclusiveMaximum: Option[Long] = None, + multipleOf: Option[Int] = None, + description: Option[String] = None, + default: Option[RawJson] = None, + `type`: SchemaType = SchemaType.`integer` + ) extends StdSchema + +case class NumberSchema( + minimum: Option[Double] = None, + maximum: Option[Double] = None, + exclusiveMinimum: Option[Double] = None, + exclusiveMaximum: Option[Double] = None, + multipleOf: Option[Int] = None, + description: Option[String] = None, + default: Option[RawJson] = None, + `type`: SchemaType = SchemaType.`number` + ) extends StdSchema + +case class BooleanSchema( + description: Option[String] = None, + default: Option[RawJson] = None, + `type`: SchemaType = SchemaType.`boolean` + ) extends StdSchema + +case class NullSchema(description: Option[String] = None, `type`: SchemaType = SchemaType.`null`) extends StdSchema: + val default: Option[RawJson] = None // no default for null possible + +case class ArraySchema( + items: Schema, + minItems: Option[Int] = None, + maxItems: Option[Int] = None, + uniqueItems: Option[Boolean] = None, + description: Option[String] = None, + default: Option[RawJson] = None, + `type`: SchemaType = SchemaType.`array` +) extends StdSchema + +case class TupleSchema( + prefixItems: List[Schema], + items: Option[Boolean] = None, + minItems: Option[Int] = None, + maxItems: Option[Int] = None, + uniqueItems: Option[Boolean] = None, + description: Option[String] = None, + default: Option[RawJson] = None, + `type`: SchemaType = SchemaType.`array` +) extends StdSchema + +// Note: patternProperties not implemented at this time (I didn't need them) +case class ObjectSchema( + properties: Map[String,Schema], + required: List[String], + additionalProperties: Boolean = false, + `$schema`: URL = new URL("http://jsons-schema.org/draft-04/schema#"), + `$id`: Option[String] = None, + title: Option[String] = None, + description: Option[String] = None, + default: Option[RawJson] = None, + `type`: SchemaType = SchemaType.`object` +) extends StdSchema + +// Weird exception to other schemas--no type, or other decorating feature... just the enum values +case class EnumSchema( + `enum`: List[String] +) diff --git a/src/main/scala/co.blocke.scalajack/json/writing/JsonOutput.scala b/src/main/scala/co.blocke.scalajack/json/writing/JsonOutput.scala index d1cda3d8..05b6aa46 100644 --- a/src/main/scala/co.blocke.scalajack/json/writing/JsonOutput.scala +++ b/src/main/scala/co.blocke.scalajack/json/writing/JsonOutput.scala @@ -4,13 +4,13 @@ package writing import java.time.format.DateTimeFormatter.* -/** Wrapper around a StringBuilder that offers support for primitive types, +/** Wrapper around a (Fast)StringBuilder that offers support for primitive types, * including quotes-wrapping those that need it for use in Map keys (aka stringified). * Handles dangling commas/separators, plus mark & revert capability. It is reusable * via clear() for speed -- one per thread, of course! */ case class JsonOutput(): - val internal: StringBuilder = new StringBuilder() + val internal: FastStringBuilder = new FastStringBuilder() private var comma: Boolean = false private var savePoint: Int = 0 @@ -19,6 +19,8 @@ case class JsonOutput(): def clear() = internal.clear() + comma = false + savePoint = 0 this def mark() = @@ -60,7 +62,10 @@ case class JsonOutput(): inline def label(s: String): Unit = maybeComma() - internal.append("\"" + s + "\":") + internal.append('"') + internal.append(s) + internal.append('"') + internal.append(':') // ----------------------- Primitive/Simple type support @@ -75,43 +80,64 @@ case class JsonOutput(): inline def valueStringified(v: scala.math.BigDecimal): Unit = maybeComma() if v == null then throw new JsonNullKeyValue("Key values may not be null") - else internal.append("\"" + v + "\"") + else + internal.append('"') + internal.append(v.toString) + internal.append('"') comma = true inline def value(v: scala.math.BigInt): Unit = maybeComma() if v == null then internal.append("null") - else internal.append(v) + else + internal.append('"') + internal.append(v.toString) + internal.append('"') comma = true inline def valueStringified(v: scala.math.BigInt): Unit = maybeComma() if v == null then throw new JsonNullKeyValue("Key values may not be null") - else internal.append("\"" + v + "\"") + else + internal.append('"') + internal.append(v.toString) + internal.append('"') comma = true inline def value(v: java.math.BigDecimal): Unit = maybeComma() if v == null then internal.append("null") - else internal.append(v) + else + internal.append('"') + internal.append(v.toString) + internal.append('"') comma = true inline def valueStringified(v: java.math.BigDecimal): Unit = maybeComma() if v == null then throw new JsonNullKeyValue("Key values may not be null") - else internal.append("\"" + v + "\"") + else + internal.append('"') + internal.append(v.toString) + internal.append('"') comma = true inline def value(v: java.math.BigInteger): Unit = maybeComma() if v == null then internal.append("null") - else internal.append(v) + else + internal.append('"') + internal.append(v.toString) + internal.append('"') comma = true inline def valueStringified(v: java.math.BigInteger): Unit = maybeComma() if v == null then throw new JsonNullKeyValue("Key values may not be null") - else internal.append("\"" + v + "\"") + else + internal.append('"') + internal.append(v.toString) + internal.append('"') comma = true inline def value(v: Boolean): Unit = @@ -121,7 +147,9 @@ case class JsonOutput(): inline def valueStringified(v: Boolean): Unit = maybeComma() - internal.append("\"" + v + "\"") + internal.append('"') + internal.append(v.toString) + internal.append('"') comma = true inline def value(v: Byte): Unit = @@ -131,12 +159,16 @@ case class JsonOutput(): inline def valueStringified(v: Byte): Unit = maybeComma() - internal.append("\"" + v + "\"") + internal.append('"') + internal.append(v.toInt) + internal.append('"') comma = true inline def value(v: Char): Unit = maybeComma() - internal.append("\"" + v + "\"") + internal.append('"') + internal.append(v) + internal.append('"') comma = true inline def value(v: Double): Unit = @@ -146,7 +178,9 @@ case class JsonOutput(): inline def valueStringified(v: Double): Unit = maybeComma() - internal.append("\"" + v + "\"") + internal.append('"') + internal.append(v) + internal.append('"') comma = true inline def value(v: Float): Unit = @@ -156,7 +190,9 @@ case class JsonOutput(): inline def valueStringified(v: Float): Unit = maybeComma() - internal.append("\"" + v + "\"") + internal.append('"') + internal.append(v) + internal.append('"') comma = true inline def value(v: Int): Unit = @@ -166,7 +202,9 @@ case class JsonOutput(): inline def valueSringified(v: Int): Unit = maybeComma() - internal.append("\"" + v + "\"") + internal.append('"') + internal.append(v) + internal.append('"') comma = true inline def value(v: Long): Unit = @@ -176,7 +214,9 @@ case class JsonOutput(): inline def valueStringified(v: Long): Unit = maybeComma() - internal.append("\"" + v + "\"") + internal.append('"') + internal.append(v) + internal.append('"') comma = true inline def value(v: Short): Unit = @@ -186,13 +226,32 @@ case class JsonOutput(): inline def valueStringified(v: Short): Unit = maybeComma() - internal.append("\"" + v + "\"") + internal.append('"') + internal.append(v) + internal.append('"') comma = true inline def value(v: String): Unit = maybeComma() if v == null then internal.append("null") - else internal.append("\"" + v + "\"") + else + internal.append('"') + internal.append(v) + internal.append('"') + comma = true + + inline def valueEscaped(v: String): Unit = + maybeComma() + if v == null then internal.append("null") + else + internal.append('"') + internal.appendEscaped(v, 0, v.length) + internal.append('"') + comma = true + + inline def valueRaw(v: RawJson): Unit = + maybeComma() + internal.append(v.asInstanceOf[String]) comma = true inline def value(v: java.lang.Boolean): Unit = @@ -204,7 +263,10 @@ case class JsonOutput(): inline def valueStringified(v: java.lang.Boolean): Unit = maybeComma() if v == null then internal.append("null") - else internal.append("\"" + v + "\"") + else + internal.append('"') + internal.append(v) + internal.append('"') comma = true inline def value(v: java.lang.Byte): Unit = @@ -216,13 +278,19 @@ case class JsonOutput(): inline def valueStringified(v: java.lang.Byte): Unit = maybeComma() if v == null then internal.append("null") - else internal.append("\"" + v.toInt + "\"") + else + internal.append('"') + internal.append(v.toInt) + internal.append('"') comma = true inline def value(v: java.lang.Character): Unit = maybeComma() if v == null then internal.append("null") - else internal.append("\"" + v + "\"") + else + internal.append('"') + internal.append(v) + internal.append('"') comma = true inline def value(v: java.lang.Double): Unit = @@ -234,7 +302,10 @@ case class JsonOutput(): inline def valueStringified(v: java.lang.Double): Unit = maybeComma() if v == null then internal.append("null") - else internal.append("\"" + v + "\"") + else + internal.append('"') + internal.append(v) + internal.append('"') comma = true inline def value(v: java.lang.Float): Unit = @@ -246,7 +317,10 @@ case class JsonOutput(): inline def valueStringified(v: java.lang.Float): Unit = maybeComma() if v == null then internal.append("null") - else internal.append("\"" + v + "\"") + else + internal.append('"') + internal.append(v) + internal.append('"') comma = true inline def value(v: java.lang.Integer): Unit = @@ -258,7 +332,10 @@ case class JsonOutput(): inline def valueStringified(v: java.lang.Integer): Unit = maybeComma() if v == null then internal.append("null") - else internal.append("\"" + v + "\"") + else + internal.append('"') + internal.append(v) + internal.append('"') comma = true inline def value(v: java.lang.Long): Unit = @@ -270,7 +347,10 @@ case class JsonOutput(): inline def valueStringified(v: java.lang.Long): Unit = maybeComma() if v == null then internal.append("null") - else internal.append("\"" + v + "\"") + else + internal.append('"') + internal.append(v) + internal.append('"') comma = true inline def value(v: java.lang.Short): Unit = @@ -282,7 +362,10 @@ case class JsonOutput(): inline def valueStringified(v: java.lang.Short): Unit = maybeComma() if v == null then internal.append("null") - else internal.append("\"" + v + "\"") + else + internal.append('"') + internal.append(v) + internal.append('"') comma = true inline def value(v: java.lang.Number): Unit = @@ -294,95 +377,161 @@ case class JsonOutput(): inline def valueStringified(v: java.lang.Number): Unit = maybeComma() if v == null then internal.append("null") - else internal.append("\"" + v + "\"") + else + internal.append('"') + internal.append(v) + internal.append('"') comma = true inline def value(v: java.time.Duration): Unit = maybeComma() if v == null then internal.append("null") - else internal.append("\"" + v + "\"") + else + internal.append('"') + internal.append(v.toString) + internal.append('"') comma = true inline def value(v: java.time.Instant): Unit = maybeComma() if v == null then internal.append("null") - else internal.append("\"" + v + "\"") + else + internal.append('"') + internal.append(v.toString) + internal.append('"') comma = true inline def value(v: java.time.LocalDate): Unit = maybeComma() if v == null then internal.append("null") - else internal.append("\"" + v.format(ISO_LOCAL_DATE) + "\"") + else + internal.append('"') + internal.append(v.format(ISO_LOCAL_DATE)) + internal.append('"') comma = true inline def value(v: java.time.LocalDateTime): Unit = maybeComma() if v == null then internal.append("null") - else internal.append("\"" + v.format(ISO_LOCAL_DATE_TIME) + "\"") + else + internal.append('"') + internal.append(v.format(ISO_LOCAL_DATE_TIME)) + internal.append('"') comma = true inline def value(v: java.time.LocalTime): Unit = maybeComma() if v == null then internal.append("null") - else internal.append("\"" + v.format(ISO_LOCAL_TIME) + "\"") + else + internal.append('"') + internal.append(v.format(ISO_LOCAL_TIME)) + internal.append('"') comma = true inline def value(v: java.time.MonthDay): Unit = maybeComma() if v == null then internal.append("null") - else internal.append("\"" + v + "\"") + else + internal.append('"') + internal.append(v.toString) + internal.append('"') comma = true inline def value(v: java.time.OffsetDateTime): Unit = maybeComma() if v == null then internal.append("null") - else internal.append("\"" + v.format(ISO_OFFSET_DATE_TIME) + "\"") + else + internal.append('"') + internal.append(v.format(ISO_OFFSET_DATE_TIME)) + internal.append('"') comma = true inline def value(v: java.time.OffsetTime): Unit = maybeComma() if v == null then internal.append("null") - else internal.append("\"" + v.format(ISO_OFFSET_TIME) + "\"") + else + internal.append('"') + internal.append(v.format(ISO_OFFSET_TIME)) + internal.append('"') comma = true inline def value(v: java.time.Period): Unit = maybeComma() if v == null then internal.append("null") - else internal.append("\"" + v + "\"") + else + internal.append('"') + internal.append(v.toString) + internal.append('"') comma = true inline def value(v: java.time.Year): Unit = maybeComma() if v == null then internal.append("null") - else internal.append("\"" + v + "\"") + else + internal.append('"') + internal.append(v.toString) + internal.append('"') comma = true inline def value(v: java.time.YearMonth): Unit = maybeComma() if v == null then internal.append("null") - else internal.append("\"" + v + "\"") + else + internal.append('"') + internal.append(v.toString) + internal.append('"') comma = true inline def value(v: java.time.ZonedDateTime): Unit = maybeComma() if v == null then internal.append("null") - else internal.append("\"" + v.format(ISO_ZONED_DATE_TIME) + "\"") + else + internal.append('"') + internal.append(v.format(ISO_ZONED_DATE_TIME)) + internal.append('"') comma = true inline def value(v: java.time.ZoneId): Unit = maybeComma() if v == null then internal.append("null") - else internal.append("\"" + v + "\"") + else + internal.append('"') + internal.append(v.toString) + internal.append('"') comma = true inline def value(v: java.time.ZoneOffset): Unit = maybeComma() if v == null then internal.append("null") - else internal.append("\"" + v + "\"") + else + internal.append('"') + internal.append(v.toString) + internal.append('"') + comma = true + + inline def value(v: java.net.URL): Unit = + maybeComma() + if v == null then internal.append("null") + else + internal.append('"') + internal.append(v.toString) + internal.append('"') + comma = true + + inline def value(v: java.net.URI): Unit = + maybeComma() + if v == null then internal.append("null") + else + internal.append('"') + internal.append(v.toString) + internal.append('"') comma = true inline def value(v: java.util.UUID): Unit = maybeComma() if v == null then internal.append("null") - else internal.append("\"" + v + "\"") + else + internal.append('"') + internal.append(v.toString) + internal.append('"') comma = true diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index 5f2838f1..48619bfe 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -8,6 +8,7 @@ import scala.reflect.ClassTag import json.* import scala.collection.immutable.Queue import co.blocke.scalajack.json.reading.SafeNumbers.double +import co.blocke.scalajack.schema.* class Shape[T](polygon: T) class Parallelogram() @@ -46,105 +47,22 @@ object RunMe extends App: import co.blocke.scalajack.json.run.Record println("\n") - // implicit val blah: ScalaJack[Foom] = sj[Foom] - // println(ScalaJack[Foom].fromJson("""{"a": -12, "b":"Greg Z"}""")) + // println(RType.of[Person].pretty) - // implicit val blah: ScalaJack[List[Queue[Int]]] = sj[List[Queue[Int]]] - // println(ScalaJack[List[Queue[Int]]].fromJson("null")) // "[[1,2,3],[4,5,6],[7,8,9]]")) + implicit val blah: ScalaJack[Record] = sj[Record] // (JsonConfig.withSuppressedEscapedStrings()) + println(blah.toJson(record)) - implicit val blah: ScalaJack[Record] = sj[Record] - println(blah.fromJson(jsData)) + // println(RType.of[Schema].pretty) - println("===================") + val f = new FastStringBuilder() + val s = """Gregory "William" +Zoller""" + "\u20A0 wow" + f.appendEscaped(s, 0, s.length) + println(f.result) - // val c = new co.blocke.scalajack.run.Codec() - // println(c.decodeValue(co.blocke.scalajack.json.reading.JsonSource(jsData))) + // implicit val blah: ScalaJack[schema.Schema] = sj[schema.Schema](JsonConfig.withSuppressedTypeHints()) + // println(sj.toJson(schema.JsonSchema.of[Person])) - println("done.") + // println(RType.of[schema.Schema].pretty) - /* - var i = 0 - val msg = """This is a test"""" - - val cbuf = new Array[Char](4048) - val ta = System.nanoTime() - while i < 1000 do - val ps = reading.ParseString(msg.getBytes) - val z = ps.parseString(0, msg.length(), cbuf, 0) - new String(cbuf, 0, z) - i += 1 - val tb = System.nanoTime() - println("Time (Jsoniter) : " + (tb - ta)) // 3235208 - - println("------") - - i = 0 - val tr = System.nanoTime() - val msgX = "\"" + msg - while i < 1000 do - val jsrc = reading.JsonSource(msgX) - jsrc.expectString() - i += 1 - val tq = System.nanoTime() - println("Time (ExpectNew): " + (tq - tr)) // 4927792 --> about a 52% improvement!!! - - println("------") - - i = 0 - val tr2 = System.nanoTime() - while i < 1000 do - val jsrc = reading.JsonSource(msgX) - jsrc.expectStringOld() - i += 1 - val tq2 = System.nanoTime() - println("Time (ExpectOld): " + (tq2 - tr2)) // 4927792 --> about a 52% improvement!!! - - println("------") - - i = 0 - val te2 = System.nanoTime() - while i < 1000 do - val jsrc = reading.JsonSource(msg) - val pc = jsrc.parseString(0) - jsrc.js.subSequence(0, pc) - i += 1 - val tf2 = System.nanoTime() - println("Time (PNew) : " + (tf2 - te2)) // 4927792 --> about a 52% improvement!!! - - println("------") - - i = 0 - val te = System.nanoTime() - val msgY = "\"" + msg - while i < 1000 do - val jsrc = reading.JsonSource(msgY) - jsrc.parseStringOld() - i += 1 - val tf = System.nanoTime() - println("Time (POld) : " + (tf - te)) // 4927792 --> about a 52% improvement!!! - - println("------") - - i = 0 - val ty = System.nanoTime() - while i < 1000 do - blah.fromJson(jsData) - i += 1 - val tz = System.nanoTime() - println("Time (ParseAll) : " + (tz - ty)) - // Orig: 79298041 - // New : 58537667 - // Latest: 79713209 - // Newnew: 34919833 - - println("------") - - i = 0 - val ty2 = System.nanoTime() - while i < 1000 do - val g = reading.JsonSource("true}") - g.expectBoolean() - i += 1 - val tz2 = System.nanoTime() - println("Time (Boolean Old) : " + (tz2 - ty2)) - */ + println("done.") diff --git a/src/main/scala/co.blocke.scalajack/run/Record.scala b/src/main/scala/co.blocke.scalajack/run/Record.scala index 2dfcbd66..4f06d0d7 100644 --- a/src/main/scala/co.blocke.scalajack/run/Record.scala +++ b/src/main/scala/co.blocke.scalajack/run/Record.scala @@ -3,14 +3,19 @@ package json package run import neotype.* +import co.blocke.scalajack.schema.* +@additionalProperties(value = "true") +@id(value = "abc123") +@title(value = "The Greatest") +@description(value = "nothing special") case class Person( - name: String, - age: Int, - address: Address, - email: String, - phone_numbers: List[String], - is_employed: Boolean + name: String = "Greg", + age: Int = 57, + address: Address = Address("123 Main", "New York", "NY", "12345"), + email: Option[String] = Some("wow.gmail.com"), + phone_numbers: List[String] = List("a", "b"), + is_employed: Boolean = true ) case class Address( @@ -70,11 +75,18 @@ given NonEmptyList: Newtype[List[Int]] with inline def validate(input: List[Int]): Boolean = input.nonEmpty +type NonZeroInt = NonZeroInt.Type +object NonZeroInt extends Newtype[Int]: + override inline def validate(input: Int): Boolean = + input != 0 + type XList = XList.Type given XList: Newtype[List[String]] with inline def validate(input: List[String]): Boolean = input(0) == "x" +case class Validated(num: NonZeroInt, name: NonEmptyString, xspot: XList, nada: List[EmptyString]) + case class Tag[X](a: X) given [A, B](using newType: Newtype.WithType[A, B], tag: Tag[A]): Tag[B] = newType.unsafeWrapF(tag) @@ -86,7 +98,15 @@ given EmptyString: Newtype[String] with case class Person2(age: XList) -case class Foom(a: Int, b: String) +case class Foom(a: schema.Schema) + +sealed trait Candy: + val isSweet: Boolean +case class MMs(isSweet: Boolean) extends Candy +case class Musk(isSweet: Boolean) extends Candy +case class Veggies(yuks: String) + +type Food = Candy | Veggies val jsData = """{ @@ -138,7 +158,7 @@ val jsData = }""" val record = Record( - Person("John Doe", 30, Address("123 Main Street", "Anytown", "CA", "12345"), "john.doe@example.com", List("555-555-5555", "555-123-4567"), true), + Person("John Doe", 30, Address("123 Main Street", "Anytown", "CA", "12345"), Some("john.doe@example.com"), List("555-555-5555", "555-123-4567"), true), List("reading", "swimming", "traveling"), List(Friend("Jane Smith", 28, "jane.smith@example.com"), Friend("Bob Johnson", 32, "bob.johnson@example.com")), List(Pet("Fido", "Dog", 5), Pet("Whiskers", "Cat", 3)) diff --git a/src/test/scala/co.blocke.scalajack/json/misc/MiscTests.scala b/src/test/scala/co.blocke.scalajack/json/misc/MiscTests.scala index ff4418d0..40eb1266 100644 --- a/src/test/scala/co.blocke.scalajack/json/misc/MiscTests.scala +++ b/src/test/scala/co.blocke.scalajack/json/misc/MiscTests.scala @@ -20,17 +20,14 @@ class MiscSpec() extends AnyFunSpec with JsonMatchers: describe(colorString("-------------------------------\n: Misc Tests :\n-------------------------------", Console.YELLOW)) { describe(colorString("+++ Positive Tests +++")) { - it("String escaping must work") { - val inst = StringHolder("""This is a "strange" test\non another level.""") - val js = sj[StringHolder].toJson(inst) - js should matchJson("""{"a":"This is a \"strange\" test\\non another level."}""") - } - it("String escaping must work (bad JSON, but proves escape can be turned off)") { - val inst = StringHolder("""This is a "strange" test\non another level.""") + it("String escaping must work (proves escape can be turned off)") { + val inst = StringHolder("""This is a "strange" test +on another level.""") val js1 = sj[StringHolder].toJson(inst) - val js2 = sj[StringHolder](JsonConfig.withEscapedStrings()).toJson(inst) - js1 should equal("""{"a":"This is a "strange" test\non another level."}""") - js2 should equal("""{"a":"This is a "strange" test\non another level."}""") + val js2 = sj[StringHolder](JsonConfig.withSuppressEscapedStrings()).toJson(inst) + js1 should equal("""{"a":"This is a \"strange\" test\non another level."}""") + js2 should equal("""{"a":"This is a "strange" test +on another level."}""") } it("NeoType integration must work") { val inst = Validated(NonEmptyString("Mike"), XList(List("x", "y", "z")), List(EmptyString(""), EmptyString(""), EmptyString(""))) @@ -50,7 +47,7 @@ class MiscSpec() extends AnyFunSpec with JsonMatchers: (Some('a'), None, Some('b')) ) val js = sj[AnyHolder].toJson(inst) - js should equal("""{"maybe":[1,2,3],"itried":{"a":-5},"itried2":99,"ifailed":"anymap":{"a":1,"b":2},"whichOneR":3,"bunch":["a",null,"b"]}""") + js should equal("""{"maybe":[1,2,3],"itried":{"a":-5},"itried2":99,"anymap":{"a":1,"b":2},"whichOneR":3,"bunch":["a",null,"b"]}""") } it("Any type must work (none as null)") { val inst = AnyHolder( diff --git a/src/test/scala/co.blocke.scalajack/json/primitives/Model.scala b/src/test/scala/co.blocke.scalajack/json/primitives/Model.scala index ee156366..8ce7b642 100644 --- a/src/test/scala/co.blocke.scalajack/json/primitives/Model.scala +++ b/src/test/scala/co.blocke.scalajack/json/primitives/Model.scala @@ -2,6 +2,7 @@ package co.blocke.scalajack package json.primitives import java.util.UUID +import java.net.{URI, URL} import java.lang.{Boolean as JBoolean, Byte as JByte, Character as JChar, Double as JDouble, Float as JFloat, Integer as JInt, Long as JLong, Number as JNumber, Short as JShort} import java.math.{BigDecimal as JBigDecimal, BigInteger as JBigInteger} import java.time.* @@ -61,6 +62,7 @@ case class SampleJLong(l1: JLong, l2: JLong, l3: JLong, l4: JLong, l5: JLong) case class SampleJNumber(n1: JNumber, n2: JNumber, n3: JNumber, n4: JNumber, n5: JNumber, n6: JNumber, n7: JNumber, n8: JNumber, n9: JNumber, n10: JNumber, n11: JNumber, n12: JNumber, n13: JNumber, n14: JNumber, n15: JNumber, n16: JNumber, n17: JNumber) case class SampleJShort(s1: JShort, s2: JShort, s3: JShort, s4: JShort, s5: JShort) case class SampleUUID(u1: UUID, u2: UUID) +case class SampleNet(u1: URL, u2: URL, u3: URI, u4: URI) // === Java Time case class SampleDuration(d1: Duration, d2: Duration, d3: Duration) diff --git a/src/test/scala/co.blocke.scalajack/json/primitives/SimpleSpec.scala b/src/test/scala/co.blocke.scalajack/json/primitives/SimpleSpec.scala index 1e6b9ca8..065ef4bc 100644 --- a/src/test/scala/co.blocke.scalajack/json/primitives/SimpleSpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/primitives/SimpleSpec.scala @@ -10,6 +10,7 @@ import org.scalatest.* import TestUtil.* import java.time.* import java.util.UUID +import java.net.{URI, URL} class SimpleSpec() extends AnyFunSpec with JsonMatchers: @@ -161,6 +162,18 @@ class SimpleSpec() extends AnyFunSpec with JsonMatchers: // inst shouldEqual ScalaJack.read[SampleZoneOffset](js) } + it("Net types URL and URI must work") { + val inst = SampleNet( + null, + new URL("https://www.foom.com"), + null, + new URI("https://www.foom.com") + ) + val js = sj[SampleNet].toJson(inst) + println(js) + js should matchJson("""{"u1":null,"u2":"https://www.foom.com","u3":null,"u4":"https://www.foom.com"}""") + } + it("UUID must work") { val inst = SampleUUID( null, From ce27f87fc9e254d04e635f60d3359848f11f4544 Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Tue, 19 Mar 2024 00:22:41 -0500 Subject: [PATCH 49/65] implement read primitives --- benchmark/build.sbt | 2 +- .../src/main/scala/co.blocke/Record.scala | 14 + benchmark/src/main/scala/co.blocke/Run.scala | 20 +- .../src/main/scala/co.blocke/ScalaJack.scala | 17 +- build.sbt | 2 +- .../schema/additionalProperties.java | 2 +- .../{ => json}/schema/description.java | 2 +- .../{ => json}/schema/exclusiveMaximum.java | 2 +- .../{ => json}/schema/exclusiveMinimum.java | 2 +- .../scalajack/{ => json}/schema/format.java | 2 +- .../scalajack/{ => json}/schema/id.java | 2 +- .../scalajack/{ => json}/schema/items.java | 2 +- .../scalajack/{ => json}/schema/maxItems.java | 2 +- .../{ => json}/schema/maxLength.java | 2 +- .../scalajack/{ => json}/schema/maximum.java | 2 +- .../scalajack/{ => json}/schema/minItems.java | 2 +- .../{ => json}/schema/minLength.java | 2 +- .../scalajack/{ => json}/schema/minimum.java | 2 +- .../{ => json}/schema/multipleOf.java | 2 +- .../scalajack/{ => json}/schema/pattern.java | 2 +- .../scalajack/{ => json}/schema/title.java | 2 +- .../{ => json}/schema/uniqueItems.java | 2 +- .../scala/co.blocke.scalajack/ScalaJack.scala | 11 +- .../json/FastStringBuilder.scala | 91 ++- .../json/JsonCodecMaker.scala | 337 +++++++++-- .../json/reading/JsonSource.scala | 352 +++++------ .../json/schema/JsonSchema.scala | 571 +++++++++--------- .../json/schema/Schema.scala | 33 +- .../scala/co.blocke.scalajack/run/Play.scala | 33 +- .../co.blocke.scalajack/run/Record.scala | 18 +- 30 files changed, 916 insertions(+), 619 deletions(-) rename src/main/java/co/blocke/scalajack/{ => json}/schema/additionalProperties.java (79%) rename src/main/java/co/blocke/scalajack/{ => json}/schema/description.java (79%) rename src/main/java/co/blocke/scalajack/{ => json}/schema/exclusiveMaximum.java (79%) rename src/main/java/co/blocke/scalajack/{ => json}/schema/exclusiveMinimum.java (79%) rename src/main/java/co/blocke/scalajack/{ => json}/schema/format.java (78%) rename src/main/java/co/blocke/scalajack/{ => json}/schema/id.java (77%) rename src/main/java/co/blocke/scalajack/{ => json}/schema/items.java (78%) rename src/main/java/co/blocke/scalajack/{ => json}/schema/maxItems.java (78%) rename src/main/java/co/blocke/scalajack/{ => json}/schema/maxLength.java (78%) rename src/main/java/co/blocke/scalajack/{ => json}/schema/maximum.java (78%) rename src/main/java/co/blocke/scalajack/{ => json}/schema/minItems.java (78%) rename src/main/java/co/blocke/scalajack/{ => json}/schema/minLength.java (78%) rename src/main/java/co/blocke/scalajack/{ => json}/schema/minimum.java (78%) rename src/main/java/co/blocke/scalajack/{ => json}/schema/multipleOf.java (78%) rename src/main/java/co/blocke/scalajack/{ => json}/schema/pattern.java (78%) rename src/main/java/co/blocke/scalajack/{ => json}/schema/title.java (77%) rename src/main/java/co/blocke/scalajack/{ => json}/schema/uniqueItems.java (79%) diff --git a/benchmark/build.sbt b/benchmark/build.sbt index 5f47969c..4d06fc5a 100644 --- a/benchmark/build.sbt +++ b/benchmark/build.sbt @@ -37,7 +37,7 @@ lazy val benchmark = project libraryDependencies ++= Seq( "org.playframework" %% "play-json" % "3.0.1", "io.argonaut" %% "argonaut" % "6.3.9", - "co.blocke" %% "scalajack" % "19ca46_unknown", + "co.blocke" %% "scalajack" % "5dbb0c_unknown", "dev.zio" %% "zio-json" % "0.6.1", "org.typelevel" %% "fabric-core" % "1.12.6", "org.typelevel" %% "fabric-io" % "1.12.6", diff --git a/benchmark/src/main/scala/co.blocke/Record.scala b/benchmark/src/main/scala/co.blocke/Record.scala index 7d8cf2ac..7dd85434 100644 --- a/benchmark/src/main/scala/co.blocke/Record.scala +++ b/benchmark/src/main/scala/co.blocke/Record.scala @@ -35,6 +35,20 @@ case class Record2( pets: List[Pet2] ) +// import io.circe.{Codec,Decoder,Encoder,Json} +// import io.circe.generic.semiauto._ +// case class Who(id: String, name:String, `type`:Who.Type) +// object Who: +// implicit val codec: Codec[Who] = deriveCodec[Who] + +// sealed trait Type extends Product with Serializable +// object Type : +// val values: Set[Who.Type] = Set(Staff, Customer, Program) +// implicit val encoder: Encoder[Type] = ??? +// implicit val decoder: Decoder[Type] = ??? +// case object Staff extends Type +// case object Customer extends Type +// case object Program extends Type val jsData2 = """{ diff --git a/benchmark/src/main/scala/co.blocke/Run.scala b/benchmark/src/main/scala/co.blocke/Run.scala index 1b1d0c2d..f7855dc4 100644 --- a/benchmark/src/main/scala/co.blocke/Run.scala +++ b/benchmark/src/main/scala/co.blocke/Run.scala @@ -1,21 +1,17 @@ package co.blocke - import zio.json._ +import com.github.plokhotnyuk.jsoniter_scala.core._ +import com.github.plokhotnyuk.jsoniter_scala.macros._ object RunMe extends App: - implicit val decoder: JsonDecoder[Record2] = DeriveJsonDecoder.gen[Record2] - implicit val encoder: JsonEncoder[Record2] = DeriveJsonEncoder.gen[Record2] + import co.blocke.scalajack.ScalaJack.* + import co.blocke.scalajack.* - implicit val decoder1: JsonDecoder[Address2] = DeriveJsonDecoder.gen[Address2] - implicit val decoder2: JsonDecoder[Pet2] = DeriveJsonDecoder.gen[Pet2] - implicit val decoder3: JsonDecoder[Friend2] = DeriveJsonDecoder.gen[Friend2] - implicit val decoder4: JsonDecoder[Person2] = DeriveJsonDecoder.gen[Person2] - implicit val encoder1: JsonEncoder[Address2] = DeriveJsonEncoder.gen[Address2] - implicit val encoder2: JsonEncoder[Pet2] = DeriveJsonEncoder.gen[Pet2] - implicit val encoder3: JsonEncoder[Friend2] = DeriveJsonEncoder.gen[Friend2] - implicit val encoder4: JsonEncoder[Person2] = DeriveJsonEncoder.gen[Person2] + // given codec: JsonValueCodec[Who.Type] = JsonCodecMaker.make - println( jsData2.fromJson[Record2] ) + // implicit val blah: ScalaJack[co.blocke.scalajack.json.run.Yippy] = sj[co.blocke.scalajack.json.run.Yippy] + + // println( ScalaJack[co.blocke.scalajack.json.run.Yippy].toJson(yippy) ) println("\nDone") \ No newline at end of file diff --git a/benchmark/src/main/scala/co.blocke/ScalaJack.scala b/benchmark/src/main/scala/co.blocke/ScalaJack.scala index a6f8b96a..733ad446 100644 --- a/benchmark/src/main/scala/co.blocke/ScalaJack.scala +++ b/benchmark/src/main/scala/co.blocke/ScalaJack.scala @@ -6,7 +6,7 @@ object ScalaJackZ: import co.blocke.scalajack.ScalaJack.* import co.blocke.scalajack.* - implicit val blah: ScalaJack[co.blocke.Record2] = sj[co.blocke.Record2] + implicit val blah: ScalaJack[co.blocke.Record2] = codecOf[co.blocke.Record2] trait ScalaJackReadingBenchmark{ @Benchmark @@ -16,7 +16,16 @@ object ScalaJackZ: trait ScalaJackWritingBenchmark { @Benchmark def writeRecordScalaJack = ScalaJack[co.blocke.Record2].toJson(record) - // 344702.052 with escaped strings (apache lib) - // 2225843.198 with FastStringBuilder - // 1081490.833 with StringBuilder } + + /* + This is the way: + + * Use implicit to define ScalaJack[...] = sj[...] + * Then use ScalaJack[...].___() to do json function + + implicit val blah: ScalaJack[co.blocke.Record2] = sj[co.blocke.Record2] + def writeRecordScalaJack = ScalaJack[co.blocke.Record2].toJson(record) + + TODO: Maybe rewrite sj to be something like buildCodec or something more descriptive. + */ \ No newline at end of file diff --git a/build.sbt b/build.sbt index 6e09e519..89d650ed 100644 --- a/build.sbt +++ b/build.sbt @@ -35,7 +35,7 @@ lazy val root = project Test / parallelExecution := false, scalafmtOnCompile := !isCI, libraryDependencies ++= Seq( - "co.blocke" %% "scala-reflection" % "2.0.1", + "co.blocke" %% "scala-reflection" % "2.0.2", "org.apache.commons" % "commons-text" % "1.11.0", "io.github.kitlangton" %% "neotype" % "0.0.9", "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core" % "2.24.5-SNAPSHOT", diff --git a/src/main/java/co/blocke/scalajack/schema/additionalProperties.java b/src/main/java/co/blocke/scalajack/json/schema/additionalProperties.java similarity index 79% rename from src/main/java/co/blocke/scalajack/schema/additionalProperties.java rename to src/main/java/co/blocke/scalajack/json/schema/additionalProperties.java index 08275be7..a3f46238 100644 --- a/src/main/java/co/blocke/scalajack/schema/additionalProperties.java +++ b/src/main/java/co/blocke/scalajack/json/schema/additionalProperties.java @@ -1,4 +1,4 @@ -package co.blocke.scalajack.schema; +package co.blocke.scalajack.json.schema; import java.lang.annotation.*; diff --git a/src/main/java/co/blocke/scalajack/schema/description.java b/src/main/java/co/blocke/scalajack/json/schema/description.java similarity index 79% rename from src/main/java/co/blocke/scalajack/schema/description.java rename to src/main/java/co/blocke/scalajack/json/schema/description.java index fcb118bf..995e8bd5 100644 --- a/src/main/java/co/blocke/scalajack/schema/description.java +++ b/src/main/java/co/blocke/scalajack/json/schema/description.java @@ -1,4 +1,4 @@ -package co.blocke.scalajack.schema; +package co.blocke.scalajack.json.schema; import java.lang.annotation.*; diff --git a/src/main/java/co/blocke/scalajack/schema/exclusiveMaximum.java b/src/main/java/co/blocke/scalajack/json/schema/exclusiveMaximum.java similarity index 79% rename from src/main/java/co/blocke/scalajack/schema/exclusiveMaximum.java rename to src/main/java/co/blocke/scalajack/json/schema/exclusiveMaximum.java index d05dca95..ad50b948 100644 --- a/src/main/java/co/blocke/scalajack/schema/exclusiveMaximum.java +++ b/src/main/java/co/blocke/scalajack/json/schema/exclusiveMaximum.java @@ -1,4 +1,4 @@ -package co.blocke.scalajack.schema; +package co.blocke.scalajack.json.schema; import java.lang.annotation.*; diff --git a/src/main/java/co/blocke/scalajack/schema/exclusiveMinimum.java b/src/main/java/co/blocke/scalajack/json/schema/exclusiveMinimum.java similarity index 79% rename from src/main/java/co/blocke/scalajack/schema/exclusiveMinimum.java rename to src/main/java/co/blocke/scalajack/json/schema/exclusiveMinimum.java index 64cd32ef..0b31b2c2 100644 --- a/src/main/java/co/blocke/scalajack/schema/exclusiveMinimum.java +++ b/src/main/java/co/blocke/scalajack/json/schema/exclusiveMinimum.java @@ -1,4 +1,4 @@ -package co.blocke.scalajack.schema; +package co.blocke.scalajack.json.schema; import java.lang.annotation.*; diff --git a/src/main/java/co/blocke/scalajack/schema/format.java b/src/main/java/co/blocke/scalajack/json/schema/format.java similarity index 78% rename from src/main/java/co/blocke/scalajack/schema/format.java rename to src/main/java/co/blocke/scalajack/json/schema/format.java index 060648e1..8864d438 100644 --- a/src/main/java/co/blocke/scalajack/schema/format.java +++ b/src/main/java/co/blocke/scalajack/json/schema/format.java @@ -1,4 +1,4 @@ -package co.blocke.scalajack.schema; +package co.blocke.scalajack.json.schema; import java.lang.annotation.*; diff --git a/src/main/java/co/blocke/scalajack/schema/id.java b/src/main/java/co/blocke/scalajack/json/schema/id.java similarity index 77% rename from src/main/java/co/blocke/scalajack/schema/id.java rename to src/main/java/co/blocke/scalajack/json/schema/id.java index 9a0fd1c0..d02f7b68 100644 --- a/src/main/java/co/blocke/scalajack/schema/id.java +++ b/src/main/java/co/blocke/scalajack/json/schema/id.java @@ -1,4 +1,4 @@ -package co.blocke.scalajack.schema; +package co.blocke.scalajack.json.schema; import java.lang.annotation.*; diff --git a/src/main/java/co/blocke/scalajack/schema/items.java b/src/main/java/co/blocke/scalajack/json/schema/items.java similarity index 78% rename from src/main/java/co/blocke/scalajack/schema/items.java rename to src/main/java/co/blocke/scalajack/json/schema/items.java index 389c66b2..64278aba 100644 --- a/src/main/java/co/blocke/scalajack/schema/items.java +++ b/src/main/java/co/blocke/scalajack/json/schema/items.java @@ -1,4 +1,4 @@ -package co.blocke.scalajack.schema; +package co.blocke.scalajack.json.schema; import java.lang.annotation.*; diff --git a/src/main/java/co/blocke/scalajack/schema/maxItems.java b/src/main/java/co/blocke/scalajack/json/schema/maxItems.java similarity index 78% rename from src/main/java/co/blocke/scalajack/schema/maxItems.java rename to src/main/java/co/blocke/scalajack/json/schema/maxItems.java index 35addb58..63141cfe 100644 --- a/src/main/java/co/blocke/scalajack/schema/maxItems.java +++ b/src/main/java/co/blocke/scalajack/json/schema/maxItems.java @@ -1,4 +1,4 @@ -package co.blocke.scalajack.schema; +package co.blocke.scalajack.json.schema; import java.lang.annotation.*; diff --git a/src/main/java/co/blocke/scalajack/schema/maxLength.java b/src/main/java/co/blocke/scalajack/json/schema/maxLength.java similarity index 78% rename from src/main/java/co/blocke/scalajack/schema/maxLength.java rename to src/main/java/co/blocke/scalajack/json/schema/maxLength.java index 59a40ef2..7a336c91 100644 --- a/src/main/java/co/blocke/scalajack/schema/maxLength.java +++ b/src/main/java/co/blocke/scalajack/json/schema/maxLength.java @@ -1,4 +1,4 @@ -package co.blocke.scalajack.schema; +package co.blocke.scalajack.json.schema; import java.lang.annotation.*; diff --git a/src/main/java/co/blocke/scalajack/schema/maximum.java b/src/main/java/co/blocke/scalajack/json/schema/maximum.java similarity index 78% rename from src/main/java/co/blocke/scalajack/schema/maximum.java rename to src/main/java/co/blocke/scalajack/json/schema/maximum.java index 1c5a2f3c..694d7d5c 100644 --- a/src/main/java/co/blocke/scalajack/schema/maximum.java +++ b/src/main/java/co/blocke/scalajack/json/schema/maximum.java @@ -1,4 +1,4 @@ -package co.blocke.scalajack.schema; +package co.blocke.scalajack.json.schema; import java.lang.annotation.*; diff --git a/src/main/java/co/blocke/scalajack/schema/minItems.java b/src/main/java/co/blocke/scalajack/json/schema/minItems.java similarity index 78% rename from src/main/java/co/blocke/scalajack/schema/minItems.java rename to src/main/java/co/blocke/scalajack/json/schema/minItems.java index 272d520e..8dfdfe51 100644 --- a/src/main/java/co/blocke/scalajack/schema/minItems.java +++ b/src/main/java/co/blocke/scalajack/json/schema/minItems.java @@ -1,4 +1,4 @@ -package co.blocke.scalajack.schema; +package co.blocke.scalajack.json.schema; import java.lang.annotation.*; diff --git a/src/main/java/co/blocke/scalajack/schema/minLength.java b/src/main/java/co/blocke/scalajack/json/schema/minLength.java similarity index 78% rename from src/main/java/co/blocke/scalajack/schema/minLength.java rename to src/main/java/co/blocke/scalajack/json/schema/minLength.java index f9a0fded..fc843077 100644 --- a/src/main/java/co/blocke/scalajack/schema/minLength.java +++ b/src/main/java/co/blocke/scalajack/json/schema/minLength.java @@ -1,4 +1,4 @@ -package co.blocke.scalajack.schema; +package co.blocke.scalajack.json.schema; import java.lang.annotation.*; diff --git a/src/main/java/co/blocke/scalajack/schema/minimum.java b/src/main/java/co/blocke/scalajack/json/schema/minimum.java similarity index 78% rename from src/main/java/co/blocke/scalajack/schema/minimum.java rename to src/main/java/co/blocke/scalajack/json/schema/minimum.java index c983d825..bb719e6d 100644 --- a/src/main/java/co/blocke/scalajack/schema/minimum.java +++ b/src/main/java/co/blocke/scalajack/json/schema/minimum.java @@ -1,4 +1,4 @@ -package co.blocke.scalajack.schema; +package co.blocke.scalajack.json.schema; import java.lang.annotation.*; diff --git a/src/main/java/co/blocke/scalajack/schema/multipleOf.java b/src/main/java/co/blocke/scalajack/json/schema/multipleOf.java similarity index 78% rename from src/main/java/co/blocke/scalajack/schema/multipleOf.java rename to src/main/java/co/blocke/scalajack/json/schema/multipleOf.java index 0fa0e9e2..88e25cb3 100644 --- a/src/main/java/co/blocke/scalajack/schema/multipleOf.java +++ b/src/main/java/co/blocke/scalajack/json/schema/multipleOf.java @@ -1,4 +1,4 @@ -package co.blocke.scalajack.schema; +package co.blocke.scalajack.json.schema; import java.lang.annotation.*; diff --git a/src/main/java/co/blocke/scalajack/schema/pattern.java b/src/main/java/co/blocke/scalajack/json/schema/pattern.java similarity index 78% rename from src/main/java/co/blocke/scalajack/schema/pattern.java rename to src/main/java/co/blocke/scalajack/json/schema/pattern.java index 0e25f979..c3e370e8 100644 --- a/src/main/java/co/blocke/scalajack/schema/pattern.java +++ b/src/main/java/co/blocke/scalajack/json/schema/pattern.java @@ -1,4 +1,4 @@ -package co.blocke.scalajack.schema; +package co.blocke.scalajack.json.schema; import java.lang.annotation.*; diff --git a/src/main/java/co/blocke/scalajack/schema/title.java b/src/main/java/co/blocke/scalajack/json/schema/title.java similarity index 77% rename from src/main/java/co/blocke/scalajack/schema/title.java rename to src/main/java/co/blocke/scalajack/json/schema/title.java index 12e2f34c..a7c6e1a6 100644 --- a/src/main/java/co/blocke/scalajack/schema/title.java +++ b/src/main/java/co/blocke/scalajack/json/schema/title.java @@ -1,4 +1,4 @@ -package co.blocke.scalajack.schema; +package co.blocke.scalajack.json.schema; import java.lang.annotation.*; diff --git a/src/main/java/co/blocke/scalajack/schema/uniqueItems.java b/src/main/java/co/blocke/scalajack/json/schema/uniqueItems.java similarity index 79% rename from src/main/java/co/blocke/scalajack/schema/uniqueItems.java rename to src/main/java/co/blocke/scalajack/json/schema/uniqueItems.java index c2a001ba..d8dad30c 100644 --- a/src/main/java/co/blocke/scalajack/schema/uniqueItems.java +++ b/src/main/java/co/blocke/scalajack/json/schema/uniqueItems.java @@ -1,4 +1,4 @@ -package co.blocke.scalajack.schema; +package co.blocke.scalajack.json.schema; import java.lang.annotation.*; diff --git a/src/main/scala/co.blocke.scalajack/ScalaJack.scala b/src/main/scala/co.blocke.scalajack/ScalaJack.scala index 067c5d1d..e2f2184d 100644 --- a/src/main/scala/co.blocke.scalajack/ScalaJack.scala +++ b/src/main/scala/co.blocke.scalajack/ScalaJack.scala @@ -23,22 +23,19 @@ object ScalaJack: def apply[A](implicit a: ScalaJack[A]): ScalaJack[A] = a // ----- Use default JsonConfig - inline def sj[T]: ScalaJack[T] = ${ sjImpl[T] } - def sjImpl[T: Type](using Quotes): Expr[ScalaJack[T]] = + inline def codecOf[T]: ScalaJack[T] = ${ codecOfImpl[T] } + def codecOfImpl[T: Type](using Quotes): Expr[ScalaJack[T]] = import quotes.reflect.* val classRef = ReflectOnType[T](quotes)(TypeRepr.of[T], true)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) - // val jsonDecoder = reading.JsonReader.refRead2(classRef) - // println(s"Decoder: ${jsonDecoder.show}") val jsonCodec = JsonCodecMaker.generateCodecFor(classRef, JsonConfig) '{ ScalaJack($jsonCodec) } // ----- Use given JsonConfig - inline def sj[T](inline cfg: JsonConfig): ScalaJack[T] = ${ sjImplWithConfig[T]('cfg) } - def sjImplWithConfig[T: Type](cfgE: Expr[JsonConfig])(using Quotes): Expr[ScalaJack[T]] = + inline def codecOf[T](inline cfg: JsonConfig): ScalaJack[T] = ${ codecOfImplWithConfig[T]('cfg) } + def codecOfImplWithConfig[T: Type](cfgE: Expr[JsonConfig])(using Quotes): Expr[ScalaJack[T]] = import quotes.reflect.* val cfg = summon[FromExpr[JsonConfig]].unapply(cfgE) val classRef = ReflectOnType[T](quotes)(TypeRepr.of[T], true)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) - // val jsonDecoder = reading.JsonReader.refRead2(classRef) val jsonCodec = JsonCodecMaker.generateCodecFor(classRef, cfg.getOrElse(JsonConfig)) '{ ScalaJack($jsonCodec) } diff --git a/src/main/scala/co.blocke.scalajack/json/FastStringBuilder.scala b/src/main/scala/co.blocke.scalajack/json/FastStringBuilder.scala index 9c4d4da0..4f8c81e3 100644 --- a/src/main/scala/co.blocke.scalajack/json/FastStringBuilder.scala +++ b/src/main/scala/co.blocke.scalajack/json/FastStringBuilder.scala @@ -16,43 +16,29 @@ final class FastStringBuilder(initial: Int = 16) { @tailrec final def ensureCapacity(size: Int): Unit = - if i+size < chars.length then () + if i + size < chars.length then () else chars = Arrays.copyOf(chars, chars.length * 2) ensureCapacity(size) - def append(c: Char): Unit = + def append(c: Char): Unit = if i == chars.length then chars = Arrays.copyOf(chars, chars.length * 2) chars(i) = c i += 1 - private final val escapedChars: Array[Byte] = Array( - -1, -1, -1, -1, -1, -1, -1, -1, 98, 116, 110, -1, 102, 114, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 0, 0, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 92, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1 + final private val escapedChars: Array[Byte] = Array( + -1, -1, -1, -1, -1, -1, -1, -1, 98, 116, 110, -1, 102, 114, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1 ) - private final val lowerCaseHexDigits: Array[Short] = Array( - 12336, 12592, 12848, 13104, 13360, 13616, 13872, 14128, 14384, 14640, 24880, 25136, 25392, 25648, 25904, 26160, - 12337, 12593, 12849, 13105, 13361, 13617, 13873, 14129, 14385, 14641, 24881, 25137, 25393, 25649, 25905, 26161, - 12338, 12594, 12850, 13106, 13362, 13618, 13874, 14130, 14386, 14642, 24882, 25138, 25394, 25650, 25906, 26162, - 12339, 12595, 12851, 13107, 13363, 13619, 13875, 14131, 14387, 14643, 24883, 25139, 25395, 25651, 25907, 26163, - 12340, 12596, 12852, 13108, 13364, 13620, 13876, 14132, 14388, 14644, 24884, 25140, 25396, 25652, 25908, 26164, - 12341, 12597, 12853, 13109, 13365, 13621, 13877, 14133, 14389, 14645, 24885, 25141, 25397, 25653, 25909, 26165, - 12342, 12598, 12854, 13110, 13366, 13622, 13878, 14134, 14390, 14646, 24886, 25142, 25398, 25654, 25910, 26166, - 12343, 12599, 12855, 13111, 13367, 13623, 13879, 14135, 14391, 14647, 24887, 25143, 25399, 25655, 25911, 26167, - 12344, 12600, 12856, 13112, 13368, 13624, 13880, 14136, 14392, 14648, 24888, 25144, 25400, 25656, 25912, 26168, - 12345, 12601, 12857, 13113, 13369, 13625, 13881, 14137, 14393, 14649, 24889, 25145, 25401, 25657, 25913, 26169, - 12385, 12641, 12897, 13153, 13409, 13665, 13921, 14177, 14433, 14689, 24929, 25185, 25441, 25697, 25953, 26209, - 12386, 12642, 12898, 13154, 13410, 13666, 13922, 14178, 14434, 14690, 24930, 25186, 25442, 25698, 25954, 26210, - 12387, 12643, 12899, 13155, 13411, 13667, 13923, 14179, 14435, 14691, 24931, 25187, 25443, 25699, 25955, 26211, - 12388, 12644, 12900, 13156, 13412, 13668, 13924, 14180, 14436, 14692, 24932, 25188, 25444, 25700, 25956, 26212, - 12389, 12645, 12901, 13157, 13413, 13669, 13925, 14181, 14437, 14693, 24933, 25189, 25445, 25701, 25957, 26213, - 12390, 12646, 12902, 13158, 13414, 13670, 13926, 14182, 14438, 14694, 24934, 25190, 25446, 25702, 25958, 26214 + final private val lowerCaseHexDigits: Array[Short] = Array( + 12336, 12592, 12848, 13104, 13360, 13616, 13872, 14128, 14384, 14640, 24880, 25136, 25392, 25648, 25904, 26160, 12337, 12593, 12849, 13105, 13361, 13617, 13873, 14129, 14385, 14641, 24881, 25137, 25393, 25649, 25905, 26161, 12338, 12594, 12850, 13106, + 13362, 13618, 13874, 14130, 14386, 14642, 24882, 25138, 25394, 25650, 25906, 26162, 12339, 12595, 12851, 13107, 13363, 13619, 13875, 14131, 14387, 14643, 24883, 25139, 25395, 25651, 25907, 26163, 12340, 12596, 12852, 13108, 13364, 13620, 13876, 14132, + 14388, 14644, 24884, 25140, 25396, 25652, 25908, 26164, 12341, 12597, 12853, 13109, 13365, 13621, 13877, 14133, 14389, 14645, 24885, 25141, 25397, 25653, 25909, 26165, 12342, 12598, 12854, 13110, 13366, 13622, 13878, 14134, 14390, 14646, 24886, 25142, + 25398, 25654, 25910, 26166, 12343, 12599, 12855, 13111, 13367, 13623, 13879, 14135, 14391, 14647, 24887, 25143, 25399, 25655, 25911, 26167, 12344, 12600, 12856, 13112, 13368, 13624, 13880, 14136, 14392, 14648, 24888, 25144, 25400, 25656, 25912, 26168, + 12345, 12601, 12857, 13113, 13369, 13625, 13881, 14137, 14393, 14649, 24889, 25145, 25401, 25657, 25913, 26169, 12385, 12641, 12897, 13153, 13409, 13665, 13921, 14177, 14433, 14689, 24929, 25185, 25441, 25697, 25953, 26209, 12386, 12642, 12898, 13154, + 13410, 13666, 13922, 14178, 14434, 14690, 24930, 25186, 25442, 25698, 25954, 26210, 12387, 12643, 12899, 13155, 13411, 13667, 13923, 14179, 14435, 14691, 24931, 25187, 25443, 25699, 25955, 26211, 12388, 12644, 12900, 13156, 13412, 13668, 13924, 14180, + 14436, 14692, 24932, 25188, 25444, 25700, 25956, 26212, 12389, 12645, 12901, 13157, 13413, 13669, 13925, 14181, 14437, 14693, 24933, 25189, 25445, 25701, 25957, 26213, 12390, 12646, 12902, 13158, 13414, 13670, 13926, 14182, 14438, 14694, 24934, 25190, + 25446, 25702, 25958, 26214 ) // private[this] def appendEscapedUnicode(ch: Int, pos: Int, buf: Array[Byte]): Int = { @@ -72,48 +58,47 @@ final class FastStringBuilder(initial: Int = 16) { } @tailrec - final def appendEscaped(s: String, from: Int, to: Int): Unit = + final def appendEscaped(s: String, from: Int, to: Int): Unit = ensureCapacity(2) - if (from >= to) () + if from >= to then () else val ch1 = s.charAt(from) if ch1 < 0x80 then val esc = escapedChars(ch1) - if (esc == 0) + if esc == 0 then chars(i) = ch1 i += 1 appendEscaped(s, from + 1, to) - else if esc > 0 then - chars(i) = 0x5C - chars(i+1) = esc.toChar + else if esc > 0 then + chars(i) = 0x5c + chars(i + 1) = esc.toChar i += 2 appendEscaped(s, from + 1, to) - else - appendEscapedUnicode(ch1) - else if ((ch1 & 0xF800) != 0xD800) then - appendEscapedUnicode(ch1) + else appendEscapedUnicode(ch1) + else if (ch1 & 0xf800) != 0xd800 then appendEscapedUnicode(ch1) else var ch2 = 0 - if (ch1 >= 0xDC00 || from + 1 >= to || { - ch2 = s.charAt(from + 1).toInt - (ch2 & 0xFC00) != 0xDC00 - }) throw new JsonIllegalCharacterError("Illegal encoded text character in string value: "+ch2) + if ch1 >= 0xdc00 || from + 1 >= to || { + ch2 = s.charAt(from + 1).toInt + (ch2 & 0xfc00) != 0xdc00 + } + then throw new JsonIllegalCharacterError("Illegal encoded text character in string value: " + ch2) appendEscapedUnicode(ch2.toChar) - def append(s: String): Unit = + def append(s: String): Unit = ensureCapacity(s.length) s.getChars(0, s.length, chars, i) i += s.length - def append(v: scala.math.BigDecimal): Unit = append( v.toString ) - def append(v: scala.math.BigInt): Unit = append( v.toString ) - def append(v: Boolean): Unit = append( v.toString ) - def append(v: Double): Unit = append( v.toString ) - def append(v: Float): Unit = append( v.toString ) - def append(v: Int): Unit = append( v.toString ) - def append(v: Long): Unit = append( v.toString ) - def append(v: Short): Unit = append( v.toString ) - def append(v: java.lang.Number): Unit = append( v.toString ) + def append(v: scala.math.BigDecimal): Unit = append(v.toString) + def append(v: scala.math.BigInt): Unit = append(v.toString) + def append(v: Boolean): Unit = append(v.toString) + def append(v: Double): Unit = append(v.toString) + def append(v: Float): Unit = append(v.toString) + def append(v: Int): Unit = append(v.toString) + def append(v: Long): Unit = append(v.toString) + def append(v: Short): Unit = append(v.toString) + def append(v: java.lang.Number): Unit = append(v.toString) def result = CharBuffer.wrap(chars, 0, i).toString -} \ No newline at end of file +} diff --git a/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala b/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala index ca6234cf..dc067189 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala @@ -712,8 +712,7 @@ object JsonCodecMaker: if isStringified then '{ $out.value(${ aE.asExprOf[java.lang.Byte] }) } else '{ $out.value(${ aE.asExprOf[java.lang.Byte] }) } case t: JCharacterRef => - if isStringified then '{ $out.value(${ aE.asExprOf[java.lang.Character] }) } - else '{ $out.value(${ aE.asExprOf[java.lang.Character] }) } + '{ $out.value(${ aE.asExprOf[java.lang.Character] }) } case t: JDoubleRef => if isStringified then '{ $out.value(${ aE.asExprOf[java.lang.Double] }) } else '{ $out.value(${ aE.asExprOf[java.lang.Double] }) } @@ -909,7 +908,7 @@ object JsonCodecMaker: Block(varDefs :+ reqVarDef, parseLoop).asExprOf[T] ) - case _ => ??? + case t => throw new ParseError("Not yet implemented: " + t) // --------------------------------------------------------------------------------------------- @@ -929,40 +928,308 @@ object JsonCodecMaker: .getOrElse( ref match // First cover all primitive and simple types... - // case t: BigDecimalRef => - // if isStringified then '{ $out.valueStringified(${ aE.asExprOf[scala.math.BigDecimal] }) } - // else '{ $out.value(${ aE.asExprOf[scala.math.BigDecimal] }) } - // case t: BigIntRef => - // if isStringified then '{ $out.valueStringified(${ aE.asExprOf[scala.math.BigInt] }) } - // else '{ $out.value(${ aE.asExprOf[scala.math.BigInt] }) } + case t: BigDecimalRef => + if isStringified then '{ scala.math.BigDecimal($in.expectString()) }.asExprOf[T] + else + '{ + $in.mark() + $in.skipNumber() + scala.math.BigDecimal($in.captureMark()) + }.asExprOf[T] + case t: BigIntRef => + if isStringified then '{ scala.math.BigInt($in.expectString()) }.asExprOf[T] + else + '{ + $in.mark() + $in.skipNumber() + scala.math.BigInt($in.captureMark()) + }.asExprOf[T] case t: BooleanRef => - '{ $in.expectBoolean() }.asExprOf[T] - // if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Boolean] }) } - // else '{ $out.value(${ aE.asExprOf[Boolean] }) } - // case t: ByteRef => - // if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Byte] }) } - // else '{ $out.value(${ aE.asExprOf[Byte] }) } - // case t: CharRef => '{ $out.value(${ aE.asExprOf[Char] }) } - // case t: DoubleRef => - // if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Double] }) } - // else '{ $out.value(${ aE.asExprOf[Double] }) } - // case t: FloatRef => - // if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Float] }) } - // else '{ $out.value(${ aE.asExprOf[Float] }) } + if isStringified then + '{ + if $in.readChar() != '"' then throw ParseError("Quoted boolean value expected") + val v = $in.expectBoolean() + if $in.readChar() != '"' then throw ParseError("Close quotes expected") + v + }.asExprOf[T] + else '{ $in.expectBoolean() }.asExprOf[T] + case t: ByteRef => + if isStringified then + '{ + if $in.readChar() != '"' then throw ParseError("Quoted byte value expected") + val v = $in.expectInt().toByte + if $in.readChar() != '"' then throw ParseError("Close quotes expected") + v + }.asExprOf[T] + else '{ $in.expectInt().toByte }.asExprOf[T] + case t: CharRef => + '{ + val c = $in.expectString() + if c.length == 0 then throw ParseError("Char value expected but empty string found in json") + else c.charAt(0) + }.asExprOf[T] + case t: DoubleRef => + if isStringified then + '{ + if $in.readChar() != '"' then throw ParseError("Quoted double value expected") + val v = $in.expectDouble() + if $in.readChar() != '"' then throw ParseError("Close quotes expected") + v + }.asExprOf[T] + else '{ $in.expectDouble() }.asExprOf[T] + case t: FloatRef => + if isStringified then + '{ + if $in.readChar() != '"' then throw ParseError("Quoted float value expected") + val v = $in.expectFloat() + if $in.readChar() != '"' then throw ParseError("Close quotes expected") + v + }.asExprOf[T] + else '{ $in.expectFloat() }.asExprOf[T] case t: IntRef => - '{ $in.expectInt() }.asExprOf[T] - // if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Int] }) } - // else '{ $out.value(${ aE.asExprOf[Int] }) } - // case t: LongRef => - // if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Long] }) } - // else '{ $out.value(${ aE.asExprOf[Long] }) } - // case t: ShortRef => - // if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Short] }) } - // else '{ $out.value(${ aE.asExprOf[Short] }) } + if isStringified then + '{ + if $in.readChar() != '"' then throw ParseError("Quoted integer value expected") + val v = $in.expectInt() + if $in.readChar() != '"' then throw ParseError("Close quotes expected") + v + }.asExprOf[T] + else '{ $in.expectInt() }.asExprOf[T] + case t: LongRef => + if isStringified then + '{ + if $in.readChar() != '"' then throw ParseError("Quoted long value expected") + val v = $in.expectLong() + if $in.readChar() != '"' then throw ParseError("Close quotes expected") + v + }.asExprOf[T] + else '{ $in.expectLong() }.asExprOf[T] + case t: ShortRef => + if isStringified then + '{ + if $in.readChar() != '"' then throw ParseError("Quoted integer value expected") + val v = $in.expectInt().toShort + if $in.readChar() != '"' then throw ParseError("Close quotes expected") + v + }.asExprOf[T] + else '{ $in.expectInt().toShort }.asExprOf[T] case t: StringRef => - '{ $in.expectString().toString }.asExprOf[T] - // if cfg.escapedStrings then '{ $out.value(StringEscapeUtils.escapeJson(${ aE.asExprOf[String] })) } - // else '{ $out.value(${ aE.asExprOf[String] }) } + '{ $in.expectString() }.asExprOf[T] + + case t: JBigDecimalRef => + if isStringified then '{ new java.math.BigDecimal($in.expectString()) }.asExprOf[T] + else + '{ + $in.mark() + $in.skipNumber() + new java.math.BigDecimal($in.captureMark()) + }.asExprOf[T] + case t: JBigIntegerRef => + if isStringified then '{ new java.math.BigInteger($in.expectString()) }.asExprOf[T] + else + '{ + $in.mark() + $in.skipNumber() + new java.math.BigInteger($in.captureMark()) + }.asExprOf[T] + case t: JBooleanRef => + if isStringified then + '{ + if $in.readChar() != '"' then throw ParseError("Quoted boolean value expected") + val v = java.lang.Boolean.valueOf($in.expectBoolean()) + if $in.readChar() != '"' then throw ParseError("Close quotes expected") + v + }.asExprOf[T] + else '{ java.lang.Boolean.valueOf($in.expectBoolean()) }.asExprOf[T] + case t: JByteRef => + if isStringified then + '{ + if $in.readChar() != '"' then throw ParseError("Quoted integer value expected") + val v = java.lang.Byte.valueOf($in.expectInt().toByte) + if $in.readChar() != '"' then throw ParseError("Close quotes expected") + v + }.asExprOf[T] + else '{ java.lang.Byte.valueOf($in.expectInt().toByte) }.asExprOf[T] + case t: JCharacterRef => + '{ + val c = $in.expectString() + if c.length == 0 then throw ParseError("Character value expected but empty string found in json") + else java.lang.Character.valueOf(c.charAt(0)) + }.asExprOf[T] + case t: JDoubleRef => + if isStringified then + '{ + if $in.readChar() != '"' then throw ParseError("Quoted double value expected") + val v = java.lang.Double.valueOf($in.expectDouble()) + if $in.readChar() != '"' then throw ParseError("Close quotes expected") + v + }.asExprOf[T] + else '{ java.lang.Double.valueOf($in.expectDouble()) }.asExprOf[T] + case t: JFloatRef => + if isStringified then + '{ + if $in.readChar() != '"' then throw ParseError("Quoted float value expected") + val v = java.lang.Float.valueOf($in.expectFloat()) + if $in.readChar() != '"' then throw ParseError("Close quotes expected") + v + }.asExprOf[T] + else '{ java.lang.Float.valueOf($in.expectFloat()) }.asExprOf[T] + case t: JIntegerRef => + if isStringified then + '{ + if $in.readChar() != '"' then throw ParseError("Quoted ineger value expected") + val v = java.lang.Integer.valueOf($in.expectInt()) + if $in.readChar() != '"' then throw ParseError("Close quotes expected") + v + }.asExprOf[T] + else '{ java.lang.Integer.valueOf($in.expectInt()) }.asExprOf[T] + case t: JLongRef => + if isStringified then + '{ + if $in.readChar() != '"' then throw ParseError("Quoted long value expected") + val v = java.lang.Long.valueOf($in.expectLong()) + if $in.readChar() != '"' then throw ParseError("Close quotes expected") + v + }.asExprOf[T] + else '{ java.lang.Long.valueOf($in.expectLong()) }.asExprOf[T] + case t: JShortRef => + if isStringified then + '{ + if $in.readChar() != '"' then throw ParseError("Quoted integer value expected") + val v = java.lang.Short.valueOf($in.expectInt().toShort) + if $in.readChar() != '"' then throw ParseError("Close quotes expected") + v + }.asExprOf[T] + else '{ java.lang.Short.valueOf($in.expectInt().toShort) }.asExprOf[T] + case t: JNumberRef => + if isStringified then + '{ + if $in.readChar() != '"' then throw ParseError("Quoted integer value expected") + $in.mark() + $in.skipNumber() + val v = scala.math.BigDecimal($in.captureMark()) match { + case d if d.isValidByte => java.lang.Byte.valueOf(d.toByteExact) + case d if d.isValidShort => java.lang.Short.valueOf(d.toShortExact) + case d if d.isValidInt => java.lang.Integer.valueOf(d.toIntExact) + case d if d.isValidLong => java.lang.Long.valueOf(d.toLongExact) + case d if d.isDecimalFloat => java.lang.Float.valueOf(d.toFloat) + case d if d.isDecimalDouble => java.lang.Double.valueOf(d.toDouble) + case d => d + } + if $in.readChar() != '"' then throw ParseError("Close quotes expected") + v + }.asExprOf[T] + else + '{ + $in.mark() + $in.skipNumber() + scala.math.BigDecimal($in.captureMark()) match { + case d if d.isValidByte => java.lang.Byte.valueOf(d.toByteExact) + case d if d.isValidShort => java.lang.Short.valueOf(d.toShortExact) + case d if d.isValidInt => java.lang.Integer.valueOf(d.toIntExact) + case d if d.isValidLong => java.lang.Long.valueOf(d.toLongExact) + case d if d.isDecimalFloat => java.lang.Float.valueOf(d.toFloat) + case d if d.isDecimalDouble => java.lang.Double.valueOf(d.toDouble) + case d => d + } + }.asExprOf[T] + + case t: DurationRef => '{ java.time.Duration.parse($in.expectString()) }.asExprOf[T] + case t: InstantRef => '{ java.time.Instant.parse($in.expectString()) }.asExprOf[T] + case t: LocalDateRef => '{ java.time.LocalDate.parse($in.expectString()) }.asExprOf[T] + case t: LocalDateTimeRef => '{ java.time.LocalDateTime.parse($in.expectString()) }.asExprOf[T] + case t: LocalTimeRef => '{ java.time.LocalTime.parse($in.expectString()) }.asExprOf[T] + case t: MonthDayRef => '{ java.time.MonthDay.parse($in.expectString()) }.asExprOf[T] + case t: OffsetDateTimeRef => '{ java.time.OffsetDateTime.parse($in.expectString()) }.asExprOf[T] + case t: OffsetTimeRef => '{ java.time.OffsetTime.parse($in.expectString()) }.asExprOf[T] + case t: PeriodRef => '{ java.time.Period.parse($in.expectString()) }.asExprOf[T] + case t: YearRef => '{ java.time.Year.parse($in.expectString()) }.asExprOf[T] + case t: YearMonthRef => '{ java.time.YearMonth.parse($in.expectString()) }.asExprOf[T] + case t: ZonedDateTimeRef => '{ java.time.ZonedDateTime.parse($in.expectString()) }.asExprOf[T] + case t: ZoneIdRef => '{ java.time.ZoneId.of($in.expectString()) }.asExprOf[T] + case t: ZoneOffsetRef => '{ java.time.ZoneOffset.of($in.expectString()) }.asExprOf[T] + + case t: URLRef => '{ new java.net.URL($in.expectString()) }.asExprOf[T] + case t: URIRef => '{ new java.net.URI($in.expectString()) }.asExprOf[T] + case t: UUIDRef => '{ java.util.UUID.fromString($in.expectString()) }.asExprOf[T] + + case t: AliasRef[?] => + // Special check for RawJson pseudo-type + if lastPart(t.definedType) == "RawJson" then + '{ + $in.mark() + $in.skipValue() + $in.captureMark() + }.asExprOf[T] + else + t.unwrappedType.refType match + case '[e] => + genReadVal[e]( + t.unwrappedType.asInstanceOf[RTypeRef[e]], + in, + isStringified, + inTuple + ).asExprOf[T] + + // -------------------- + // Enumerations... + // -------------------- + case t: ScalaEnumRef[?] => + import quotes.reflect.* + t.refType match + case '[e] => + '{ + $in.expectEnum() match + case null => null + case s: String => + ${ + val typeRepr = TypeRepr.of[e] + val m = typeRepr.typeSymbol.companionClass.declaredMethod("valueOf") + Apply(Ref(m(0)), List('{ s }.asTerm)).asExpr + } + case v: Int => + ${ + val typeRepr = TypeRepr.of[e] + val m = typeRepr.typeSymbol.companionClass.declaredMethod("fromOrdinal") + Apply(Ref(m(0)), List('{ v }.asTerm)).asExpr + } + }.asExprOf[T] + case t: ScalaEnumerationRef[?] => + import quotes.reflect.* + t.refType match + case '[e] => + '{ + $in.expectEnum() match + case null => null + case s: String => + ${ + val enumeration = TypeRepr.of[e] match + case TypeRef(ct, _) => Ref(ct.termSymbol).asExprOf[Enumeration] + '{ ${ enumeration }.values.iterator.find(_.toString == s).get }.asExprOf[e] + } + case v: Int => + ${ + val enumeration = TypeRepr.of[e] match + case TypeRef(ct, _) => Ref(ct.termSymbol).asExprOf[Enumeration] + '{ ${ enumeration }.values.iterator.find(_.id == v).get }.asExprOf[e] + } + }.asExprOf[T] + case t: JavaEnumRef[?] => + import quotes.reflect.* + t.refType match + case '[e] => + '{ + $in.expectEnum() match + case null => null + case s: String => + ${ + val typeRepr = TypeRepr.of[e] + val m = typeRepr.classSymbol.get.companionModule.methodMember("valueOf") + Apply(Ref(m(0)), List('{ s }.asTerm)).asExpr + } + case v: Int => + throw ParseError("Ordinal value initiation not valid for Java Enums") + }.asExprOf[T] // -------------------- // Collections... @@ -1064,7 +1331,7 @@ object JsonCodecMaker: // } def encodeValue(in: T, out: JsonOutput): Unit = ${ genWriteVal('in, ref, 'out) } - def decodeValue(in: JsonSource): T = null.asInstanceOf[T] // ${ genReadVal(ref, 'in) } + def decodeValue(in: JsonSource): T = ${ genReadVal(ref, 'in) } } }.asTerm val neededDefs = diff --git a/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala b/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala index 2cdd497d..8ea7730c 100644 --- a/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala +++ b/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala @@ -15,28 +15,22 @@ object JsonSource: // ZIO-Json defines a series of different Readers. Not exactly sure why--maybe to support different // modes (streaming, ...)? At least for now we only need one, so merged key bits of Readers into one. case class JsonSource(js: CharSequence): - var i = 0 - private var expectFieldValue = false - private[json] val max = js.length + + private var i = 0 + private var _mark = 0 + val max = js.length + + // Navigation... + // ======================================================= def pos = i - // inline def here = js(i).toChar inline def here = js.charAt(i) - inline def revert = i -= 1 - - private var c: Char = 0 - inline def readChar(): Char = - if i < max then - c = here - i += 1 - c - else BUFFER_EXCEEDED + inline def retract() = i -= 1 - // JSON definition of whitespace - private inline def isWhitespace(c: Char): Boolean = - c == ' ' || c == '\n' || (c | 0x4) == '\r' || c == '\t' + inline def mark() = _mark = i + inline def captureMark(): String = js.subSequence(_mark, i).toString @tailrec final def readToken(): Char = @@ -47,49 +41,41 @@ case class JsonSource(js: CharSequence): if !(b == ' ' || b == '\n' || b == '\t' || (b | 0x4) == '\r') then b else readToken() - inline def skipWS(): Unit = - while isWhitespace(here) && i < max do i += 1 - if i == max then throw new JsonParseError("Unexpected end of buffer", this) - - inline def retract() = i -= 1 - - @inline private[this] def isNumber(c: Char): Boolean = - (c: @switch) match - case '+' | '-' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '.' | 'e' | 'E' => true - case _ => false + // inline def nextHex4(): Char = + // var i: Int = 0 + // var accum: Int = 0 + // while i < 4 do + // var c = readChar().toInt + // if c == BUFFER_EXCEEDED then throw JsonParseError("Unexpected end of buffer", this) + // c = + // if '0' <= c && c <= '9' then c - '0' + // else if 'A' <= c && c <= 'F' then c - 'A' + 10 + // else if 'a' <= c && c <= 'f' then c - 'a' + 10 + // else throw JsonParseError("Invalid hex character in string", this) + // accum = accum * 16 + c + // i += 1 + // accum.toChar + + // Enum... + // ======================================================= + def expectEnum(): Int | String = + readToken() match + case t if t >= '0' && t <= '9' => + retract() + expectInt() + case t if t == '"' => + val endI = parseString(i) + val str = js.subSequence(i, endI).toString + i = endI + 1 + str + case t if t == 'n' => + readChars(JsonSource.ull, "null") + null.asInstanceOf[String] + case _ => + throw ParseError("Expected valid enumeration value or null here") - // Read, transforming escaped chars and stopping when we hit '"' - inline def readEscapedChar(): Char = - readChar() match - case '\\' => - val c2 = readChar() - (c2: @switch) match - case '"' | '\\' | '/' => c2 - case 'b' => '\b' - case 'f' => '\f' - case 'n' => '\n' - case 'r' => '\r' - case 't' => '\t' - case 'u' => nextHex4() - case _ => throw JsonParseError(s"Invalid '\\${c2.toChar}' in string", this) - case '"' => END_OF_STRING - case BUFFER_EXCEEDED => throw new JsonParseError("Unexpected end of buffer", this) - case c => c - - inline def nextHex4(): Char = - var i: Int = 0 - var accum: Int = 0 - while i < 4 do - var c = readChar().toInt - if c == BUFFER_EXCEEDED then throw JsonParseError("Unexpected end of buffer", this) - c = - if '0' <= c && c <= '9' then c - '0' - else if 'A' <= c && c <= 'F' then c - 'A' + 10 - else if 'a' <= c && c <= 'f' then c - 'a' + 10 - else throw JsonParseError("Invalid hex character in string", this) - accum = accum * 16 + c - i += 1 - accum.toChar + // Object... + // ======================================================= // returns false if 'null' found def expectFirstObjectField(fieldNameMatrix: StringMatrix): Option[Int] = @@ -97,7 +83,7 @@ case class JsonSource(js: CharSequence): if t == '{' then val tt = readToken() if tt == '"' then - val foundIndex = parseFieldName(fieldNameMatrix) + val foundIndex = parseObjectKey(fieldNameMatrix) Some(foundIndex) else if tt == '}' then None else throw new JsonParseError(s"Expected object field name or '}' but found '$tt'", this) @@ -111,15 +97,33 @@ case class JsonSource(js: CharSequence): if t == ',' then val tt = readToken() if tt == '"' then - val foundIndex = parseFieldName(fieldNameMatrix) + val foundIndex = parseObjectKey(fieldNameMatrix) Some(foundIndex) else throw new JsonParseError(s"Expected object field name but found '$tt'", this) else if t == '}' then None else throw new JsonParseError(s"Expected ',' or '}' but found $t", this) // returns false if 'null' found + final def parseObjectKey(fieldNameMatrix: StringMatrix): Int = // returns index of field name or -1 if not found + var fi: Int = 0 + var bs: Long = fieldNameMatrix.initial + var c: Int = here + while c != '"' do { + bs = fieldNameMatrix.update(bs, fi, c) + fi += 1 + i += 1 + c = here + } + i += 1 + bs = fieldNameMatrix.exact(bs, fi) + if readToken() != ':' then throw new JsonParseError(s"Expected ':' field separator but found $here", this) + fieldNameMatrix.first(bs) + + // Array... + // ======================================================= + @tailrec - final private[this] def addAllArray[E](s: scala.collection.mutable.ListBuffer[E], f: () => E): scala.collection.mutable.ListBuffer[E] = + final private def addAllArray[E](s: scala.collection.mutable.ListBuffer[E], f: () => E): scala.collection.mutable.ListBuffer[E] = if i == max then throw JsonParseError("Unexpected end of buffer", this) s.addOne(f()) val tt = readToken() @@ -142,67 +146,17 @@ case class JsonSource(js: CharSequence): null else throw JsonParseError(s"Expected array start '[' or null but got '$t'", this) - // --------------- - // DEPRECATED !!! - // --------------- - // True if we got anything besides a ], False for ] - def firstArrayElement(): Boolean = - (readCharWS(): @switch) match - case ']' => false - case _ => - retract() - true - - // --------------- - // DEPRECATED !!! - // --------------- - // True if we got a comma, and False for ] - def nextArrayElement(): Boolean = - (readCharWS(): @switch) match - case ',' => - true - case ']' => - false - case c => throw JsonParseError(s"Expected ',' or ']' got '$c'", this) - - // --------------- - // DEPRECATED !!! - // --------------- - // True if we got a string (implies a retraction), False for } - def firstField(): Boolean = - (readCharWS(): @switch) match { - case '"' => true - case '}' => false - case c => - throw JsonParseError(s"expected string or '}' got '$c'", this) - } - - // --------------- - // DEPRECATED !!! - // --------------- - // True if we got a comma, and False for } - def nextField(): Boolean = - (readCharWS(): @switch) match { - case ',' => - expectFieldValue = false - true - // case '}' if !expectFieldValue => - // false - case '}' => - false - // throw JsonParseError("Expected field value but got '}' instead.", this) - case c => - throw JsonParseError(s"expected ',' or '}' got '$c'", this) - } + // String... + // ======================================================= // Value might be null! // expectString() will look for leading '"'. parseString() presumes the '"' has already been consumed. - inline def expectString(): CharSequence = + inline def expectString(): String = val t = readToken() if t == '"' then val endI = parseString(i) if endI >= 0 then - val str = js.subSequence(i, endI) + val str = js.subSequence(i, endI).toString i = endI + 1 str else ??? // slower-parseString looking for escaped special chars @@ -228,26 +182,8 @@ case class JsonSource(js: CharSequence): else if (b - 0x20 ^ 0x3c) <= 0 then -1 // special char found else parseString(pos + 1) - final def parseFieldName(fieldNameMatrix: StringMatrix): Int = // returns index of field name or -1 if not found - var fi: Int = 0 - var bs: Long = fieldNameMatrix.initial - var c: Int = here - while c != '"' do { - bs = fieldNameMatrix.update(bs, fi, c) - fi += 1 - i += 1 - c = here - } - i += 1 - bs = fieldNameMatrix.exact(bs, fi) - if readToken() != ':' then throw new JsonParseError(s"Expected ':' field separator but found $here", this) - fieldNameMatrix.first(bs) - - inline def expectChar(): Char = - expectString() match { - case s if s.length == 1 => s.charAt(0) - case s => throw new JsonParseError(s"Expected a Char value but got '$s'", this) - } + // Boolean... + // ======================================================= def expectBoolean(): Boolean = skipWS() @@ -260,84 +196,120 @@ case class JsonSource(js: CharSequence): false else throw JsonParseError(s"Expected 'true' or 'false'", this) - def expectInt(): Int = { - var b = readCharWS() + // Characters... + // ======================================================= + + private var c: Char = 0 + inline def readChar(): Char = + if i < max then + c = here + i += 1 + c + else BUFFER_EXCEEDED + + inline def readChars( + expect: Array[Char], + errMsg: String + ): Unit = + var i: Int = 0 + while i < expect.length do + if readChar() != expect(i) then throw JsonParseError(s"Expected $errMsg", this) + i += 1 + + inline def expectChar(): Char = + expectString() match { + case s if s.length == 1 => s.charAt(0) + case s => throw new JsonParseError(s"Expected a Char value but got '$s'", this) + } + + // Numbers... + // ======================================================= + + @inline private def isNumber(c: Char): Boolean = + (c: @switch) match + case '+' | '-' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '.' | 'e' | 'E' => true + case _ => false + + def expectFloat(): Float = + val result = UnsafeNumbers.float_(this, false, 32) + retract() + result + + def expectDouble(): Double = + val result = UnsafeNumbers.double_(this, false, 64) + retract() + result + + // BUG: Can't handle single-digit ints... + def expectInt(): Int = + var b = readToken() var s = -1 - if here == '-' then { - readChar() + if b == '-' then + b = readChar() s = 0 - } - if b < '0' || b > '9' then throw new Exception("Boom 1") + if b < '0' || b > '9' then throw ParseError("Non-numeric digit found when integer value expected") var x = '0' - b while { b = readChar(); b >= '0' && b <= '9' } do if x < -214748364 || { x = x * 10 + ('0' - b) x > 0 } - then throw new Exception("Boom 2") + then throw ParseError("Integer value overflow") x ^= s x -= s - if (s & x) == -2147483648 then throw new Exception("Boom 3") - if (b | 0x20) == 'e' || b == '.' then throw new Exception("Boom 4") + if (s & x) == -2147483648 then throw ParseError("Integer value overflow") + if (b | 0x20) == 'e' || b == '.' then throw ParseError("Decimal digit 'e' or '.' found when integer value expected") retract() x - } - inline def readChars( - expect: Array[Char], - errMsg: String - ): Unit = - var i: Int = 0 - while i < expect.length do - if readChar() != expect(i) then throw JsonParseError(s"Expected $errMsg", this) - i += 1 + def expectLong(): Long = + val result = UnsafeNumbers.long_(this, false) + retract() + result - // --------------- - // DEPRECATED !!! - // --------------- - inline def readCharWS(): Char = - while isWhitespace(here) && i < max do i += 1 - readChar() + // Skip things... + // ======================================================= - // --------------- - // DEPRECATED !!! - // --------------- - inline def charWithWS(c: Char): Unit = - val got = readCharWS() - if got != c then throw JsonParseError(s"Expected '$c' got '$got'", this) + inline def skipWS(): Unit = + while (here == ' ' || here == '\n' || (here | 0x4) == '\r' || here == '\t') && i < max do i += 1 + if i == max then throw new JsonParseError("Unexpected end of buffer", this) def skipValue(): Unit = - (readCharWS(): @switch) match { + (readToken(): @switch) match { case 'n' => readChars(JsonSource.ull, "null") case 'f' => readChars(JsonSource.alse, "false") case 't' => readChars(JsonSource.rue, "true") - case '{' => - if firstField() then { - while { - { - charWithWS('"') - skipString() - charWithWS(':') - skipValue() - }; nextField() - } do () - } - case '[' => - if firstArrayElement() then { - while { skipValue(); nextArrayElement() } do () - } - case '"' => - skipString() + case '{' => skipObjectValue() + case '[' => skipArrayValue() + case '"' => parseString(i) case '-' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '.' => skipNumber() case c => throw JsonParseError(s"Unexpected '$c'", this) } - def skipNumber(): Unit = { - while isNumber(readChar()) do {} - retract() - } + @tailrec + final def skipNumber(): Unit = + if !isNumber(readChar()) then retract() + else skipNumber() - def skipString(): Unit = - var i: Int = 0 - while { i = readCharWS(); i != -1 } do () + @tailrec + final def skipArrayValue(k: Int = 0): Unit = + readChar() match + case ']' if k == 0 => () + case '"' => + i = parseString(i) + 1 + skipArrayValue(k) + case ']' => skipArrayValue(k - 1) + case '[' => skipArrayValue(k + 1) + case _ => skipArrayValue(k) + + @tailrec + final def skipObjectValue(k: Int = 0): Unit = + readChar() match + case '}' if k == 0 => () + case '"' => + i = parseString(i) + 1 + skipObjectValue(k) + case '}' => skipObjectValue(k - 1) + case '{' => skipObjectValue(k + 1) + case _ => skipObjectValue(k) diff --git a/src/main/scala/co.blocke.scalajack/json/schema/JsonSchema.scala b/src/main/scala/co.blocke.scalajack/json/schema/JsonSchema.scala index 5c6d4e59..3fb038d3 100644 --- a/src/main/scala/co.blocke.scalajack/json/schema/JsonSchema.scala +++ b/src/main/scala/co.blocke.scalajack/json/schema/JsonSchema.scala @@ -13,281 +13,314 @@ import scala.quoted.* object JsonSchema: - inline def of[T]: Schema = ${ ofImpl[T]() } - def ofImpl[T]()(using t: Type[T])(using quotes: Quotes): Expr[Schema] = - import quotes.reflect.* - val classRef = ReflectOnType[T](quotes)(TypeRepr.of[T], true)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) - genSchema(quotes)( classRef, None) + inline def of[T](overrides: Map[String, Schema]): Schema = ${ ofImpl[T]('overrides) } - private def genSchema[T](quotes: Quotes)(rt: RTypeRef[T], context: Option[ScalaFieldInfoRef] = None, defaultValue: Option[quotes.reflect.Term] = None): Expr[Schema] = - import quotes.reflect.* - implicit val q: Quotes = quotes + inline def of[T]: Schema = ${ ofImpl[T]() } - def ofOption[T](xs: Option[Expr[T]])(using Type[T])(using Quotes): Expr[Option[T]] = - if xs.isEmpty then Expr(None) else '{ Some(${xs.get}) } + def ofImpl[T]()(using t: Type[T])(using quotes: Quotes): Expr[Schema] = + import quotes.reflect.* + val classRef = ReflectOnType[T](quotes)(TypeRepr.of[T], true)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) + genSchema(quotes)(classRef, None, '{ Map.empty[String, Schema] }, None, true) - def typeArgs(tpe: TypeRepr): List[TypeRepr] = tpe match - case AppliedType(_, typeArgs) => typeArgs.map(_.dealias) - case _ => Nil + def ofImpl[T](overrides: Expr[Map[String, Schema]])(using t: Type[T])(using quotes: Quotes): Expr[Schema] = + import quotes.reflect.* + val classRef = ReflectOnType[T](quotes)(TypeRepr.of[T], true)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) + genSchema(quotes)(classRef, None, overrides, None, true) - rt match - case t: AliasRef[_] => - t.unwrappedType.refType match + private def genSchema[T](quotes: Quotes)( + rt: RTypeRef[T], + context: Option[ScalaFieldInfoRef] = None, + overrides: Expr[Map[String, Schema]], + defaultValue: Option[quotes.reflect.Term] = None, + isInitialSchema: Boolean = false + ): Expr[Schema] = + import quotes.reflect.* + implicit val q: Quotes = quotes + + def ofOption[T](xs: Option[Expr[T]])(using Type[T])(using Quotes): Expr[Option[T]] = + if xs.isEmpty then Expr(None) else '{ Some(${ xs.get }) } + + def typeArgs(tpe: TypeRepr): List[TypeRepr] = tpe match + case AppliedType(_, typeArgs) => typeArgs.map(_.dealias) + case _ => Nil + + val rtName = Expr(rt.name) + '{ + $overrides + .get($rtName) + .getOrElse(${ + rt match + case t: AliasRef[?] => + t.unwrappedType.refType match + case '[e] => + genSchema[e](quotes)(t.unwrappedType.asInstanceOf[RTypeRef[e]], context, overrides, defaultValue) + case t: ArrayRef[?] => + t.refType match + case '[u] => + t.elementRef.refType match case '[e] => - genSchema[e](quotes)(t.unwrappedType.asInstanceOf[RTypeRef[e]], context, defaultValue) - case t: ArrayRef[_] => - t.refType match - case '[u] => - t.elementRef.refType match - case '[e] => - '{ - ArraySchema( - ${ genSchema[e](quotes)(t.elementRef.asInstanceOf[RTypeRef[e]], context, None) }, - ${ Expr(context.flatMap(_.annotations.get("minItems")).flatMap(_.get("value")).map(_.toInt)) }, - ${ Expr(context.flatMap(_.annotations.get("maxItems")).flatMap(_.get("value")).map(_.toInt)) }, - ${ Expr(context.flatMap(_.annotations.get("uniqueItems")).flatMap(_.get("value")).map(_.toBoolean)) }, - ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, - ${ - if defaultValue.isDefined then - val codec = JsonCodecMaker.generateCodecFor[u](t.refType.asInstanceOf[RTypeRef[u]], json.JsonConfig) - '{ - val out = new writing.JsonOutput() - $codec.encodeValue(${defaultValue.get.asExprOf[u]}, out) - Some(out.toString.asInstanceOf[RawJson]) - } - else - Expr(None) - } - ) - } - case t: EnumRef[_] => '{ EnumSchema( ${Expr(t.values)} ) } - case t: OptionRef[_] => - // Go ahead and gen schema for body of Option. Higher level (ie class) is resposible for tracking if a - // value is required or not... - t.optionParamType.refType match + '{ + ArraySchema( + ${ genSchema[e](quotes)(t.elementRef.asInstanceOf[RTypeRef[e]], context, overrides, None) }, + ${ Expr(context.flatMap(_.annotations.get("minItems")).flatMap(_.get("value")).map(_.toInt)) }, + ${ Expr(context.flatMap(_.annotations.get("maxItems")).flatMap(_.get("value")).map(_.toInt)) }, + ${ Expr(context.flatMap(_.annotations.get("uniqueItems")).flatMap(_.get("value")).map(_.toBoolean)) }, + ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, + ${ + if defaultValue.isDefined then + val codec = JsonCodecMaker.generateCodecFor[u](t.refType.asInstanceOf[RTypeRef[u]], json.JsonConfig) + '{ + val out = new writing.JsonOutput() + $codec.encodeValue(${ defaultValue.get.asExprOf[u] }, out) + Some(out.toString.asInstanceOf[RawJson]) + } + else Expr(None) + } + ) + } + case t: EnumRef[?] => '{ EnumSchema(${ Expr(t.values) }) } + case t: OptionRef[?] => + // Go ahead and gen schema for body of Option. Higher level (ie class) is resposible for tracking if a + // value is required or not... + t.optionParamType.refType match + case '[e] => + defaultValue + .map { dv => + val dve = dv.asExprOf[Option[e]] + '{ + $dve match + case Some(a) => ${ genSchema[e](quotes)(t.optionParamType.asInstanceOf[RTypeRef[e]], context, overrides, Some('{ a }.asTerm)) } + case None => ${ genSchema[e](quotes)(t.optionParamType.asInstanceOf[RTypeRef[e]], context, overrides, None) } + } + } + .getOrElse(genSchema[e](quotes)(t.optionParamType.asInstanceOf[RTypeRef[e]], context, overrides, None)) + case t: TryRef[?] => + // Go ahead and gen schema for body of Try. Higher level (ie class) is resposible for tracking if a + // value is required or not... + t.tryRef.refType match + case '[e] => + genSchema[e](quotes)(t.tryRef.asInstanceOf[RTypeRef[e]], context, overrides, None) + case t: SeqRef[?] => + t.refType match + case '[u] => + t.elementRef.refType match case '[e] => - defaultValue.map{ dv => - val dve = dv.asExprOf[Option[e]] - '{ - $dve match - case Some(a) => ${ genSchema[e](quotes)(t.optionParamType.asInstanceOf[RTypeRef[e]], context, Some('{a}.asTerm)) } - case None => ${ genSchema[e](quotes)(t.optionParamType.asInstanceOf[RTypeRef[e]], context, None) } - } - }.getOrElse( genSchema[e](quotes)(t.optionParamType.asInstanceOf[RTypeRef[e]], context, None) ) - case t: TryRef[_] => - // Go ahead and gen schema for body of Try. Higher level (ie class) is resposible for tracking if a - // value is required or not... - t.tryRef.refType match + '{ + ArraySchema( + ${ genSchema[e](quotes)(t.elementRef.asInstanceOf[RTypeRef[e]], context, overrides, None) }, + ${ Expr(context.flatMap(_.annotations.get("minItems")).flatMap(_.get("value")).map(_.toInt)) }, + ${ Expr(context.flatMap(_.annotations.get("maxItems")).flatMap(_.get("value")).map(_.toInt)) }, + ${ Expr(context.flatMap(_.annotations.get("uniqueItems")).flatMap(_.get("value")).map(_.toBoolean)) }, + ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, + ${ + if defaultValue.isDefined then + val codec = JsonCodecMaker.generateCodecFor[u](t.asInstanceOf[RTypeRef[u]], json.JsonConfig) + '{ + val out = new writing.JsonOutput() + $codec.encodeValue(${ defaultValue.get.asExprOf[u] }, out) + Some(out.result.asInstanceOf[RawJson]) + } + else Expr(None) + } + ) + } + case t: SetRef[?] => + t.refType match + case '[u] => + t.elementRef.refType match case '[e] => - genSchema[e](quotes)(t.tryRef.asInstanceOf[RTypeRef[e]], context, None) - case t: SeqRef[_] => - t.refType match - case '[u] => - t.elementRef.refType match - case '[e] => - '{ - ArraySchema( - ${ genSchema[e](quotes)(t.elementRef.asInstanceOf[RTypeRef[e]], context, None) }, - ${ Expr(context.flatMap(_.annotations.get("minItems")).flatMap(_.get("value")).map(_.toInt)) }, - ${ Expr(context.flatMap(_.annotations.get("maxItems")).flatMap(_.get("value")).map(_.toInt)) }, - ${ Expr(context.flatMap(_.annotations.get("uniqueItems")).flatMap(_.get("value")).map(_.toBoolean)) }, - ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, - ${ - if defaultValue.isDefined then - val codec = JsonCodecMaker.generateCodecFor[u](t.asInstanceOf[RTypeRef[u]], json.JsonConfig) - '{ - val out = new writing.JsonOutput() - $codec.encodeValue(${defaultValue.get.asExprOf[u]}, out) - Some(out.result.asInstanceOf[RawJson]) - } - else - Expr(None) - } - ) - } - case t: SetRef[_] => - t.refType match - case '[u] => - t.elementRef.refType match - case '[e] => - '{ - ArraySchema( - ${ genSchema[e](quotes)(t.elementRef.asInstanceOf[RTypeRef[e]], context, None) }, - ${ Expr(context.flatMap(_.annotations.get("minItems")).flatMap(_.get("value")).map(_.toInt)) }, - ${ Expr(context.flatMap(_.annotations.get("maxItems")).flatMap(_.get("value")).map(_.toInt)) }, - ${ Expr(context.flatMap(_.annotations.get("uniqueItems")).flatMap(_.get("value")).map(_.toBoolean)) }, - ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, - ${ - if defaultValue.isDefined then - val codec = JsonCodecMaker.generateCodecFor[u](t.asInstanceOf[RTypeRef[u]], json.JsonConfig) - '{ - val out = new writing.JsonOutput() - $codec.encodeValue(${defaultValue.get.asExprOf[u]}, out) - Some(out.result.asInstanceOf[RawJson]) - } - else - Expr(None) - } - ) - } - case t: TupleRef[_] => - t.refType match - case '[u] => - '{ - TupleSchema( - ${ - Expr.ofList(t.tupleRefs.map{ tr => - tr.refType match - case '[w] => - genSchema[w](quotes)(tr.asInstanceOf[RTypeRef[w]], context, None) - }) - }, - ${ Expr(context.flatMap(_.annotations.get("items")).flatMap(_.get("value")).map(_.toBoolean)) }, - ${ Expr(context.flatMap(_.annotations.get("minItems")).flatMap(_.get("value")).map(_.toInt)) }, - ${ Expr(context.flatMap(_.annotations.get("maxItems")).flatMap(_.get("value")).map(_.toInt)) }, - ${ Expr(context.flatMap(_.annotations.get("uniqueItems")).flatMap(_.get("value")).map(_.toBoolean)) }, - ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, - ${ - if defaultValue.isDefined then - val codec = JsonCodecMaker.generateCodecFor[u](t.asInstanceOf[RTypeRef[u]], json.JsonConfig) - '{ - val out = new writing.JsonOutput() - $codec.encodeValue(${defaultValue.get.asExprOf[u]}, out) - Some(out.result.asInstanceOf[RawJson]) - } - else - Expr(None) - } - ) - } - case t: BooleanRef => - '{ - BooleanSchema( - ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, - ${ - ofOption(defaultValue.map(v => '{ ${v.asExprOf[Boolean]}.toString.asInstanceOf[RawJson] })) - } - ) - } - case t: DoubleRef => - '{ - NumberSchema( - ${ Expr(context.flatMap(_.annotations.get("minimum")).flatMap(_.get("value")).map(_.toDouble)) }, - ${ Expr(context.flatMap(_.annotations.get("maximum")).flatMap(_.get("value")).map(_.toDouble)) }, - ${ Expr(context.flatMap(_.annotations.get("exclusiveMinimum")).flatMap(_.get("value")).map(_.toDouble)) }, - ${ Expr(context.flatMap(_.annotations.get("exclusiveMaximum")).flatMap(_.get("value")).map(_.toDouble)) }, - ${ Expr(context.flatMap(_.annotations.get("multipleOf")).flatMap(_.get("value")).map(_.toInt)) }, - ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, - ${ - ofOption(defaultValue.map(v => '{ ${v.asExprOf[Double]}.toString.asInstanceOf[RawJson] })) - } - ) - } - case t: IntRef => - '{ - IntegerSchema( - ${ Expr(context.flatMap(_.annotations.get("minimum")).flatMap(_.get("value")).map(_.toInt)) }, - ${ Expr(context.flatMap(_.annotations.get("maximum")).flatMap(_.get("value")).map(_.toInt)) }, - ${ Expr(context.flatMap(_.annotations.get("exclusiveMinimum")).flatMap(_.get("value")).map(_.toInt)) }, - ${ Expr(context.flatMap(_.annotations.get("exclusiveMaximum")).flatMap(_.get("value")).map(_.toInt)) }, - ${ Expr(context.flatMap(_.annotations.get("multipleOf")).flatMap(_.get("value")).map(_.toInt)) }, - ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, - ${ - ofOption(defaultValue.map(v => '{ ${v.asExprOf[Int]}.toString.asInstanceOf[RawJson] })) - } - ) - } - case t: LongRef => - '{ - IntegerSchema( - ${ Expr(context.flatMap(_.annotations.get("minimum")).flatMap(_.get("value")).map(_.toLong)) }, - ${ Expr(context.flatMap(_.annotations.get("maximum")).flatMap(_.get("value")).map(_.toLong)) }, - ${ Expr(context.flatMap(_.annotations.get("exclusiveMinimum")).flatMap(_.get("value")).map(_.toLong)) }, - ${ Expr(context.flatMap(_.annotations.get("exclusiveMaximum")).flatMap(_.get("value")).map(_.toLong)) }, - ${ Expr(context.flatMap(_.annotations.get("multipleOf")).flatMap(_.get("value")).map(_.toInt)) }, - ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, - ${ - ofOption(defaultValue.map(v => '{ ${v.asExprOf[Long]}.toString.asInstanceOf[RawJson] })) - } - ) - } - case t: StringRef => - '{ - StringSchema( - ${ Expr(context.flatMap(_.annotations.get("minLength")).flatMap(_.get("value")).map(_.toInt)) }, - ${ Expr(context.flatMap(_.annotations.get("maxLength")).flatMap(_.get("value")).map(_.toInt)) }, - ${ Expr(context.flatMap(_.annotations.get("pattern")).flatMap(_.get("value")).map(v => StringEscapeUtils.escapeJson(v))) }, - ${ Expr(context.flatMap(_.annotations.get("format")).flatMap(_.get("value"))) }.map(v => StringFormat.valueOf(v)), - ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, - ${ - ofOption(defaultValue.map(v => '{ ("\"" + StringEscapeUtils.escapeJson(${v.asExprOf[String]}) + "\"").asInstanceOf[RawJson] })) - } + '{ + ArraySchema( + ${ genSchema[e](quotes)(t.elementRef.asInstanceOf[RTypeRef[e]], context, overrides, None) }, + ${ Expr(context.flatMap(_.annotations.get("minItems")).flatMap(_.get("value")).map(_.toInt)) }, + ${ Expr(context.flatMap(_.annotations.get("maxItems")).flatMap(_.get("value")).map(_.toInt)) }, + ${ Expr(context.flatMap(_.annotations.get("uniqueItems")).flatMap(_.get("value")).map(_.toBoolean)) }, + ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, + ${ + if defaultValue.isDefined then + val codec = JsonCodecMaker.generateCodecFor[u](t.asInstanceOf[RTypeRef[u]], json.JsonConfig) + '{ + val out = new writing.JsonOutput() + $codec.encodeValue(${ defaultValue.get.asExprOf[u] }, out) + Some(out.result.asInstanceOf[RawJson]) + } + else Expr(None) + } + ) + } + case t: TupleRef[?] => + t.refType match + case '[u] => + '{ + TupleSchema( + ${ + Expr.ofList(t.tupleRefs.map { tr => + tr.refType match + case '[w] => + genSchema[w](quotes)(tr.asInstanceOf[RTypeRef[w]], context, overrides, None) + }) + }, + ${ Expr(context.flatMap(_.annotations.get("items")).flatMap(_.get("value")).map(_.toBoolean)) }, + ${ Expr(context.flatMap(_.annotations.get("minItems")).flatMap(_.get("value")).map(_.toInt)) }, + ${ Expr(context.flatMap(_.annotations.get("maxItems")).flatMap(_.get("value")).map(_.toInt)) }, + ${ Expr(context.flatMap(_.annotations.get("uniqueItems")).flatMap(_.get("value")).map(_.toBoolean)) }, + ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, + ${ + if defaultValue.isDefined then + val codec = JsonCodecMaker.generateCodecFor[u](t.asInstanceOf[RTypeRef[u]], json.JsonConfig) + '{ + val out = new writing.JsonOutput() + $codec.encodeValue(${ defaultValue.get.asExprOf[u] }, out) + Some(out.result.asInstanceOf[RawJson]) + } + else Expr(None) + } ) - } - case t: ZonedDateTimeRef => - '{ - StringSchema( - ${ Expr(context.flatMap(_.annotations.get("minLength")).flatMap(_.get("value")).map(_.toInt)) }, - ${ Expr(context.flatMap(_.annotations.get("maxLength")).flatMap(_.get("value")).map(_.toInt)) }, - ${ Expr(context.flatMap(_.annotations.get("pattern")).flatMap(_.get("value")).map(v => StringEscapeUtils.escapeJson(v))) }, - Some(StringFormat.`date-time`), - ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, - ${ - ofOption(defaultValue.map(v => '{ ${v.asExprOf[java.time.ZonedDateTime]}.toString.asInstanceOf[RawJson] })) - } + } + case t: BooleanRef => + '{ + BooleanSchema( + ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, + ${ + ofOption(defaultValue.map(v => '{ ${ v.asExprOf[Boolean] }.toString.asInstanceOf[RawJson] })) + } + ) + } + case t: DoubleRef => + '{ + NumberSchema( + ${ Expr(context.flatMap(_.annotations.get("minimum")).flatMap(_.get("value")).map(_.toDouble)) }, + ${ Expr(context.flatMap(_.annotations.get("maximum")).flatMap(_.get("value")).map(_.toDouble)) }, + ${ Expr(context.flatMap(_.annotations.get("exclusiveMinimum")).flatMap(_.get("value")).map(_.toDouble)) }, + ${ Expr(context.flatMap(_.annotations.get("exclusiveMaximum")).flatMap(_.get("value")).map(_.toDouble)) }, + ${ Expr(context.flatMap(_.annotations.get("multipleOf")).flatMap(_.get("value")).map(_.toInt)) }, + ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, + ${ + ofOption(defaultValue.map(v => '{ ${ v.asExprOf[Double] }.toString.asInstanceOf[RawJson] })) + } + ) + } + case t: IntRef => + '{ + IntegerSchema( + ${ Expr(context.flatMap(_.annotations.get("minimum")).flatMap(_.get("value")).map(_.toInt)) }, + ${ Expr(context.flatMap(_.annotations.get("maximum")).flatMap(_.get("value")).map(_.toInt)) }, + ${ Expr(context.flatMap(_.annotations.get("exclusiveMinimum")).flatMap(_.get("value")).map(_.toInt)) }, + ${ Expr(context.flatMap(_.annotations.get("exclusiveMaximum")).flatMap(_.get("value")).map(_.toInt)) }, + ${ Expr(context.flatMap(_.annotations.get("multipleOf")).flatMap(_.get("value")).map(_.toInt)) }, + ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, + ${ + ofOption(defaultValue.map(v => '{ ${ v.asExprOf[Int] }.toString.asInstanceOf[RawJson] })) + } + ) + } + case t: LongRef => + '{ + IntegerSchema( + ${ Expr(context.flatMap(_.annotations.get("minimum")).flatMap(_.get("value")).map(_.toLong)) }, + ${ Expr(context.flatMap(_.annotations.get("maximum")).flatMap(_.get("value")).map(_.toLong)) }, + ${ Expr(context.flatMap(_.annotations.get("exclusiveMinimum")).flatMap(_.get("value")).map(_.toLong)) }, + ${ Expr(context.flatMap(_.annotations.get("exclusiveMaximum")).flatMap(_.get("value")).map(_.toLong)) }, + ${ Expr(context.flatMap(_.annotations.get("multipleOf")).flatMap(_.get("value")).map(_.toInt)) }, + ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, + ${ + ofOption(defaultValue.map(v => '{ ${ v.asExprOf[Long] }.toString.asInstanceOf[RawJson] })) + } + ) + } + case t: StringRef => + '{ + StringSchema( + ${ Expr(context.flatMap(_.annotations.get("minLength")).flatMap(_.get("value")).map(_.toInt)) }, + ${ Expr(context.flatMap(_.annotations.get("maxLength")).flatMap(_.get("value")).map(_.toInt)) }, + ${ Expr(context.flatMap(_.annotations.get("pattern")).flatMap(_.get("value")).map(v => StringEscapeUtils.escapeJson(v))) }, + ${ Expr(context.flatMap(_.annotations.get("format")).flatMap(_.get("value"))) }.map(v => StringFormat.valueOf(v)), + ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, + ${ + ofOption(defaultValue.map(v => '{ ("\"" + StringEscapeUtils.escapeJson(${ v.asExprOf[String] }) + "\"").asInstanceOf[RawJson] })) + } + ) + } + case t: ZonedDateTimeRef => + '{ + StringSchema( + ${ Expr(context.flatMap(_.annotations.get("minLength")).flatMap(_.get("value")).map(_.toInt)) }, + ${ Expr(context.flatMap(_.annotations.get("maxLength")).flatMap(_.get("value")).map(_.toInt)) }, + ${ Expr(context.flatMap(_.annotations.get("pattern")).flatMap(_.get("value")).map(v => StringEscapeUtils.escapeJson(v))) }, + Some(StringFormat.`date-time`), + ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, + ${ + ofOption(defaultValue.map(v => '{ ${ v.asExprOf[java.time.ZonedDateTime] }.toString.asInstanceOf[RawJson] })) + } + ) + } + case t: ScalaClassRef[?] => + t.refType match + case '[u] => + val requiredFields = Expr(t.fields.collect { + case f: FieldInfoRef if !f.fieldRef.isInstanceOf[OptionRef[_]] => f.name + }) + '{ + ObjectSchema( + ${ + Expr.ofList( + t.fields.map(f => + f.fieldRef.refType match + case '[b] => + // Get default value if any + val tpe = TypeRepr.of[u] + val classCompanion = tpe.typeSymbol.companionClass + val companionModule = tpe.typeSymbol.companionModule + val dvMembers = classCompanion.methodMember("$lessinit$greater$default$" + (f.index + 1)) + val fieldDefault = + if dvMembers.isEmpty then None + else + val methodSymbol = dvMembers.head + val dvSelectNoTArgs = Ref(companionModule).select(methodSymbol) + val dvSelect = methodSymbol.paramSymss match + case Nil => dvSelectNoTArgs + case List(params) if (params.exists(_.isTypeParam)) => + typeArgs(tpe) match + case Nil => ??? // throw JsonParseError("Expected an applied type", ???) + case typeArgs => TypeApply(dvSelectNoTArgs, typeArgs.map(Inferred(_))) + case _ => ??? // fail(s"Default method for ${symbol.name} of class ${tpe.show} have a complex " + + Some(dvSelect) + Expr.ofTuple((Expr(f.name), genSchema[b](quotes)(f.fieldRef.asInstanceOf[RTypeRef[b]], Some(f.asInstanceOf[ScalaFieldInfoRef]), overrides, fieldDefault))) + ) + ) + }.toMap, + $requiredFields, + ${ Expr(t.annotations.get("co.blocke.scalajack.schema.additionalProperties").flatMap(_.get("value")).map(_.toBoolean)) }, + ${ + if isInitialSchema then '{ Some(new URL("http://jsons-schema.org/draft-04/schema#")) } + else '{ None } + }, + ${ + if isInitialSchema then Expr(t.annotations.get("co.blocke.scalajack.schema.id").flatMap(_.get("value"))) + else '{ None } + }, + ${ + if isInitialSchema then Expr(t.annotations.get("co.blocke.scalajack.schema.title").flatMap(_.get("value"))) + else '{ None } + }, + ${ Expr(t.annotations.get("co.blocke.scalajack.schema.description").flatMap(_.get("value"))) }, + ${ + if defaultValue.isDefined then + val codec = JsonCodecMaker.generateCodecFor[u](t.asInstanceOf[RTypeRef[u]], json.JsonConfig.withSuppressTypeHints()) + '{ + val out = new writing.JsonOutput() + $codec.encodeValue(${ defaultValue.get.asExprOf[u] }, out) + Some(out.result.asInstanceOf[RawJson]) + } + else Expr(None) + } ) - } - case t: ScalaClassRef[_] => - t.refType match - case '[u] => - val requiredFields = Expr(t.fields.collect{ - case f: FieldInfoRef if !f.fieldRef.isInstanceOf[OptionRef[_]] => f.name - }) - '{ - ObjectSchema( - ${ - Expr.ofList(t.fields.map(f => - f.fieldRef.refType match - case '[b] => - // Get default value if any - val tpe = TypeRepr.of[u] - val classCompanion = tpe.typeSymbol.companionClass - val companionModule = tpe.typeSymbol.companionModule - val dvMembers = classCompanion.methodMember("$lessinit$greater$default$" + (f.index + 1)) - val fieldDefault = if dvMembers.isEmpty then - None - else - val methodSymbol = dvMembers.head - val dvSelectNoTArgs = Ref(companionModule).select(methodSymbol) - val dvSelect = methodSymbol.paramSymss match - case Nil => dvSelectNoTArgs - case List(params) if (params.exists(_.isTypeParam)) => - typeArgs(tpe) match - case Nil => ??? // throw JsonParseError("Expected an applied type", ???) - case typeArgs => TypeApply(dvSelectNoTArgs, typeArgs.map(Inferred(_))) - case _ => ??? // fail(s"Default method for ${symbol.name} of class ${tpe.show} have a complex " + - Some(dvSelect) - Expr.ofTuple((Expr(f.name), genSchema[b](quotes)(f.fieldRef.asInstanceOf[RTypeRef[b]], Some(f.asInstanceOf[ScalaFieldInfoRef]), fieldDefault))) - ) - ) - }.toMap, - $requiredFields, - ${ Expr(t.annotations.get("co.blocke.scalajack.schema.additionalProperties").flatMap(_.get("value")).map(_.toBoolean).getOrElse(false)) }, - new URL("http://jsons-schema.org/draft-04/schema#"), - ${ Expr(t.annotations.get("co.blocke.scalajack.schema.id").flatMap(_.get("value"))) }, - ${ Expr(t.annotations.get("co.blocke.scalajack.schema.title").flatMap(_.get("value"))) }, - ${ Expr(t.annotations.get("co.blocke.scalajack.schema.description").flatMap(_.get("value"))) }, - ${ - if defaultValue.isDefined then - val codec = JsonCodecMaker.generateCodecFor[u](t.asInstanceOf[RTypeRef[u]], json.JsonConfig.withSuppressTypeHints()) - '{ - val out = new writing.JsonOutput() - $codec.encodeValue(${defaultValue.get.asExprOf[u]}, out) - Some(out.result.asInstanceOf[RawJson]) - } - else - Expr(None) - } - ) - } - case _ => throw new Exception(s"Unsupported type ${rt.name} for JSON schema generation") + } + case t: TraitRef[?] => + t.refType match + case '[u] => + if t.childrenAreObject then '{ EnumSchema(${ Expr(t.sealedChildren.map(_.name.split("\\.").last)) }) } + else throw new Exception(s"Unsupported type ${rt.name} of type ${rt.getClass.getName} for JSON schema generation") + case x => throw new Exception(s"Unsupported type ${rt.name} of type ${rt.getClass.getName} for JSON schema generation") + }) + } diff --git a/src/main/scala/co.blocke.scalajack/json/schema/Schema.scala b/src/main/scala/co.blocke.scalajack/json/schema/Schema.scala index d6d62e58..0ae149cb 100644 --- a/src/main/scala/co.blocke.scalajack/json/schema/Schema.scala +++ b/src/main/scala/co.blocke.scalajack/json/schema/Schema.scala @@ -4,12 +4,11 @@ package schema import java.net.URL -/** - * A *very* sparse implementation of JSON Schema Draft 4 (model only). It is full of holes, but is just +/** A *very* sparse implementation of JSON Schema Draft 4 (model only). It is full of holes, but is just * enough for what I needed at the time--and had the advantage of leveraging scala-reflection * to generate the schema from a type. At the time of this writing, no other Scala 3-capable JSON * library generated a JSON Schema document. (ZIO-Json genrated their own Schema structure, however.) - * + * * If there is strong utility for a full-blown JSON Schema utility, that might be something I look * at later, unless someone wants to take it up. I would suggest development of "helper" objects * for the more advanced schema operaitons (allOf, anyOf, if/then/else, etc) vs trying to do all that @@ -19,19 +18,19 @@ import java.net.URL // Reference: https://json-schema.org/UnderstandingJSONSchema.pdf enum SchemaType: - case `null`, `boolean`, `object`, `array`, `string`, `number`, `integer` + case `null`, `boolean`, `object`, `array`, `string`, `number`, `integer` enum StringFormat: - case `date-time`, email, hostname, ipv4, ipv6, uuid, uri, url + case `date-time`, email, hostname, ipv4, ipv6, uuid, uri, url // opaque type JSON_LITERAL = String type Schema = StdSchema | EnumSchema sealed trait StdSchema: - val `type`: SchemaType - val description: Option[String] - val default: Option[RawJson] + val `type`: SchemaType + val description: Option[String] + val default: Option[RawJson] // Formats: Dates & Times, Email, Hostnames case class StringSchema( @@ -42,7 +41,7 @@ case class StringSchema( description: Option[String] = None, default: Option[RawJson] = None, `type`: SchemaType = SchemaType.`string` - ) extends StdSchema +) extends StdSchema case class IntegerSchema( minimum: Option[Long] = None, @@ -53,7 +52,7 @@ case class IntegerSchema( description: Option[String] = None, default: Option[RawJson] = None, `type`: SchemaType = SchemaType.`integer` - ) extends StdSchema +) extends StdSchema case class NumberSchema( minimum: Option[Double] = None, @@ -64,16 +63,16 @@ case class NumberSchema( description: Option[String] = None, default: Option[RawJson] = None, `type`: SchemaType = SchemaType.`number` - ) extends StdSchema +) extends StdSchema case class BooleanSchema( description: Option[String] = None, default: Option[RawJson] = None, `type`: SchemaType = SchemaType.`boolean` - ) extends StdSchema +) extends StdSchema case class NullSchema(description: Option[String] = None, `type`: SchemaType = SchemaType.`null`) extends StdSchema: - val default: Option[RawJson] = None // no default for null possible + val default: Option[RawJson] = None // no default for null possible case class ArraySchema( items: Schema, @@ -98,12 +97,12 @@ case class TupleSchema( // Note: patternProperties not implemented at this time (I didn't need them) case class ObjectSchema( - properties: Map[String,Schema], + properties: Map[String, Schema], required: List[String], - additionalProperties: Boolean = false, - `$schema`: URL = new URL("http://jsons-schema.org/draft-04/schema#"), + additionalProperties: Option[Boolean] = None, + `$schema`: Option[URL] = Some(new URL("http://jsons-schema.org/draft-04/schema#")), `$id`: Option[String] = None, - title: Option[String] = None, + title: Option[String] = None, description: Option[String] = None, default: Option[RawJson] = None, `type`: SchemaType = SchemaType.`object` diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index 48619bfe..7bae64b4 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -8,7 +8,7 @@ import scala.reflect.ClassTag import json.* import scala.collection.immutable.Queue import co.blocke.scalajack.json.reading.SafeNumbers.double -import co.blocke.scalajack.schema.* +import co.blocke.scalajack.json.schema.* class Shape[T](polygon: T) class Parallelogram() @@ -49,20 +49,29 @@ object RunMe extends App: // println(RType.of[Person].pretty) - implicit val blah: ScalaJack[Record] = sj[Record] // (JsonConfig.withSuppressedEscapedStrings()) - println(blah.toJson(record)) + // implicit val blah: ScalaJack[Record] = sj[Record] // (JsonConfig.withSuppressedEscapedStrings()) + // println(blah.toJson(record)) - // println(RType.of[Schema].pretty) +// val f = new FastStringBuilder() +// val s = """Gregory "William" +// Zoller""" + "\u20A0 wow" +// f.appendEscaped(s, 0, s.length) +// println(f.result) - val f = new FastStringBuilder() - val s = """Gregory "William" -Zoller""" + "\u20A0 wow" - f.appendEscaped(s, 0, s.length) - println(f.result) + // implicit val blah: ScalaJack[schema.Schema] = codecOf[schema.Schema](JsonConfig.withSuppressTypeHints()) + // val oride = Map("co.blocke.scalajack.json.run.Header$Who$$Type" -> schema.EnumSchema(List("staff", "customer", "program"))) + // println(ScalaJack[schema.Schema].toJson(schema.JsonSchema.of[DormancyEvent](oride))) - // implicit val blah: ScalaJack[schema.Schema] = sj[schema.Schema](JsonConfig.withSuppressedTypeHints()) - // println(sj.toJson(schema.JsonSchema.of[Person])) + // NOT YET! + // implicit val blah: ScalaJack[schema.Schema] = codecOf[schema.Schema](JsonConfig.withSuppressTypeHints()) + // val s = schema.JsonSchema.of[DormancyEvent] + // println(ScalaJack[schema.Schema].toJson(s)) - // println(RType.of[schema.Schema].pretty) + // val jssrc = json.reading.JsonSource(""""Red"""") + // println("E: " + jssrc.expectEnum()) + + // implicit val blah: ScalaJack[Pizza] = codecOf[Pizza] + // val c: Pizza = ScalaJack[Pizza].fromJson("\"READY\"") + // println("Pizza: " + c) println("done.") diff --git a/src/main/scala/co.blocke.scalajack/run/Record.scala b/src/main/scala/co.blocke.scalajack/run/Record.scala index 4f06d0d7..51393781 100644 --- a/src/main/scala/co.blocke.scalajack/run/Record.scala +++ b/src/main/scala/co.blocke.scalajack/run/Record.scala @@ -3,7 +3,7 @@ package json package run import neotype.* -import co.blocke.scalajack.schema.* +import co.blocke.scalajack.json.schema.* @additionalProperties(value = "true") @id(value = "abc123") @@ -157,6 +157,22 @@ val jsData = ] }""" +case class Yippy(a: (Int, List[String], Boolean) = (5, List("a", "b"), false), b: Boolean) +sealed trait Flavor +case object Vanilla extends Flavor +case object ChocolateX extends Flavor +case object Bourbon extends Flavor +case class FlavorHolder(f: Flavor) + +enum Colors: + case Red, Green, Blue + +object WeekDay extends Enumeration { + type WeekDay = Value + val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value +} +import WeekDay.* + val record = Record( Person("John Doe", 30, Address("123 Main Street", "Anytown", "CA", "12345"), Some("john.doe@example.com"), List("555-555-5555", "555-123-4567"), true), List("reading", "swimming", "traveling"), From b140b523aa8311ff63b2327aeb1a6572d419a198 Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Tue, 26 Mar 2024 00:53:26 -0500 Subject: [PATCH 50/65] Continue read implementations --- benchmark/build.sbt | 2 +- .../src/main/scala/co.blocke/ScalaJack.scala | 2 +- build.sbt | 2 +- .../scala/co.blocke.scalajack/ScalaJack.scala | 8 +- .../json/FastStringBuilder.scala | 12 +- .../json/JsonCodecMaker.scala | 554 +++++++++--------- .../co.blocke.scalajack/json/JsonConfig.scala | 52 +- .../co.blocke.scalajack/json/JsonError.scala | 2 +- .../co.blocke.scalajack/json/package.scala | 5 + .../json/reading/JsonParser.scalax | 163 ------ .../json/reading/JsonSource.scala | 147 ++++- .../json/reading/Numbers.scala | 34 +- .../json/schema/JsonSchema.scala | 5 +- .../json/writing/JsonOutput.scala | 15 +- .../scala/co.blocke.scalajack/run/Play.scala | 7 + .../co.blocke.scalajack/run/Record.scala | 2 + .../{ClassSpec.scala => ClassSpec.scalax} | 0 ...JavaCollSpec.scala => JavaCollSpec.scalax} | 0 .../{JavaMapSpec.scala => JavaMapSpec.scalax} | 0 .../{MapSpec.scala => MapSpec.scalax} | 0 ...ArraySpec.scala => SeqSetArraySpec.scalax} | 60 +- .../{TupleSpec.scala => TupleSpec.scalax} | 0 .../{AliasSpec.scala => AliasSpec.scalax} | 0 .../{MiscTests.scala => MiscTests.scalax} | 0 .../json/misc/OptionLRTrySpec.scala | 52 +- .../json/primitives/JavaPrimSpec.scala | 300 +++++----- .../json/primitives/ScalaPrimSpec.scala | 197 +++---- .../json/primitives/SimpleSpec.scala | 431 +++++++------- 28 files changed, 995 insertions(+), 1057 deletions(-) delete mode 100644 src/main/scala/co.blocke.scalajack/json/reading/JsonParser.scalax rename src/test/scala/co.blocke.scalajack/json/classes/{ClassSpec.scala => ClassSpec.scalax} (100%) rename src/test/scala/co.blocke.scalajack/json/collections/{JavaCollSpec.scala => JavaCollSpec.scalax} (100%) rename src/test/scala/co.blocke.scalajack/json/collections/{JavaMapSpec.scala => JavaMapSpec.scalax} (100%) rename src/test/scala/co.blocke.scalajack/json/collections/{MapSpec.scala => MapSpec.scalax} (100%) rename src/test/scala/co.blocke.scalajack/json/collections/{SeqSetArraySpec.scala => SeqSetArraySpec.scalax} (73%) rename src/test/scala/co.blocke.scalajack/json/collections/{TupleSpec.scala => TupleSpec.scalax} (100%) rename src/test/scala/co.blocke.scalajack/json/misc/{AliasSpec.scala => AliasSpec.scalax} (100%) rename src/test/scala/co.blocke.scalajack/json/misc/{MiscTests.scala => MiscTests.scalax} (100%) diff --git a/benchmark/build.sbt b/benchmark/build.sbt index 4d06fc5a..88646a67 100644 --- a/benchmark/build.sbt +++ b/benchmark/build.sbt @@ -37,7 +37,7 @@ lazy val benchmark = project libraryDependencies ++= Seq( "org.playframework" %% "play-json" % "3.0.1", "io.argonaut" %% "argonaut" % "6.3.9", - "co.blocke" %% "scalajack" % "5dbb0c_unknown", + "co.blocke" %% "scalajack" % "ce27f8_unknown", "dev.zio" %% "zio-json" % "0.6.1", "org.typelevel" %% "fabric-core" % "1.12.6", "org.typelevel" %% "fabric-io" % "1.12.6", diff --git a/benchmark/src/main/scala/co.blocke/ScalaJack.scala b/benchmark/src/main/scala/co.blocke/ScalaJack.scala index 733ad446..5dc3aa20 100644 --- a/benchmark/src/main/scala/co.blocke/ScalaJack.scala +++ b/benchmark/src/main/scala/co.blocke/ScalaJack.scala @@ -6,7 +6,7 @@ object ScalaJackZ: import co.blocke.scalajack.ScalaJack.* import co.blocke.scalajack.* - implicit val blah: ScalaJack[co.blocke.Record2] = codecOf[co.blocke.Record2] + implicit val blah: ScalaJack[co.blocke.Record2] = sjCodecOf[co.blocke.Record2] trait ScalaJackReadingBenchmark{ @Benchmark diff --git a/build.sbt b/build.sbt index 89d650ed..441eac88 100644 --- a/build.sbt +++ b/build.sbt @@ -35,7 +35,7 @@ lazy val root = project Test / parallelExecution := false, scalafmtOnCompile := !isCI, libraryDependencies ++= Seq( - "co.blocke" %% "scala-reflection" % "2.0.2", + "co.blocke" %% "scala-reflection" % "2.0.3", "org.apache.commons" % "commons-text" % "1.11.0", "io.github.kitlangton" %% "neotype" % "0.0.9", "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core" % "2.24.5-SNAPSHOT", diff --git a/src/main/scala/co.blocke.scalajack/ScalaJack.scala b/src/main/scala/co.blocke.scalajack/ScalaJack.scala index e2f2184d..5b070a6d 100644 --- a/src/main/scala/co.blocke.scalajack/ScalaJack.scala +++ b/src/main/scala/co.blocke.scalajack/ScalaJack.scala @@ -8,10 +8,10 @@ import quoted.Quotes import json.* case class ScalaJack[T](jsonCodec: JsonCodec[T]): // extends JsonCodec[T] //with YamlCodec with MsgPackCodec - def fromJson(js: String): T = // Either[JsonParseError, T] = + def fromJson(js: String): T = jsonCodec.decodeValue(reading.JsonSource(js)) - val out = writing.JsonOutput() // let's clear & re-use JsonOutput--avoid re-allocating all the internal buffer space + val out = writing.JsonOutput() def toJson(a: T): String = jsonCodec.encodeValue(a, out.clear()) out.result @@ -23,7 +23,7 @@ object ScalaJack: def apply[A](implicit a: ScalaJack[A]): ScalaJack[A] = a // ----- Use default JsonConfig - inline def codecOf[T]: ScalaJack[T] = ${ codecOfImpl[T] } + inline def sjCodecOf[T]: ScalaJack[T] = ${ codecOfImpl[T] } def codecOfImpl[T: Type](using Quotes): Expr[ScalaJack[T]] = import quotes.reflect.* val classRef = ReflectOnType[T](quotes)(TypeRepr.of[T], true)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) @@ -32,7 +32,7 @@ object ScalaJack: '{ ScalaJack($jsonCodec) } // ----- Use given JsonConfig - inline def codecOf[T](inline cfg: JsonConfig): ScalaJack[T] = ${ codecOfImplWithConfig[T]('cfg) } + inline def sjCodecOf[T](inline cfg: JsonConfig): ScalaJack[T] = ${ codecOfImplWithConfig[T]('cfg) } def codecOfImplWithConfig[T: Type](cfgE: Expr[JsonConfig])(using Quotes): Expr[ScalaJack[T]] = import quotes.reflect.* val cfg = summon[FromExpr[JsonConfig]].unapply(cfgE) diff --git a/src/main/scala/co.blocke.scalajack/json/FastStringBuilder.scala b/src/main/scala/co.blocke.scalajack/json/FastStringBuilder.scala index 4f8c81e3..adf3d895 100644 --- a/src/main/scala/co.blocke.scalajack/json/FastStringBuilder.scala +++ b/src/main/scala/co.blocke.scalajack/json/FastStringBuilder.scala @@ -41,20 +41,10 @@ final class FastStringBuilder(initial: Int = 16) { 25446, 25702, 25958, 26214 ) - // private[this] def appendEscapedUnicode(ch: Int, pos: Int, buf: Array[Byte]): Int = { - // val ds = lowerCaseHexDigits - // ByteArrayAccess.setShort(buf, pos, 0x755C) - // val d1 = ds(ch >> 8) - // val d2 = ds(ch & 0xFF) << 16 - // ByteArrayAccess.setInt(buf, pos + 2, d1 | d2) - // pos + 6 - // } - private[this] def appendEscapedUnicode(c: Char): Unit = { append('\\') append('u') append("%04x".format(c.toInt)) - i += 6 } @tailrec @@ -70,7 +60,7 @@ final class FastStringBuilder(initial: Int = 16) { i += 1 appendEscaped(s, from + 1, to) else if esc > 0 then - chars(i) = 0x5c + chars(i) = 0x5c // double quote chars(i + 1) = esc.toChar i += 2 appendEscaped(s, from + 1, to) diff --git a/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala b/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala index dc067189..d03d4b3c 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala @@ -12,6 +12,7 @@ import scala.quoted.* import scala.reflect.ClassTag import scala.annotation.switch import scala.collection.Factory +import scala.util.{Failure, Success, Try} import dotty.tools.dotc.ast.Trees.EmptyTree import org.apache.commons.text.StringEscapeUtils import org.apache.commons.lang3.text.translate.CharSequenceTranslator @@ -495,7 +496,7 @@ object JsonCodecMaker: case '[c] => val subtype = TypeIdent(TypeRepr.of[c].typeSymbol) val sym = Symbol.newBind(Symbol.spliceOwner, "t", Flags.EmptyFlags, subtype.tpe) - CaseDef(Bind(sym, Typed(Wildcard(), Inferred(subtype.tpe))), None, genEncFnBody[c](child, Ref(sym).asExprOf[c], out, cfg.suppressTypeHints).asTerm) + CaseDef(Bind(sym, Typed(Wildcard(), Inferred(subtype.tpe))), None, genEncFnBody[c](child, Ref(sym).asExprOf[c], out, cfg._suppressTypeHints).asTerm) } :+ CaseDef(Literal(NullConstant()), None, '{ $out.burpNull() }.asTerm) Match(in.asTerm, cases).asExprOf[Unit] } @@ -655,7 +656,6 @@ object JsonCodecMaker: aE: Expr[T], ref: RTypeRef[T], out: Expr[JsonOutput], - isStringified: Boolean = false, // e.g. Map key values. Doesn't apply to stringish values, which are always quotes-wrapped inTuple: Boolean = false )(using Quotes): Expr[Unit] = val methodKey = MethodKey(ref, false) @@ -667,70 +667,29 @@ object JsonCodecMaker: .getOrElse( ref match // First cover all primitive and simple types... - case t: BigDecimalRef => - if isStringified then '{ $out.valueStringified(${ aE.asExprOf[scala.math.BigDecimal] }) } - else '{ $out.value(${ aE.asExprOf[scala.math.BigDecimal] }) } - case t: BigIntRef => - if isStringified then '{ $out.valueStringified(${ aE.asExprOf[scala.math.BigInt] }) } - else '{ $out.value(${ aE.asExprOf[scala.math.BigInt] }) } - case t: BooleanRef => - if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Boolean] }) } - else '{ $out.value(${ aE.asExprOf[Boolean] }) } - case t: ByteRef => - if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Byte] }) } - else '{ $out.value(${ aE.asExprOf[Byte] }) } - case t: CharRef => '{ $out.value(${ aE.asExprOf[Char] }) } - case t: DoubleRef => - if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Double] }) } - else '{ $out.value(${ aE.asExprOf[Double] }) } - case t: FloatRef => - if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Float] }) } - else '{ $out.value(${ aE.asExprOf[Float] }) } - case t: IntRef => - if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Int] }) } - else '{ $out.value(${ aE.asExprOf[Int] }) } - case t: LongRef => - if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Long] }) } - else '{ $out.value(${ aE.asExprOf[Long] }) } - case t: ShortRef => - if isStringified then '{ $out.valueStringified(${ aE.asExprOf[Short] }) } - else '{ $out.value(${ aE.asExprOf[Short] }) } - case t: StringRef => - if cfg.suppressEscapedStrings then '{ $out.value(${ aE.asExprOf[String] }) } - else '{ $out.valueEscaped(${ aE.asExprOf[String] }) } - - case t: JBigDecimalRef => - if isStringified then '{ $out.valueStringified(${ aE.asExprOf[java.math.BigDecimal] }) } - else '{ $out.value(${ aE.asExprOf[java.math.BigDecimal] }) } - case t: JBigIntegerRef => - if isStringified then '{ $out.value(${ aE.asExprOf[java.math.BigInteger] }) } - else '{ $out.value(${ aE.asExprOf[java.math.BigInteger] }) } - case t: JBooleanRef => - if isStringified then '{ $out.value(${ aE.asExprOf[java.lang.Boolean] }) } - else '{ $out.value(${ aE.asExprOf[java.lang.Boolean] }) } - case t: JByteRef => - if isStringified then '{ $out.value(${ aE.asExprOf[java.lang.Byte] }) } - else '{ $out.value(${ aE.asExprOf[java.lang.Byte] }) } - case t: JCharacterRef => - '{ $out.value(${ aE.asExprOf[java.lang.Character] }) } - case t: JDoubleRef => - if isStringified then '{ $out.value(${ aE.asExprOf[java.lang.Double] }) } - else '{ $out.value(${ aE.asExprOf[java.lang.Double] }) } - case t: JFloatRef => - if isStringified then '{ $out.value(${ aE.asExprOf[java.lang.Float] }) } - else '{ $out.value(${ aE.asExprOf[java.lang.Float] }) } - case t: JIntegerRef => - if isStringified then '{ $out.value(${ aE.asExprOf[java.lang.Integer] }) } - else '{ $out.value(${ aE.asExprOf[java.lang.Integer] }) } - case t: JLongRef => - if isStringified then '{ $out.value(${ aE.asExprOf[java.lang.Long] }) } - else '{ $out.value(${ aE.asExprOf[java.lang.Long] }) } - case t: JShortRef => - if isStringified then '{ $out.value(${ aE.asExprOf[java.lang.Short] }) } - else '{ $out.value(${ aE.asExprOf[java.lang.Short] }) } - case t: JNumberRef => - if isStringified then '{ $out.value(${ aE.asExprOf[java.lang.Number] }) } - else '{ $out.value(${ aE.asExprOf[java.lang.Number] }) } + case t: BigDecimalRef => '{ $out.value(${ aE.asExprOf[scala.math.BigDecimal] }) } + case t: BigIntRef => '{ $out.value(${ aE.asExprOf[scala.math.BigInt] }) } + case t: BooleanRef => '{ $out.value(${ aE.asExprOf[Boolean] }) } + case t: ByteRef => '{ $out.value(${ aE.asExprOf[Byte] }) } + case t: CharRef => '{ $out.value(${ aE.asExprOf[Char] }) } + case t: DoubleRef => '{ $out.value(${ aE.asExprOf[Double] }) } + case t: FloatRef => '{ $out.value(${ aE.asExprOf[Float] }) } + case t: IntRef => '{ $out.value(${ aE.asExprOf[Int] }) } + case t: LongRef => '{ $out.value(${ aE.asExprOf[Long] }) } + case t: ShortRef => '{ $out.value(${ aE.asExprOf[Short] }) } + case t: StringRef => '{ $out.valueEscaped(${ aE.asExprOf[String] }) } + + case t: JBigDecimalRef => '{ $out.value(${ aE.asExprOf[java.math.BigDecimal] }) } + case t: JBigIntegerRef => '{ $out.value(${ aE.asExprOf[java.math.BigInteger] }) } + case t: JBooleanRef => '{ $out.value(${ aE.asExprOf[java.lang.Boolean] }) } + case t: JByteRef => '{ $out.value(${ aE.asExprOf[java.lang.Byte] }) } + case t: JCharacterRef => '{ $out.value(${ aE.asExprOf[java.lang.Character] }) } + case t: JDoubleRef => '{ $out.value(${ aE.asExprOf[java.lang.Double] }) } + case t: JFloatRef => '{ $out.value(${ aE.asExprOf[java.lang.Float] }) } + case t: JIntegerRef => '{ $out.value(${ aE.asExprOf[java.lang.Integer] }) } + case t: JLongRef => '{ $out.value(${ aE.asExprOf[java.lang.Long] }) } + case t: JShortRef => '{ $out.value(${ aE.asExprOf[java.lang.Short] }) } + case t: JNumberRef => '{ $out.value(${ aE.asExprOf[java.lang.Number] }) } case t: DurationRef => '{ $out.value(${ aE.asExprOf[java.time.Duration] }) } case t: InstantRef => '{ $out.value(${ aE.asExprOf[java.time.Instant] }) } @@ -769,9 +728,7 @@ object JsonCodecMaker: case Some(list) if list.contains(t.name) => true case _ => false val rtype = t.expr - if enumAsId then - if isStringified then '{ $out.value($rtype.asInstanceOf[EnumRType[_]].ordinal($aE.toString).get.toString) } - else '{ $out.value($rtype.asInstanceOf[EnumRType[_]].ordinal($aE.toString).get) } + if enumAsId then '{ $out.value($rtype.asInstanceOf[EnumRType[_]].ordinal($aE.toString).get) } else '{ $out.value($aE.toString) } // NeoType is a bit of a puzzle-box. To get the correct underlying base type, I had to dig into @@ -800,14 +757,11 @@ object JsonCodecMaker: } */ - case t: AnyRef => - '{ - AnyWriter.writeAny($aE, $out, ${ Expr(cfg) }) - } + case t: AnyRef => '{ AnyWriter.writeAny($aE, $out, ${ Expr(cfg) }) } // Everything else... - case _ if isStringified => throw new JsonIllegalKeyType("Non-primitive/non-simple types cannot be map keys") - case _ => genEncFnBody(ref, aE, out, inTuple = inTuple) + // case _ if isStringified => throw new JsonIllegalKeyType("Non-primitive/non-simple types cannot be map keys") + case _ => genEncFnBody(ref, aE, out, inTuple = inTuple) ) // --------------------------------------------------------------------------------------------- @@ -901,10 +855,8 @@ object JsonCodecMaker: maybeFieldNum = $in.expectObjectField(${ Ref(fieldMatrixSym).asExprOf[StringMatrix] }) if (${ Ref(reqSym).asExprOf[Int] } & ${ exprRequired }) == 0 then ${ instantiateClass.asExprOf[T] } else throw new JsonParseError("Missing required field(s) " + ${ allFieldNames }(Integer.numberOfTrailingZeros(${ Ref(reqSym).asExprOf[Int] } & ${ exprRequired })), $in) - // ${ instantiateClass.asExprOf[T] } }.asTerm - // Block(varDefs, parseLoop).asExprOf[T] Block(varDefs :+ reqVarDef, parseLoop).asExprOf[T] ) @@ -916,7 +868,6 @@ object JsonCodecMaker: // default: Expr[T], // needed? This should already be in ref... ref: RTypeRef[T], in: Expr[JsonSource], - isStringified: Boolean = false, // e.g. Map key values. Doesn't apply to stringish values, which are always quotes-wrapped inTuple: Boolean = false // not sure if needed... )(using Quotes): Expr[T] = val methodKey = MethodKey(ref, false) @@ -929,229 +880,143 @@ object JsonCodecMaker: ref match // First cover all primitive and simple types... case t: BigDecimalRef => - if isStringified then '{ scala.math.BigDecimal($in.expectString()) }.asExprOf[T] - else - '{ - $in.mark() - $in.skipNumber() - scala.math.BigDecimal($in.captureMark()) - }.asExprOf[T] + '{ + $in.expectNumberOrNull() match + case null => null + case s => scala.math.BigDecimal(s) + }.asExprOf[T] case t: BigIntRef => - if isStringified then '{ scala.math.BigInt($in.expectString()) }.asExprOf[T] - else - '{ - $in.mark() - $in.skipNumber() - scala.math.BigInt($in.captureMark()) - }.asExprOf[T] + '{ + $in.expectNumberOrNull() match + case null => null + case s => scala.math.BigInt(s) + }.asExprOf[T] case t: BooleanRef => - if isStringified then - '{ - if $in.readChar() != '"' then throw ParseError("Quoted boolean value expected") - val v = $in.expectBoolean() - if $in.readChar() != '"' then throw ParseError("Close quotes expected") - v - }.asExprOf[T] - else '{ $in.expectBoolean() }.asExprOf[T] + '{ $in.expectBoolean() }.asExprOf[T] case t: ByteRef => - if isStringified then - '{ - if $in.readChar() != '"' then throw ParseError("Quoted byte value expected") - val v = $in.expectInt().toByte - if $in.readChar() != '"' then throw ParseError("Close quotes expected") - v - }.asExprOf[T] - else '{ $in.expectInt().toByte }.asExprOf[T] + '{ $in.expectInt().toByte }.asExprOf[T] case t: CharRef => '{ - val c = $in.expectString() - if c.length == 0 then throw ParseError("Char value expected but empty string found in json") - else c.charAt(0) + $in.expectString() match + case null => + $in.retract() + $in.retract() + $in.retract() + $in.retract() + throw JsonParseError("Char value cannot be null", $in) + case "" => + $in.retract() + $in.retract() + throw JsonParseError("Char value expected but empty string found in json", $in) + case c => c.charAt(0) }.asExprOf[T] case t: DoubleRef => - if isStringified then - '{ - if $in.readChar() != '"' then throw ParseError("Quoted double value expected") - val v = $in.expectDouble() - if $in.readChar() != '"' then throw ParseError("Close quotes expected") - v - }.asExprOf[T] - else '{ $in.expectDouble() }.asExprOf[T] + '{ $in.expectDouble() }.asExprOf[T] case t: FloatRef => - if isStringified then - '{ - if $in.readChar() != '"' then throw ParseError("Quoted float value expected") - val v = $in.expectFloat() - if $in.readChar() != '"' then throw ParseError("Close quotes expected") - v - }.asExprOf[T] - else '{ $in.expectFloat() }.asExprOf[T] + '{ $in.expectFloat() }.asExprOf[T] case t: IntRef => - if isStringified then - '{ - if $in.readChar() != '"' then throw ParseError("Quoted integer value expected") - val v = $in.expectInt() - if $in.readChar() != '"' then throw ParseError("Close quotes expected") - v - }.asExprOf[T] - else '{ $in.expectInt() }.asExprOf[T] + '{ $in.expectInt() }.asExprOf[T] case t: LongRef => - if isStringified then - '{ - if $in.readChar() != '"' then throw ParseError("Quoted long value expected") - val v = $in.expectLong() - if $in.readChar() != '"' then throw ParseError("Close quotes expected") - v - }.asExprOf[T] - else '{ $in.expectLong() }.asExprOf[T] + '{ $in.expectLong() }.asExprOf[T] case t: ShortRef => - if isStringified then - '{ - if $in.readChar() != '"' then throw ParseError("Quoted integer value expected") - val v = $in.expectInt().toShort - if $in.readChar() != '"' then throw ParseError("Close quotes expected") - v - }.asExprOf[T] - else '{ $in.expectInt().toShort }.asExprOf[T] + '{ $in.expectInt().toShort }.asExprOf[T] case t: StringRef => '{ $in.expectString() }.asExprOf[T] case t: JBigDecimalRef => - if isStringified then '{ new java.math.BigDecimal($in.expectString()) }.asExprOf[T] - else - '{ - $in.mark() - $in.skipNumber() - new java.math.BigDecimal($in.captureMark()) - }.asExprOf[T] + '{ + $in.expectNumberOrNull() match + case null => null + case n => new java.math.BigDecimal(n) + }.asExprOf[T] case t: JBigIntegerRef => - if isStringified then '{ new java.math.BigInteger($in.expectString()) }.asExprOf[T] - else - '{ - $in.mark() - $in.skipNumber() - new java.math.BigInteger($in.captureMark()) - }.asExprOf[T] + '{ + $in.expectNumberOrNull() match + case null => null + case n => new java.math.BigInteger(n) + }.asExprOf[T] case t: JBooleanRef => - if isStringified then - '{ - if $in.readChar() != '"' then throw ParseError("Quoted boolean value expected") - val v = java.lang.Boolean.valueOf($in.expectBoolean()) - if $in.readChar() != '"' then throw ParseError("Close quotes expected") - v - }.asExprOf[T] - else '{ java.lang.Boolean.valueOf($in.expectBoolean()) }.asExprOf[T] + '{ $in.expectJavaBoolean() }.asExprOf[T] case t: JByteRef => - if isStringified then - '{ - if $in.readChar() != '"' then throw ParseError("Quoted integer value expected") - val v = java.lang.Byte.valueOf($in.expectInt().toByte) - if $in.readChar() != '"' then throw ParseError("Close quotes expected") - v - }.asExprOf[T] - else '{ java.lang.Byte.valueOf($in.expectInt().toByte) }.asExprOf[T] + '{ + $in.expectNumberOrNull() match + case null => null + case n => java.lang.Byte.valueOf(n) + }.asExprOf[T] case t: JCharacterRef => '{ val c = $in.expectString() - if c.length == 0 then throw ParseError("Character value expected but empty string found in json") + if c == null then null + else if c.length == 0 then + $in.retract() + $in.retract() + throw JsonParseError("Character value expected but empty string found in json", $in) else java.lang.Character.valueOf(c.charAt(0)) }.asExprOf[T] case t: JDoubleRef => - if isStringified then - '{ - if $in.readChar() != '"' then throw ParseError("Quoted double value expected") - val v = java.lang.Double.valueOf($in.expectDouble()) - if $in.readChar() != '"' then throw ParseError("Close quotes expected") - v - }.asExprOf[T] - else '{ java.lang.Double.valueOf($in.expectDouble()) }.asExprOf[T] + '{ + $in.expectNumberOrNull() match + case null => null + case n => java.lang.Double.valueOf(n) + }.asExprOf[T] case t: JFloatRef => - if isStringified then - '{ - if $in.readChar() != '"' then throw ParseError("Quoted float value expected") - val v = java.lang.Float.valueOf($in.expectFloat()) - if $in.readChar() != '"' then throw ParseError("Close quotes expected") - v - }.asExprOf[T] - else '{ java.lang.Float.valueOf($in.expectFloat()) }.asExprOf[T] + '{ + $in.expectNumberOrNull() match + case null => null + case n => java.lang.Float.valueOf(n) + }.asExprOf[T] case t: JIntegerRef => - if isStringified then - '{ - if $in.readChar() != '"' then throw ParseError("Quoted ineger value expected") - val v = java.lang.Integer.valueOf($in.expectInt()) - if $in.readChar() != '"' then throw ParseError("Close quotes expected") - v - }.asExprOf[T] - else '{ java.lang.Integer.valueOf($in.expectInt()) }.asExprOf[T] + '{ + $in.expectNumberOrNull() match + case null => null + case n => java.lang.Integer.valueOf(n) + }.asExprOf[T] case t: JLongRef => - if isStringified then - '{ - if $in.readChar() != '"' then throw ParseError("Quoted long value expected") - val v = java.lang.Long.valueOf($in.expectLong()) - if $in.readChar() != '"' then throw ParseError("Close quotes expected") - v - }.asExprOf[T] - else '{ java.lang.Long.valueOf($in.expectLong()) }.asExprOf[T] + '{ + $in.expectNumberOrNull() match + case null => null + case n => java.lang.Long.valueOf(n) + }.asExprOf[T] case t: JShortRef => - if isStringified then - '{ - if $in.readChar() != '"' then throw ParseError("Quoted integer value expected") - val v = java.lang.Short.valueOf($in.expectInt().toShort) - if $in.readChar() != '"' then throw ParseError("Close quotes expected") - v - }.asExprOf[T] - else '{ java.lang.Short.valueOf($in.expectInt().toShort) }.asExprOf[T] + '{ + $in.expectNumberOrNull() match + case null => null + case n => java.lang.Short.valueOf(n) + }.asExprOf[T] case t: JNumberRef => - if isStringified then - '{ - if $in.readChar() != '"' then throw ParseError("Quoted integer value expected") - $in.mark() - $in.skipNumber() - val v = scala.math.BigDecimal($in.captureMark()) match { - case d if d.isValidByte => java.lang.Byte.valueOf(d.toByteExact) - case d if d.isValidShort => java.lang.Short.valueOf(d.toShortExact) - case d if d.isValidInt => java.lang.Integer.valueOf(d.toIntExact) - case d if d.isValidLong => java.lang.Long.valueOf(d.toLongExact) - case d if d.isDecimalFloat => java.lang.Float.valueOf(d.toFloat) - case d if d.isDecimalDouble => java.lang.Double.valueOf(d.toDouble) - case d => d - } - if $in.readChar() != '"' then throw ParseError("Close quotes expected") - v - }.asExprOf[T] - else - '{ - $in.mark() - $in.skipNumber() - scala.math.BigDecimal($in.captureMark()) match { - case d if d.isValidByte => java.lang.Byte.valueOf(d.toByteExact) - case d if d.isValidShort => java.lang.Short.valueOf(d.toShortExact) - case d if d.isValidInt => java.lang.Integer.valueOf(d.toIntExact) - case d if d.isValidLong => java.lang.Long.valueOf(d.toLongExact) - case d if d.isDecimalFloat => java.lang.Float.valueOf(d.toFloat) - case d if d.isDecimalDouble => java.lang.Double.valueOf(d.toDouble) - case d => d - } - }.asExprOf[T] + '{ + $in.expectNumberOrNull() match + case null => null + case n => + scala.math.BigDecimal(n) match { + case d if d.isValidByte => java.lang.Byte.valueOf(d.toByteExact) + case d if d.isValidShort => java.lang.Short.valueOf(d.toShortExact) + case d if d.isValidInt => java.lang.Integer.valueOf(d.toIntExact) + case d if d.isValidLong => java.lang.Long.valueOf(d.toLongExact) + case d if d.isDecimalFloat => java.lang.Float.valueOf(d.toFloat) + case d if d.isDecimalDouble => java.lang.Double.valueOf(d.toDouble) + case d => d + } + }.asExprOf[T] - case t: DurationRef => '{ java.time.Duration.parse($in.expectString()) }.asExprOf[T] - case t: InstantRef => '{ java.time.Instant.parse($in.expectString()) }.asExprOf[T] - case t: LocalDateRef => '{ java.time.LocalDate.parse($in.expectString()) }.asExprOf[T] - case t: LocalDateTimeRef => '{ java.time.LocalDateTime.parse($in.expectString()) }.asExprOf[T] - case t: LocalTimeRef => '{ java.time.LocalTime.parse($in.expectString()) }.asExprOf[T] - case t: MonthDayRef => '{ java.time.MonthDay.parse($in.expectString()) }.asExprOf[T] - case t: OffsetDateTimeRef => '{ java.time.OffsetDateTime.parse($in.expectString()) }.asExprOf[T] - case t: OffsetTimeRef => '{ java.time.OffsetTime.parse($in.expectString()) }.asExprOf[T] - case t: PeriodRef => '{ java.time.Period.parse($in.expectString()) }.asExprOf[T] - case t: YearRef => '{ java.time.Year.parse($in.expectString()) }.asExprOf[T] - case t: YearMonthRef => '{ java.time.YearMonth.parse($in.expectString()) }.asExprOf[T] - case t: ZonedDateTimeRef => '{ java.time.ZonedDateTime.parse($in.expectString()) }.asExprOf[T] - case t: ZoneIdRef => '{ java.time.ZoneId.of($in.expectString()) }.asExprOf[T] - case t: ZoneOffsetRef => '{ java.time.ZoneOffset.of($in.expectString()) }.asExprOf[T] - - case t: URLRef => '{ new java.net.URL($in.expectString()) }.asExprOf[T] - case t: URIRef => '{ new java.net.URI($in.expectString()) }.asExprOf[T] - case t: UUIDRef => '{ java.util.UUID.fromString($in.expectString()) }.asExprOf[T] + case t: DurationRef => '{ $in.expectString(java.time.Duration.parse) }.asExprOf[T] + case t: InstantRef => '{ $in.expectString(java.time.Instant.parse) }.asExprOf[T] + case t: LocalDateRef => '{ $in.expectString(java.time.LocalDate.parse) }.asExprOf[T] + case t: LocalDateTimeRef => '{ $in.expectString(java.time.LocalDateTime.parse) }.asExprOf[T] + case t: LocalTimeRef => '{ $in.expectString(java.time.LocalTime.parse) }.asExprOf[T] + case t: MonthDayRef => '{ $in.expectString(java.time.MonthDay.parse) }.asExprOf[T] + case t: OffsetDateTimeRef => '{ $in.expectString(java.time.OffsetDateTime.parse) }.asExprOf[T] + case t: OffsetTimeRef => '{ $in.expectString(java.time.OffsetTime.parse) }.asExprOf[T] + case t: PeriodRef => '{ $in.expectString(java.time.Period.parse) }.asExprOf[T] + case t: YearRef => '{ $in.expectString(java.time.Year.parse) }.asExprOf[T] + case t: YearMonthRef => '{ $in.expectString(java.time.YearMonth.parse) }.asExprOf[T] + case t: ZonedDateTimeRef => '{ $in.expectString(java.time.ZonedDateTime.parse) }.asExprOf[T] + case t: ZoneIdRef => '{ $in.expectString(java.time.ZoneId.of) }.asExprOf[T] + case t: ZoneOffsetRef => '{ $in.expectString(java.time.ZoneOffset.of) }.asExprOf[T] + + case t: URLRef => '{ $in.expectString((s: String) => new java.net.URL(s)) }.asExprOf[T] + case t: URIRef => '{ $in.expectString((s: String) => new java.net.URI(s)) }.asExprOf[T] + case t: UUIDRef => '{ $in.expectString(java.util.UUID.fromString) }.asExprOf[T] case t: AliasRef[?] => // Special check for RawJson pseudo-type @@ -1167,10 +1032,88 @@ object JsonCodecMaker: genReadVal[e]( t.unwrappedType.asInstanceOf[RTypeRef[e]], in, - isStringified, inTuple ).asExprOf[T] + // -------------------- + // Options... + // -------------------- + case t: OptionRef[?] => + import quotes.reflect.* + t.optionParamType.refType match + case '[e] => + if cfg.noneAsNull || inTuple then + '{ + if $in.expectNull() then None + else Some(${ genReadVal[e](t.optionParamType.asInstanceOf[RTypeRef[e]], in) }) + }.asExprOf[T] + else ofOption[e](Some(genReadVal[e](t.optionParamType.asInstanceOf[RTypeRef[e]], in))).asExprOf[T] + + case t: LeftRightRef[?] if t.lrkind == LRKind.EITHER => + import quotes.reflect.* + t.leftRef.refType match + case '[l] => + t.rightRef.refType match + case '[r] => + '{ + $in.mark() + if $in.expectNull() then null + else + scala.util.Try(${ genReadVal[r](t.rightRef.asInstanceOf[RTypeRef[r]], in, inTuple) }) match + case Success(rval) => + Right(rval) + case Failure(f) => + scala.util.Try(${ genReadVal[l](t.leftRef.asInstanceOf[RTypeRef[l]], in, inTuple) }) match + case Success(lval) => Left(lval) + case Failure(_) => + $in.retract() + throw JsonParseError("Failed to read either side of Either type", $in) + }.asExprOf[T] + /* + Either: + def read(parser: Parser): Either[L, R] = { + val savedReader = parser.mark() + if (parser.peekForNull) + null + else + Try(rightTypeAdapter.read(parser)) match { + case Success(rightValue) => + Right(rightValue.asInstanceOf[R]) + case Failure(_) => // Right parse failed... try left + parser.revertToMark(savedReader) + Try(leftTypeAdapter.read(parser)) match { + case Success(leftValue) => + Left(leftValue.asInstanceOf[L]) + case Failure(x) => + parser.backspace() + throw new ScalaJackError( + parser.showError(s"Failed to read either side of Either") + ) + } + } + } + + Intersection: +val syntheticTA = taCache.typeAdapterOf[L] +syntheticTA.write(t.asInstanceOf[L], writer, out) + + Union: + def read(parser: Parser): L | R = { + val savedReader = parser.mark() + Try(leftTypeAdapter.read(parser)) match { + case Success(leftValue) => leftValue.asInstanceOf[L] + case Failure(_) => // Left parse failed... try Right + parser.revertToMark(savedReader) + Try(rightTypeAdapter.read(parser)) match { + case Success(rightValue) => rightValue.asInstanceOf[R] + case Failure(x) => + parser.backspace() + throw new ScalaJackError( parser.showError(s"Failed to read any values for union type") ) + } + } + } + */ + // -------------------- // Enumerations... // -------------------- @@ -1228,7 +1171,7 @@ object JsonCodecMaker: Apply(Ref(m(0)), List('{ s }.asTerm)).asExpr } case v: Int => - throw ParseError("Ordinal value initiation not valid for Java Enums") + throw JsonParseError("Ordinal value initiation not valid for Java Enums", $in) }.asExprOf[T] // -------------------- @@ -1241,7 +1184,7 @@ object JsonCodecMaker: case '[e] => val rtypeRef = t.elementRef.asInstanceOf[RTypeRef[e]] '{ - val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in) }) + val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in, inTuple) }) if parsedArray != null then parsedArray.toList else null }.asExprOf[T] @@ -1250,7 +1193,7 @@ object JsonCodecMaker: case '[e] => val rtypeRef = t.elementRef.asInstanceOf[RTypeRef[e]] '{ - val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in) }) + val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in, inTuple) }) if parsedArray != null then parsedArray.toVector else null }.asExprOf[T] @@ -1259,7 +1202,7 @@ object JsonCodecMaker: case '[e] => val rtypeRef = t.elementRef.asInstanceOf[RTypeRef[e]] '{ - val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in) }) + val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in, inTuple) }) if parsedArray != null then parsedArray.toSeq else null }.asExprOf[T] @@ -1268,7 +1211,7 @@ object JsonCodecMaker: case '[e] => val rtypeRef = t.elementRef.asInstanceOf[RTypeRef[e]] '{ - val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in) }) + val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in, inTuple) }) if parsedArray != null then parsedArray.toIndexedSeq else null }.asExprOf[T] @@ -1277,7 +1220,7 @@ object JsonCodecMaker: case '[e] => val rtypeRef = t.elementRef.asInstanceOf[RTypeRef[e]] '{ - val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in) }) + val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in, inTuple) }) if parsedArray != null then parsedArray.toIterable else null }.asExprOf[T] @@ -1287,7 +1230,7 @@ object JsonCodecMaker: case '[e] => val rtypeRef = t.elementRef.asInstanceOf[RTypeRef[e]] '{ - val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in) }) + val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in, inTuple) }) if parsedArray != null then parsedArray.to(${ Expr.summon[Factory[e, T]].get }) // create appropriate flavor of Seq[T] here else null }.asExprOf[T] @@ -1298,17 +1241,64 @@ object JsonCodecMaker: val rtypeRef = t.elementRef.asInstanceOf[RTypeRef[e]] val ct = Expr.summon[ClassTag[e]].get '{ - val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in) }) + val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in, inTuple) }) if parsedArray != null then implicit val ctt = $ct parsedArray.toArray[e] else null }.asExprOf[T] + // -------------------- + // Tuples... + // -------------------- + case t: TupleRef[?] => + import quotes.reflect.* + t.refType match + case '[tt] => + val tpe = TypeRepr.of[tt] + val maxI = Expr(t.tupleRefs.length - 1) + val indexedTypes = tpe match + case AppliedType(_, typeArgs) => typeArgs.map(_.dealias) + case _ => Nil + + // make all the tuple terms, accounting for , and ] detection + val tupleTerms = + if t.tupleRefs.length == 1 then + t.tupleRefs(0).refType match + case '[e] => + List('{ + ${ genReadVal[e](t.tupleRefs(0).asInstanceOf[RTypeRef[e]], in, true) } + $in.expectToken(']') + }.asTerm) + else + t.tupleRefs.zipWithIndex.map { case (tpart, i) => + tpart.refType match + case '[e] => + if i == 0 then genReadVal[e](tpart.asInstanceOf[RTypeRef[e]], in, true).asTerm + else if i < t.tupleRefs.length - 1 then + '{ + $in.expectToken(',') + ${ genReadVal[e](tpart.asInstanceOf[RTypeRef[e]], in, true) } + }.asTerm + else + '{ + $in.expectToken(',') + val res = ${ genReadVal[e](tpart.asInstanceOf[RTypeRef[e]], in, true) } + $in.expectToken(']') + res + }.asTerm + } + '{ + $in.expectToken('[') + ${ + Apply(TypeApply(Select.unique(New(Inferred(tpe)), ""), indexedTypes.map(x => Inferred(x))), tupleTerms).asExpr + } + }.asExprOf[T] + case _ => // Classes, traits, etc. genDecFnBody[T](ref, in) // Create a new decoder function (presumably for class, trait, etc) - genReadVal(ref, in) + genReadVal(ref, in, inTuple) // Just-created function is present now and will be called ) @@ -1338,5 +1328,5 @@ object JsonCodecMaker: // others here??? Refer to Jsoniter file JsonCodecMaker.scala classFieldMatrixValDefs ++ writeMethodDefs ++ readMethodDefs val codec = Block(neededDefs.toList, codecDef).asExprOf[JsonCodec[T]] - // println(s"Codec: ${codec.show}") + println(s"Codec: ${codec.show}") codec diff --git a/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala b/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala index 2d44c69e..24845fb2 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala @@ -12,15 +12,15 @@ class JsonConfig private[scalajack] ( val tryFailureHandling: TryPolicy, val eitherLeftHandling: EitherLeftPolicy, // val undefinedFieldHandling: UndefinedValueOption = UndefinedValueOption.THROW_EXCEPTION, - // val permissivePrimitives: Boolean = false, + // val _allowQuotedPrimitives: Boolean, val writeNonConstructorFields: Boolean, // -------------------------- val typeHintLabel: String, val typeHintPolicy: TypeHintPolicy, // -------------------------- val enumsAsIds: Option[List[String]], // None=no enums as ids, Some(Nil)=all enums as ids, Some(List(...))=specified classes enums as ids - val suppressEscapedStrings: Boolean, - val suppressTypeHints: Boolean + val _suppressEscapedStrings: Boolean, + val _suppressTypeHints: Boolean ): def withNoneAsNull(): JsonConfig = copy(noneAsNull = true) def withTryFailureHandling(tryPolicy: TryPolicy): JsonConfig = copy(tryFailureHandling = tryPolicy) @@ -29,8 +29,9 @@ class JsonConfig private[scalajack] ( def withTypeHintLabel(label: String): JsonConfig = copy(typeHintLabel = label) def withTypeHintPolicy(hintPolicy: TypeHintPolicy): JsonConfig = copy(typeHintPolicy = hintPolicy) def withEnumsAsIds(asIds: Option[List[String]]): JsonConfig = copy(enumsAsIds = asIds) - def withSuppressEscapedStrings(): JsonConfig = copy(suppressEscapedStrings = true) - def withSuppressTypeHints(): JsonConfig = copy(suppressTypeHints = true) + def suppressEscapedStrings(): JsonConfig = copy(_suppressEscapedStrings = true) + def suppressTypeHints(): JsonConfig = copy(_suppressTypeHints = true) + // def allowQuotedPrimitives(): JsonConfig = copy(_allowQuotedPrimitives = true) private[this] def copy( noneAsNull: Boolean = noneAsNull, @@ -40,18 +41,20 @@ class JsonConfig private[scalajack] ( typeHintLabel: String = typeHintLabel, typeHintPolicy: TypeHintPolicy = typeHintPolicy, enumsAsIds: Option[List[String]] = enumsAsIds, - suppressEscapedStrings: Boolean = suppressEscapedStrings, - suppressTypeHints: Boolean = suppressTypeHints + _suppressEscapedStrings: Boolean = _suppressEscapedStrings, + _suppressTypeHints: Boolean = _suppressTypeHints + // _allowQuotedPrimitives: Boolean = _allowQuotedPrimitives ): JsonConfig = new JsonConfig( noneAsNull, tryFailureHandling, eitherLeftHandling, + // _allowQuotedPrimitives, writeNonConstructorFields, typeHintLabel, typeHintPolicy, enumsAsIds, - suppressEscapedStrings, - suppressTypeHints + _suppressEscapedStrings, + _suppressTypeHints ) enum TryPolicy: @@ -71,12 +74,13 @@ object JsonConfig noneAsNull = false, tryFailureHandling = TryPolicy.NO_WRITE, eitherLeftHandling = EitherLeftPolicy.NO_WRITE, - writeNonConstructorFields = true, + // _allowQuotedPrimitives = false writeNonConstructorFields = true, + writeNonConstructorFields = false, typeHintLabel = "_hint", typeHintPolicy = TypeHintPolicy.SIMPLE_CLASSNAME, enumsAsIds = None, - suppressEscapedStrings = false, - suppressTypeHints = false + _suppressEscapedStrings = false, + _suppressTypeHints = false ): import scala.quoted.FromExpr.* @@ -90,16 +94,17 @@ object JsonConfig .withTypeHintLabel(${ Expr(x.typeHintLabel) }) .withTypeHintPolicy(${ Expr(x.typeHintPolicy) }) .withEnumsAsIds(${ Expr(x.enumsAsIds) }) + // .allowQuotedPrimitives(${ Expr(x._allowQuotedPrimitives) }) val jc2 = ${ if x.noneAsNull then '{ jc.withNoneAsNull() } else '{ jc } } val jc3 = ${ - if !x.suppressEscapedStrings then '{ jc2.withSuppressEscapedStrings() } + if !x._suppressEscapedStrings then '{ jc2.suppressEscapedStrings() } else '{ jc2 } } val jc4 = ${ - if !x.suppressTypeHints then '{ jc2.withSuppressTypeHints() } + if !x._suppressTypeHints then '{ jc2.suppressTypeHints() } else '{ jc3 } } jc4 @@ -122,6 +127,7 @@ object JsonConfig // $forbitNullsInInputE, $tryFailureHandlerE, $eitherLeftHandlerE, + // $allowQuotedPrimitivesE, // $undefinedFieldHandlingE, // $permissivePrimitivesE, $writeNonConstructorFieldsE, @@ -139,6 +145,7 @@ object JsonConfig // extract("forbitNullsInInput", forbitNullsInInputE), extract("tryFailureHandler", tryFailureHandlerE), extract("eitherLeftHandler", eitherLeftHandlerE), + // extract("_allowQuotedPrimitives", allowQuotedPrimtiviesE), // extract("undefinedFieldHandling", undefinedFieldHandlingE), // extract("permissivePrimitives", permissivePrimitivesE), extract("writeNonConstructorFields", writeNonConstructorFieldsE), @@ -146,8 +153,8 @@ object JsonConfig extract("typeHintLabel", typeHintLabelE), extract("typeHintPolicy", typeHintPolicyE), extract("enumsAsIds", enumsAsIdsE), - extract("suppressEscapedStrings", suppressEscapedStringsE), - extract("suppressTypeHints", suppressTypeHintsE) + extract("_suppressEscapedStrings", suppressEscapedStringsE), + extract("_suppressTypeHints", suppressTypeHintsE) ) ) catch { @@ -155,16 +162,17 @@ object JsonConfig println("ERROR: " + x.getMessage) None } - case '{ JsonConfig } => Some(JsonConfig) - case '{ ($x: JsonConfig).withNoneAsNull() } => Some(x.valueOrAbort.withNoneAsNull()) - case '{ ($x: JsonConfig).withTryFailureHandling($v) } => Some(x.valueOrAbort.withTryFailureHandling(v.valueOrAbort)) - case '{ ($x: JsonConfig).withEitherLeftHandling($v) } => Some(x.valueOrAbort.withEitherLeftHandling(v.valueOrAbort)) + case '{ JsonConfig } => Some(JsonConfig) + case '{ ($x: JsonConfig).withNoneAsNull() } => Some(x.valueOrAbort.withNoneAsNull()) + case '{ ($x: JsonConfig).withTryFailureHandling($v) } => Some(x.valueOrAbort.withTryFailureHandling(v.valueOrAbort)) + case '{ ($x: JsonConfig).withEitherLeftHandling($v) } => Some(x.valueOrAbort.withEitherLeftHandling(v.valueOrAbort)) + // case '{ ($x: JsonConfig).allowQuotedPrimitives() } => Some(x.valueOrAbort.allowQuotedPrimities()) case '{ ($x: JsonConfig).withWriteNonConstructorFields($v) } => Some(x.valueOrAbort.withWriteNonConstructorFields(v.valueOrAbort)) case '{ ($x: JsonConfig).withTypeHintLabel($v) } => Some(x.valueOrAbort.withTypeHintLabel(v.valueOrAbort)) case '{ ($x: JsonConfig).withTypeHintPolicy($v) } => Some(x.valueOrAbort.withTypeHintPolicy(v.valueOrAbort)) case '{ ($x: JsonConfig).withEnumsAsIds($v) } => Some(x.valueOrAbort.withEnumsAsIds(v.valueOrAbort)) - case '{ ($x: JsonConfig).withSuppressEscapedStrings() } => Some(x.valueOrAbort.withSuppressEscapedStrings()) - case '{ ($x: JsonConfig).withSuppressTypeHints() } => Some(x.valueOrAbort.withSuppressTypeHints()) + case '{ ($x: JsonConfig).suppressEscapedStrings() } => Some(x.valueOrAbort.suppressEscapedStrings()) + case '{ ($x: JsonConfig).suppressTypeHints() } => Some(x.valueOrAbort.suppressTypeHints()) } private[scalajack] given ToExpr[TryPolicy] with { diff --git a/src/main/scala/co.blocke.scalajack/json/JsonError.scala b/src/main/scala/co.blocke.scalajack/json/JsonError.scala index c071996c..5875e93f 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonError.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonError.scala @@ -28,4 +28,4 @@ case class JsonParseError(override val msg: String, context: reading.JsonSource) ("..." + js.substring(context.pos - 49), 52) case ep => ("..." + js.substring(ep - 49, ep + 27) + "...", 52) } - msg + s" at positio [${context.pos}]" + "\n" + clip.replaceAll("[\n\t]", "~") + "\n" + ("-" * dashes) + "^" + msg + s" at position [${context.pos}]" + "\n" + clip.replaceAll("[\n\t]", "~") + "\n" + ("-" * dashes) + "^" diff --git a/src/main/scala/co.blocke.scalajack/json/package.scala b/src/main/scala/co.blocke.scalajack/json/package.scala index 6f3b0e23..10143aba 100644 --- a/src/main/scala/co.blocke.scalajack/json/package.scala +++ b/src/main/scala/co.blocke.scalajack/json/package.scala @@ -2,6 +2,7 @@ package co.blocke.scalajack package json import scala.util.Failure +import scala.quoted.{Expr, Quotes, Type} opaque type RawJson = String @@ -23,3 +24,7 @@ def descrambleTest(in: String, hash: Int): Boolean = case 'A' if in.length == 13 => "" + in(0) + in(2) + in(4) + in(7) + in(10) == last5 case 'B' if in.length == 13 => "" + in(1) + in(3) + in(6) + in(8) + in(11) == last5 case _ => false + +def ofOption[T](xs: Option[Expr[T]])(using Type[T])(using q: Quotes): Expr[Option[T]] = + import q.reflect.* + if xs.isEmpty then Expr(None) else '{ Some(${ xs.get }) } diff --git a/src/main/scala/co.blocke.scalajack/json/reading/JsonParser.scalax b/src/main/scala/co.blocke.scalajack/json/reading/JsonParser.scalax deleted file mode 100644 index 9768e74b..00000000 --- a/src/main/scala/co.blocke.scalajack/json/reading/JsonParser.scalax +++ /dev/null @@ -1,163 +0,0 @@ -package co.blocke.scalajack -package json -package reading - -import scala.annotation.* - -object JsonParser: - - private val ull: Array[Char] = "ull".toCharArray - private val alse: Array[Char] = "alse".toCharArray - private val rue: Array[Char] = "rue".toCharArray - - def parseBoolean(in: JsonSource): Boolean = - (in.readSkipWhitespace(): @switch) match { - case 't' => - readChars(in, rue, "true") - true - case 'f' => - readChars(in, alse, "false") - false - case c => - throw JsonParseError(s"Expected true or false value", in) - } - - def parseInt(in: JsonSource): Int = { - checkNumber(in) - try { - val i = UnsafeNumbers.int_(in, false) - in.retract() - i - } catch { - case UnsafeNumbers.UnsafeNumber => throw JsonParseError("Expected an Int", in) - } - } - - def parseString(in: JsonSource): CharSequence = - charWithWS(in, '"') - val sb = new FastStringBuilder(64) - while true do - val c = in.readEscapedString() - if c == END_OF_STRING then return sb.buffer // mutable thing escapes, but cannot be changed - sb.append(c.toChar) - throw JsonParseError("Invalid string value detected", in) - - // Returns index of field name read in, or -1 if not found - def parseField(in: JsonSource, fieldMatrix: StringMatrix): Int = - val f = enumeration(in, fieldMatrix) - charWithWS(in, ':') - f - - // True if we got anything besides a ], False for ] - def firstArrayElement(in: JsonSource): Boolean = - (in.readSkipWhitespace(): @switch) match - case ']' => false - case _ => - in.retract() - true - - def nextArrayElement(in: JsonSource): Boolean = - (in.readSkipWhitespace(): @switch) match - case ',' => true - case ']' => false - case c => throw JsonParseError(s"expected ',' or ']' got '$c'", in) - - // True if we got a string (implies a retraction), False for } - def firstField(in: JsonSource): Boolean = - (in.readSkipWhitespace(): @switch) match { - case '"' => true - case '}' => false - case c => - throw JsonParseError(s"expected string or '}' got '$c'", in) - } - - // True if we got a comma, and False for } - def nextField(in: JsonSource): Boolean = - (in.readSkipWhitespace(): @switch) match { - case ',' => - charWithWS(in, '"') - true - case '}' => false - case c => - throw JsonParseError(s"expected ',' or '}' got '$c'", in) - } - - def skipValue(in: JsonSource): Unit = - (in.readSkipWhitespace(): @switch) match { - case 'n' => readChars(in, ull, "null") - case 'f' => readChars(in, alse, "false") - case 't' => readChars(in, rue, "true") - case '{' => - if firstField(in) then { - while { - { - char(in, '"') - skipString(in) - char(in, ':') - skipValue(in) - }; nextField(in) - } do () - } - case '[' => - if firstArrayElement(in) then { - while { skipValue(in); nextArrayElement(in) } do () - } - case '"' => - skipString(in) - case '-' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '.' => - skipNumber(in) - case c => throw JsonParseError(s"Unexpected '$c'", in) - } - - def skipNumber(in: JsonSource): Unit = { - while isNumber(in.read()) do {} - in.retract() - } - - def skipString(in: JsonSource): Unit = - var i: Int = 0 - while { i = in.readEscapedString(); i != -1 } do () - - private def checkNumber(in: JsonSource): Unit = - (in.readSkipWhitespace(): @switch) match - case '-' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '.' => () - case c => throw JsonParseError(s"Expected a number, got $c", in) - in.retract() - - @inline private[this] def isNumber(c: Char): Boolean = - (c: @switch) match - case '+' | '-' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '.' | 'e' | 'E' => true - case _ => false - - private inline def readChars( - in: JsonSource, - expect: Array[Char], - errMsg: String - ): Unit = - var i: Int = 0 - while i < expect.length do - if in.read() != expect(i) then throw JsonParseError(s"Expected $errMsg", in) - i += 1 - - @inline def charWithWS(in: JsonSource, c: Char): Unit = - val got = in.readSkipWhitespace() - if got != c then throw JsonParseError(s"Expected '$c' got '$got'", in) - - @inline def char(in: JsonSource, c: Char): Unit = - val got = in.read() - if got != c then throw JsonParseError(s"Expected '$c' got '$got'", in) - - def enumeration( - gen: JsonSource, - matrix: StringMatrix - ): Int = { - var i: Int = 0 - var bs: Long = matrix.initial - var c: Int = -1 - while { c = gen.readEscapedString(); c != END_OF_STRING } do { - bs = matrix.update(bs, i, c) - i += 1 - } - bs = matrix.exact(bs, i) - matrix.first(bs) - } diff --git a/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala b/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala index 8ea7730c..632335b9 100644 --- a/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala +++ b/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala @@ -41,20 +41,26 @@ case class JsonSource(js: CharSequence): if !(b == ' ' || b == '\n' || b == '\t' || (b | 0x4) == '\r') then b else readToken() - // inline def nextHex4(): Char = - // var i: Int = 0 - // var accum: Int = 0 - // while i < 4 do - // var c = readChar().toInt - // if c == BUFFER_EXCEEDED then throw JsonParseError("Unexpected end of buffer", this) - // c = - // if '0' <= c && c <= '9' then c - '0' - // else if 'A' <= c && c <= 'F' then c - 'A' + 10 - // else if 'a' <= c && c <= 'f' then c - 'a' + 10 - // else throw JsonParseError("Invalid hex character in string", this) - // accum = accum * 16 + c - // i += 1 - // accum.toChar + @tailrec + final def expectToken(t: Char): Unit = + if i == max then throw new JsonParseError("Unexpected end of buffer", this) + else + val b = here + i += 1 + if !(b == ' ' || b == '\n' || b == '\t' || (b | 0x4) == '\r') then + if b != t then + retract() + throw JsonParseError(s"Expected '$t' here", this) + else () + else expectToken(t) + + def expectNull(): Boolean = + if readToken() == 'n' then + readChars(JsonSource.ull, "null") + true + else + retract() + false // Enum... // ======================================================= @@ -72,7 +78,7 @@ case class JsonSource(js: CharSequence): readChars(JsonSource.ull, "null") null.asInstanceOf[String] case _ => - throw ParseError("Expected valid enumeration value or null here") + throw JsonParseError("Expected valid enumeration value or null here", this) // Object... // ======================================================= @@ -101,8 +107,9 @@ case class JsonSource(js: CharSequence): Some(foundIndex) else throw new JsonParseError(s"Expected object field name but found '$tt'", this) else if t == '}' then None - else throw new JsonParseError(s"Expected ',' or '}' but found $t", this) - // returns false if 'null' found + else + retract() + throw new JsonParseError(s"Expected ',' or '}' but found $t", this) final def parseObjectKey(fieldNameMatrix: StringMatrix): Int = // returns index of field name or -1 if not found var fi: Int = 0 @@ -119,7 +126,7 @@ case class JsonSource(js: CharSequence): if readToken() != ':' then throw new JsonParseError(s"Expected ':' field separator but found $here", this) fieldNameMatrix.first(bs) - // Array... + // Array and Tuple... // ======================================================= @tailrec @@ -152,6 +159,7 @@ case class JsonSource(js: CharSequence): // Value might be null! // expectString() will look for leading '"'. parseString() presumes the '"' has already been consumed. inline def expectString(): String = + mark() val t = readToken() if t == '"' then val endI = parseString(i) @@ -159,11 +167,61 @@ case class JsonSource(js: CharSequence): val str = js.subSequence(i, endI).toString i = endI + 1 str - else ??? // slower-parseString looking for escaped special chars + else // slower-parseString looking for escaped special chars + val buf = FastStringBuilder() + expectEncodedString(buf) + buf.result else if t == 'n' then readChars(JsonSource.ull, "null") null - else throw new JsonParseError(s"Expected a String value but got '$t'", this) + else + i = _mark + throw new JsonParseError(s"Expected a String value but got '$t'", this) + + @tailrec + final private def expectEncodedString(buf: FastStringBuilder): Unit = + readChar() match + case '"' => () // done + case '\\' => + readChar() match + case '"' => + buf.append('\"') + expectEncodedString(buf) + case '\\' => + buf.append('\\') + expectEncodedString(buf) + case 'b' => + buf.append('\b') + expectEncodedString(buf) + case 'f' => + buf.append('\f') + expectEncodedString(buf) + case 'n' => + buf.append('\n') + expectEncodedString(buf) + case 'r' => + buf.append('\r') + expectEncodedString(buf) + case 't' => + buf.append('\t') + expectEncodedString(buf) + case 'u' => + val hexEncoded = js.subSequence(i, i + 4) + i = i + 4 + val unicodeChar = Integer.parseInt(hexEncoded.toString, 16).toChar + buf.append(unicodeChar.toString) + expectEncodedString(buf) + case c => + buf.append(c) + expectEncodedString(buf) + case c => + buf.append(c) + expectEncodedString(buf) + + def expectString[T](parseFn: String => T): T = + expectString() match + case s: String => parseFn(s) + case null => null.asInstanceOf[T] @tailrec final def parseString(pos: Int): Int = @@ -191,11 +249,28 @@ case class JsonSource(js: CharSequence): if (bs ^ JsonSource.trueBytes) == 0 then i += 4 true - else if ((bs | js.charAt(pos + 4)) ^ JsonSource.falseBytes) == 0 then + else if pos + 4 < max && ((bs | js.charAt(pos + 4)) ^ JsonSource.falseBytes) == 0 then i += 5 false else throw JsonParseError(s"Expected 'true' or 'false'", this) + // Java Boolean can be null + def expectJavaBoolean(): java.lang.Boolean = + skipWS() + val bs = (js.charAt(pos)) | (js.charAt(pos + 1) << 8) | (js.charAt(pos + 2) << 16) | js.charAt(pos + 3) << 24 + if (bs ^ JsonSource.trueBytes) == 0 then + i += 4 + java.lang.Boolean.TRUE + else if pos + 4 < max && ((bs | js.charAt(pos + 4)) ^ JsonSource.falseBytes) == 0 then + i += 5 + java.lang.Boolean.FALSE + else if readChar() == 'n' then + readChars(JsonSource.ull, "null") + null.asInstanceOf[java.lang.Boolean] + else + retract() + throw JsonParseError(s"Expected 'true', 'false', or null here", this) + // Characters... // ======================================================= @@ -240,25 +315,40 @@ case class JsonSource(js: CharSequence): retract() result - // BUG: Can't handle single-digit ints... + def expectNumberOrNull(): String = + skipWS() + mark() + skipNumber() + if i != _mark then captureMark() + else if readChar() == 'n' then + readChars(JsonSource.ull, "null") + null + else + retract() + throw new JsonParseError("Expected a numerical value or null here", this) + def expectInt(): Int = var b = readToken() var s = -1 if b == '-' then b = readChar() s = 0 - if b < '0' || b > '9' then throw ParseError("Non-numeric digit found when integer value expected") + if b < '0' || b > '9' then + retract() + throw JsonParseError("Non-numeric character found when integer value expected", this) var x = '0' - b while { b = readChar(); b >= '0' && b <= '9' } do if x < -214748364 || { x = x * 10 + ('0' - b) x > 0 } - then throw ParseError("Integer value overflow") + then throw JsonParseError("Integer value overflow", this) x ^= s x -= s - if (s & x) == -2147483648 then throw ParseError("Integer value overflow") - if (b | 0x20) == 'e' || b == '.' then throw ParseError("Decimal digit 'e' or '.' found when integer value expected") + if (s & x) == -2147483648 then throw JsonParseError("Integer value overflow", this) + if (b | 0x20) == 'e' || b == '.' then + retract() + throw JsonParseError("Decimal digit 'e' or '.' found when integer value expected", this) retract() x @@ -281,7 +371,10 @@ case class JsonSource(js: CharSequence): case 't' => readChars(JsonSource.rue, "true") case '{' => skipObjectValue() case '[' => skipArrayValue() - case '"' => parseString(i) + case '"' => + i += 1 + val endI = parseString(i) + i = endI + 1 case '-' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '.' => skipNumber() case c => throw JsonParseError(s"Unexpected '$c'", this) diff --git a/src/main/scala/co.blocke.scalajack/json/reading/Numbers.scala b/src/main/scala/co.blocke.scalajack/json/reading/Numbers.scala index b6821be1..aafeda44 100644 --- a/src/main/scala/co.blocke.scalajack/json/reading/Numbers.scala +++ b/src/main/scala/co.blocke.scalajack/json/reading/Numbers.scala @@ -577,18 +577,20 @@ object UnsafeNumbers { var current: Int = 0 current = in.readChar() - if current == -1 then throw UnsafeNumber + if current == BUFFER_EXCEEDED then throw JsonParseError("Read buffer exceeded", in) var negative = false if current == '-' then { negative = true current = in.readChar() - if current == -1 then throw UnsafeNumber + if current == BUFFER_EXCEEDED then throw JsonParseError("Read buffer exceeded", in) } else if current == '+' then { current = in.readChar() - if current == -1 then throw UnsafeNumber + if current == BUFFER_EXCEEDED then throw JsonParseError("Read buffer exceeded", in) } - if !isDigit(current) then throw JsonParseError("Unexpected character in Int/Long value: " + current.toChar, in) // throw UnsafeNumber + if !isDigit(current) then + in.retract() + throw JsonParseError("Unexpected character in Int/Long value: " + current.toChar, in) var accum: Long = 0L while { @@ -603,7 +605,7 @@ object UnsafeNumbers { }; current != -1 && isDigit(current) } do () - if consume && current != -1 then throw UnsafeNumber + if consume && current != BUFFER_EXCEEDED then throw JsonParseError("Read buffer exceeded", in) if negative then if accum < lower || upper < accum then throw UnsafeNumber @@ -629,13 +631,15 @@ object UnsafeNumbers { while i < len do { current = in.readChar() - if current != s(i) then throw UnsafeNumber + if current != s(i) then + in.retract() + throw JsonParseError("Unexpected character in Int/Long value: " + current.toChar, in) i += 1 } current = in.readChar() // to be consistent read the terminator - if consume && current != -1 then throw UnsafeNumber + if consume && current != BUFFER_EXCEEDED then throw JsonParseError("Read buffer exceeded", in) } if current == 'N' then { @@ -657,7 +661,7 @@ object UnsafeNumbers { else return Float.PositiveInfinity } - if current == -1 then throw UnsafeNumber + if current == BUFFER_EXCEEDED then throw JsonParseError("Read buffer exceeded", in) val res = bigDecimal__(in, consume, negative = negative, initial = current, int_only = false, max_bits = max_bits) @@ -754,7 +758,7 @@ object UnsafeNumbers { def advance(): Boolean = { current = in.readChar() - current != -1 + current != BUFFER_EXCEEDED } // skip trailing zero on the left @@ -771,7 +775,7 @@ object UnsafeNumbers { .multiply(java.math.BigInteger.TEN) .add(bigIntegers(c)) // arbitrary limit on BigInteger size to avoid OOM attacks - if sig_.bitLength >= max_bits then throw UnsafeNumber + if sig_.bitLength >= max_bits then throw JsonParseError("Number of bits exceeded for Float/Double/BigDecimal", in) } else if sig >= longoverflow then sig_ = java.math.BigInteger .valueOf(sig) @@ -796,7 +800,7 @@ object UnsafeNumbers { } if int_only then { - if consume && current != -1 then throw UnsafeNumber + if consume && current != BUFFER_EXCEEDED then throw JsonParseError("Read buffer exceeded", in) return significand() } @@ -807,15 +811,17 @@ object UnsafeNumbers { dot += 1 if sig > 0 || current != '0' then push_sig() // overflowed... - if dot < 0 then throw UnsafeNumber + if dot < 0 then throw JsonParseError("Read buffer exceeded", in) advance() } } - if sig < 0 then throw UnsafeNumber // no significand + if sig < 0 then + in.retract() + throw JsonParseError("Malformed Float/Double/BigDecimal", in) // no significand if current == 'E' || current == 'e' then exp = int_(in, consume) - else if consume && current != -1 then throw UnsafeNumber + else if consume && current != BUFFER_EXCEEDED then throw JsonParseError("Read buffer exceeded", in) val scale = if dot < 1 then exp else exp - dot val res = significand() diff --git a/src/main/scala/co.blocke.scalajack/json/schema/JsonSchema.scala b/src/main/scala/co.blocke.scalajack/json/schema/JsonSchema.scala index 3fb038d3..a087c2bc 100644 --- a/src/main/scala/co.blocke.scalajack/json/schema/JsonSchema.scala +++ b/src/main/scala/co.blocke.scalajack/json/schema/JsonSchema.scala @@ -37,9 +37,6 @@ object JsonSchema: import quotes.reflect.* implicit val q: Quotes = quotes - def ofOption[T](xs: Option[Expr[T]])(using Type[T])(using Quotes): Expr[Option[T]] = - if xs.isEmpty then Expr(None) else '{ Some(${ xs.get }) } - def typeArgs(tpe: TypeRepr): List[TypeRepr] = tpe match case AppliedType(_, typeArgs) => typeArgs.map(_.dealias) case _ => Nil @@ -306,7 +303,7 @@ object JsonSchema: ${ Expr(t.annotations.get("co.blocke.scalajack.schema.description").flatMap(_.get("value"))) }, ${ if defaultValue.isDefined then - val codec = JsonCodecMaker.generateCodecFor[u](t.asInstanceOf[RTypeRef[u]], json.JsonConfig.withSuppressTypeHints()) + val codec = JsonCodecMaker.generateCodecFor[u](t.asInstanceOf[RTypeRef[u]], json.JsonConfig.suppressTypeHints()) '{ val out = new writing.JsonOutput() $codec.encodeValue(${ defaultValue.get.asExprOf[u] }, out) diff --git a/src/main/scala/co.blocke.scalajack/json/writing/JsonOutput.scala b/src/main/scala/co.blocke.scalajack/json/writing/JsonOutput.scala index 05b6aa46..60c2cecb 100644 --- a/src/main/scala/co.blocke.scalajack/json/writing/JsonOutput.scala +++ b/src/main/scala/co.blocke.scalajack/json/writing/JsonOutput.scala @@ -89,10 +89,7 @@ case class JsonOutput(): inline def value(v: scala.math.BigInt): Unit = maybeComma() if v == null then internal.append("null") - else - internal.append('"') - internal.append(v.toString) - internal.append('"') + else internal.append(v.toString) comma = true inline def valueStringified(v: scala.math.BigInt): Unit = @@ -107,10 +104,7 @@ case class JsonOutput(): inline def value(v: java.math.BigDecimal): Unit = maybeComma() if v == null then internal.append("null") - else - internal.append('"') - internal.append(v.toString) - internal.append('"') + else internal.append(v.toString) comma = true inline def valueStringified(v: java.math.BigDecimal): Unit = @@ -125,10 +119,7 @@ case class JsonOutput(): inline def value(v: java.math.BigInteger): Unit = maybeComma() if v == null then internal.append("null") - else - internal.append('"') - internal.append(v.toString) - internal.append('"') + else internal.append(v.toString) comma = true inline def valueStringified(v: java.math.BigInteger): Unit = diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index 7bae64b4..aabb0c2a 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -74,4 +74,11 @@ object RunMe extends App: // val c: Pizza = ScalaJack[Pizza].fromJson("\"READY\"") // println("Pizza: " + c) + // case class Group(t: (Int, String, Boolean)) + implicit val blah: ScalaJack[Group] = sjCodecOf[Group] + val g = Group((5, "Greg", true)) + val js = ScalaJack[Group].toJson(g) + println(js) + println(ScalaJack[Group].fromJson(js)) + println("done.") diff --git a/src/main/scala/co.blocke.scalajack/run/Record.scala b/src/main/scala/co.blocke.scalajack/run/Record.scala index 51393781..05c5d49e 100644 --- a/src/main/scala/co.blocke.scalajack/run/Record.scala +++ b/src/main/scala/co.blocke.scalajack/run/Record.scala @@ -100,6 +100,8 @@ case class Person2(age: XList) case class Foom(a: schema.Schema) +case class Group(t: (Int, String, Boolean)) + sealed trait Candy: val isSweet: Boolean case class MMs(isSweet: Boolean) extends Candy diff --git a/src/test/scala/co.blocke.scalajack/json/classes/ClassSpec.scala b/src/test/scala/co.blocke.scalajack/json/classes/ClassSpec.scalax similarity index 100% rename from src/test/scala/co.blocke.scalajack/json/classes/ClassSpec.scala rename to src/test/scala/co.blocke.scalajack/json/classes/ClassSpec.scalax diff --git a/src/test/scala/co.blocke.scalajack/json/collections/JavaCollSpec.scala b/src/test/scala/co.blocke.scalajack/json/collections/JavaCollSpec.scalax similarity index 100% rename from src/test/scala/co.blocke.scalajack/json/collections/JavaCollSpec.scala rename to src/test/scala/co.blocke.scalajack/json/collections/JavaCollSpec.scalax diff --git a/src/test/scala/co.blocke.scalajack/json/collections/JavaMapSpec.scala b/src/test/scala/co.blocke.scalajack/json/collections/JavaMapSpec.scalax similarity index 100% rename from src/test/scala/co.blocke.scalajack/json/collections/JavaMapSpec.scala rename to src/test/scala/co.blocke.scalajack/json/collections/JavaMapSpec.scalax diff --git a/src/test/scala/co.blocke.scalajack/json/collections/MapSpec.scala b/src/test/scala/co.blocke.scalajack/json/collections/MapSpec.scalax similarity index 100% rename from src/test/scala/co.blocke.scalajack/json/collections/MapSpec.scala rename to src/test/scala/co.blocke.scalajack/json/collections/MapSpec.scalax diff --git a/src/test/scala/co.blocke.scalajack/json/collections/SeqSetArraySpec.scala b/src/test/scala/co.blocke.scalajack/json/collections/SeqSetArraySpec.scalax similarity index 73% rename from src/test/scala/co.blocke.scalajack/json/collections/SeqSetArraySpec.scala rename to src/test/scala/co.blocke.scalajack/json/collections/SeqSetArraySpec.scalax index daad32e2..7cf615a4 100644 --- a/src/test/scala/co.blocke.scalajack/json/collections/SeqSetArraySpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/collections/SeqSetArraySpec.scalax @@ -17,154 +17,154 @@ class SeqSetArraySpec() extends AnyFunSpec with JsonMatchers: describe(colorString("+++ Positive Tests +++")) { it("Seq is null must work") { val inst = SeqHolder[Int](null) - val js = sj[SeqHolder[Int]].toJson(inst) + val js = sjCodecOf[SeqHolder[Int]].toJson(inst) js should matchJson("""{"a":null}""") } it("Seq of numeric must work") { val inst = SeqHolder[Int](List(1, 2, 3)) - val js = sj[SeqHolder[Int]].toJson(inst) + val js = sjCodecOf[SeqHolder[Int]].toJson(inst) js should matchJson("""{"a":[1,2,3]}""") } it("Seq of string must work") { val inst = SeqHolder[String](List("a", "b", "c")) - val js = sj[SeqHolder[String]].toJson(inst) + val js = sjCodecOf[SeqHolder[String]].toJson(inst) js should matchJson("""{"a":["a","b","c"]}""") } it("Seq of boolean must work") { val inst = SeqHolder[Boolean](List(true, false, true)) - val js = sj[SeqHolder[Boolean]].toJson(inst) + val js = sjCodecOf[SeqHolder[Boolean]].toJson(inst) js should matchJson("""{"a":[true,false,true]}""") } it("Seq of seq (nested) must work") { val inst = SeqHolder[List[Int]](List(List(1, 2), List(3, 4))) - val js = sj[SeqHolder[List[Int]]].toJson(inst) + val js = sjCodecOf[SeqHolder[List[Int]]].toJson(inst) js should matchJson("""{"a":[[1,2],[3,4]]}""") } it("Seq of either must work") { val inst = SeqHolder[Either[Int, Boolean]](List(Right(true), Left(15), Right(false))) - val js = sj[SeqHolder[Either[Int, Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)).toJson(inst) + val js = sjCodecOf[SeqHolder[Either[Int, Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)).toJson(inst) js should matchJson("""{"a":[true,15,false]}""") } it("Seq of union must work") { val inst = SeqHolder[Int | Boolean](List(true, 15, false)) - val js = sj[SeqHolder[Int | Boolean]].toJson(inst) + val js = sjCodecOf[SeqHolder[Int | Boolean]].toJson(inst) js should matchJson("""{"a":[true,15,false]}""") } it("Seq of option must work") { val inst = SeqHolder[Option[Int]](List(Some(1), None, Some(3))) - val js = sj[SeqHolder[Option[Int]]].toJson(inst) + val js = sjCodecOf[SeqHolder[Option[Int]]].toJson(inst) js should matchJson("""{"a":[1,3]}""") } it("Seq of map must work") { val inst = SeqHolder[Map[String, Int]](List(Map("a" -> 1, "b" -> 2), Map("c" -> 3, "d" -> 4))) - val js = sj[SeqHolder[Map[String, Int]]].toJson(inst) + val js = sjCodecOf[SeqHolder[Map[String, Int]]].toJson(inst) js should matchJson("""{"a":[{"a":1,"b":2},{"c":3,"d":4}]}""") } it("Seq of class must work") { val inst = SeqHolder[Person](List(Person("Bob", 35), Person("Sally", 54))) - val js = sj[SeqHolder[Person]].toJson(inst) + val js = sjCodecOf[SeqHolder[Person]].toJson(inst) js should matchJson("""{"a":[{"name":"Bob","age",35},{"name":"Sally","age",54}]}""") } it("Set is null must work") { val inst = SetHolder[Int](null) - val js = sj[SetHolder[Int]].toJson(inst) + val js = sjCodecOf[SetHolder[Int]].toJson(inst) js should matchJson("""{"a":null}""") } it("Set of numeric must work") { val inst = SetHolder[Int](HashSet(1, 2, 3)) - val js = sj[SetHolder[Int]].toJson(inst) + val js = sjCodecOf[SetHolder[Int]].toJson(inst) js should matchJson("""{"a":[1,2,3]}""") } it("Set of string must work") { val inst = SetHolder[String](HashSet("a", "b", "c")) - val js = sj[SetHolder[String]].toJson(inst) + val js = sjCodecOf[SetHolder[String]].toJson(inst) js should matchJson("""{"a":["a","b","c"]}""") } it("Set of boolean must work") { val inst = SetHolder[Boolean](HashSet(true, false, true)) - val js = sj[SetHolder[Boolean]].toJson(inst) + val js = sjCodecOf[SetHolder[Boolean]].toJson(inst) js should matchJson("""{"a":[false,true]}""") } it("Set of Set (nested) must work") { val inst = SetHolder[HashSet[Int]](HashSet(HashSet(1, 2), HashSet(3, 4))) - val js = sj[SetHolder[HashSet[Int]]].toJson(inst) + val js = sjCodecOf[SetHolder[HashSet[Int]]].toJson(inst) js should matchJson("""{"a":[[3,4],[1,2]]}""") } it("Set of either must work") { val inst = SetHolder[Either[Int, Boolean]](HashSet(Right(true), Left(15), Right(false))) - val js = sj[SetHolder[Either[Int, Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)).toJson(inst) + val js = sjCodecOf[SetHolder[Either[Int, Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)).toJson(inst) js should matchJson("""{"a":[15,true,false]}""") } it("Set of union must work") { val inst = SetHolder[Int | Boolean](HashSet(true, 15, false)) - val js = sj[SetHolder[Int | Boolean]].toJson(inst) + val js = sjCodecOf[SetHolder[Int | Boolean]].toJson(inst) js should matchJson("""{"a":[false,true,15]}""") } it("Set of option must work") { val inst = SetHolder[Option[Int]](HashSet(Some(1), None, Some(3))) - val js = sj[SetHolder[Option[Int]]].toJson(inst) + val js = sjCodecOf[SetHolder[Option[Int]]].toJson(inst) js should matchJson("""{"a":[3,1]}""") } it("Set of map must work") { val inst = SetHolder[Map[String, Int]](HashSet(Map("a" -> 1, "b" -> 2), Map("c" -> 3, "d" -> 4))) - val js = sj[SetHolder[Map[String, Int]]].toJson(inst) + val js = sjCodecOf[SetHolder[Map[String, Int]]].toJson(inst) js should matchJson("""{"a":[{"c":3,"d":4},{"a":1,"b":2}]}""") } it("Set of class must work") { val inst = SetHolder[Person](HashSet(Person("Bob", 35), Person("Sally", 54))) - val js = sj[SetHolder[Person]].toJson(inst) + val js = sjCodecOf[SetHolder[Person]].toJson(inst) js should matchJson("""{"a":[{"name":"Bob","age",35},{"name":"Sally","age",54}]}""") } it("Array is null must work") { val inst = ArrayHolder[Int](null) - val js = sj[ArrayHolder[Int]].toJson(inst) + val js = sjCodecOf[ArrayHolder[Int]].toJson(inst) js should matchJson("""{"a":null}""") } it("Array of numeric must work") { val inst = ArrayHolder[Int](Array(1, 2, 3)) - val js = sj[ArrayHolder[Int]].toJson(inst) + val js = sjCodecOf[ArrayHolder[Int]].toJson(inst) js should matchJson("""{"a":[1,2,3]}""") } it("Array of string must work") { val inst = ArrayHolder[String](Array("a", "b", "c")) - val js = sj[ArrayHolder[String]].toJson(inst) + val js = sjCodecOf[ArrayHolder[String]].toJson(inst) js should matchJson("""{"a":["a","b","c"]}""") } it("Array of boolean must work") { val inst = ArrayHolder[Boolean](Array(true, false, true)) - val js = sj[ArrayHolder[Boolean]].toJson(inst) + val js = sjCodecOf[ArrayHolder[Boolean]].toJson(inst) js should matchJson("""{"a":[true,false,true]}""") } it("Array of Array (nested) must work") { val inst = ArrayHolder[Array[Int]](Array(Array(1, 2), Array(3, 4))) - val js = sj[ArrayHolder[Array[Int]]].toJson(inst) + val js = sjCodecOf[ArrayHolder[Array[Int]]].toJson(inst) js should matchJson("""{"a":[[1,2],[3,4]]}""") } it("Array of either must work") { val inst = ArrayHolder[Either[Int, Boolean]](Array(Right(true), Left(15), Right(false))) - val js = sj[ArrayHolder[Either[Int, Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)).toJson(inst) + val js = sjCodecOf[ArrayHolder[Either[Int, Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)).toJson(inst) js should matchJson("""{"a":[true,15,false]}""") } it("Array of union must work") { val inst = ArrayHolder[Int | Boolean](Array(true, 15, false)) - val js = sj[ArrayHolder[Int | Boolean]].toJson(inst) + val js = sjCodecOf[ArrayHolder[Int | Boolean]].toJson(inst) js should matchJson("""{"a":[true,15,false]}""") } it("Array of option must work") { val inst = ArrayHolder[Option[Int]](Array(Some(1), None, Some(3))) - val js = sj[ArrayHolder[Option[Int]]].toJson(inst) + val js = sjCodecOf[ArrayHolder[Option[Int]]].toJson(inst) js should matchJson("""{"a":[1,3]}""") } it("Array of map must work") { val inst = ArrayHolder[Map[String, Int]](Array(Map("a" -> 1, "b" -> 2), Map("c" -> 3, "d" -> 4))) - val js = sj[ArrayHolder[Map[String, Int]]].toJson(inst) + val js = sjCodecOf[ArrayHolder[Map[String, Int]]].toJson(inst) js should matchJson("""{"a":[{"a":1,"b":2},{"c":3,"d":4}]}""") } it("Array of class must work") { val inst = ArrayHolder[Person](Array(Person("Bob", 35), Person("Sally", 54))) - val js = sj[ArrayHolder[Person]].toJson(inst) + val js = sjCodecOf[ArrayHolder[Person]].toJson(inst) js should matchJson("""{"a":[{"name":"Bob","age",35},{"name":"Sally","age",54}]}""") } } diff --git a/src/test/scala/co.blocke.scalajack/json/collections/TupleSpec.scala b/src/test/scala/co.blocke.scalajack/json/collections/TupleSpec.scalax similarity index 100% rename from src/test/scala/co.blocke.scalajack/json/collections/TupleSpec.scala rename to src/test/scala/co.blocke.scalajack/json/collections/TupleSpec.scalax diff --git a/src/test/scala/co.blocke.scalajack/json/misc/AliasSpec.scala b/src/test/scala/co.blocke.scalajack/json/misc/AliasSpec.scalax similarity index 100% rename from src/test/scala/co.blocke.scalajack/json/misc/AliasSpec.scala rename to src/test/scala/co.blocke.scalajack/json/misc/AliasSpec.scalax diff --git a/src/test/scala/co.blocke.scalajack/json/misc/MiscTests.scala b/src/test/scala/co.blocke.scalajack/json/misc/MiscTests.scalax similarity index 100% rename from src/test/scala/co.blocke.scalajack/json/misc/MiscTests.scala rename to src/test/scala/co.blocke.scalajack/json/misc/MiscTests.scalax diff --git a/src/test/scala/co.blocke.scalajack/json/misc/OptionLRTrySpec.scala b/src/test/scala/co.blocke.scalajack/json/misc/OptionLRTrySpec.scala index 27793051..1165e091 100644 --- a/src/test/scala/co.blocke.scalajack/json/misc/OptionLRTrySpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/misc/OptionLRTrySpec.scala @@ -14,6 +14,7 @@ import java.util.UUID class OptionLRTrySpec() extends AnyFunSpec with JsonMatchers: + /* describe(colorString("-------------------------------\n: Option Tests :\n-------------------------------", Console.YELLOW)) { describe(colorString("+++ Positive Tests +++")) { it("Non-empty Options must work") { @@ -29,7 +30,7 @@ class OptionLRTrySpec() extends AnyFunSpec with JsonMatchers: Right(Some(15)), // Either of Option (R) Left(Some(-3)) // Either of Option (L) ) - val js = sj[OptionHolder[Int]].toJson(inst) + val js = sjCodecOf[OptionHolder[Int]].toJson(inst) js should matchJson("""{"a":5,"b":[1,"ok"],"c":[1,3],"d":{"1":2,"3":4},"e":99,"f":100,"g":0,"h":{"name":"BoB","age":34},"i":15}""") } it("Empty Options must work (default)") { @@ -45,9 +46,10 @@ class OptionLRTrySpec() extends AnyFunSpec with JsonMatchers: Right(None), // Either of Option (R) Left(None) // Either of Option (L) ) - val js = sj[OptionHolder[Int]].toJson(inst) + val js = sjCodecOf[OptionHolder[Int]].toJson(inst) js should matchJson("""{"b":[null,"ok"],"c":[],"d":{}}""") } + /* it("Empty Options must work (config noneAsNull = true)") { val inst = OptionHolder[Int]( None, // straight Option @@ -66,67 +68,92 @@ class OptionLRTrySpec() extends AnyFunSpec with JsonMatchers: ).toJson(inst) js should matchJson("""{"a":null,"b":[null,"ok"],"c":[null,null,null],"d":{"1":null,"3":null},"e":null,"f":null,"g":null,"h":null,"i":null,"j":null}""") } + */ } } + */ describe(colorString("-------------------------------\n: Either Tests :\n-------------------------------", Console.YELLOW)) { describe(colorString("+++ Positive Tests +++")) { it("Complex Either/Option must work (non-None)") { val inst = ComplexEither[Int](Some(Right(Some(3)))) - val js = sj[ComplexEither[Int]].toJson(inst) + val sj = sjCodecOf[ComplexEither[Int]] + val js = sj.toJson(inst) js should matchJson("""{"a":3}""") + sj.fromJson(js) shouldEqual (inst) } it("Complex Either/Option must work (None no-write default)") { val inst = ComplexEither[Int](Some(Right(None))) - val js = sj[ComplexEither[Int]].toJson(inst) + val sj = sjCodecOf[ComplexEither[Int]] + val js = sj.toJson(inst) js should matchJson("""{}""") + sj.fromJson(js) shouldEqual (ComplexEither(null)) } it("Complex Either/Option must work (NoneAsNull)") { val inst = ComplexEither[Int](Some(Right(None))) - val js = sj[ComplexEither[Int]](JsonConfig.withNoneAsNull()).toJson(inst) + val sj = sjCodecOf[ComplexEither[Int]](JsonConfig.withNoneAsNull()) + val js = sj.toJson(inst) js should matchJson("""{"a":null}""") + sj.fromJson(js) shouldEqual (ComplexEither(None)) // None here because value existed, but was null with NoneAsNull } it("Complex Either/Option must work (Left-NO_WRITE)") { val inst = ComplexEither[Int](Some(Left("err"))) - val js = sj[ComplexEither[Int]].toJson(inst) + val sj = sjCodecOf[ComplexEither[Int]] + val js = sjCodecOf[ComplexEither[Int]].toJson(inst) js should matchJson("""{}""") + sj.fromJson(js) shouldEqual (ComplexEither(null)) // Null because value didn't exist at all } it("Complex Either/Option must work (Left-AS_VALUE)") { val inst = ComplexEither[Int](Some(Left("err"))) - val js = sj[ComplexEither[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)).toJson(inst) + val sj = sjCodecOf[ComplexEither[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)) + val js = sj.toJson(inst) js should matchJson("""{"a":"err"}""") + sj.fromJson(js) shouldEqual (inst) } it("Either with AS_VALUE left policy must work") { val inst = EitherHolder[Int](Left(5), Right(3)) - val js = sj[EitherHolder[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)).toJson(inst) + val sj = sjCodecOf[EitherHolder[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)) + val js = sj.toJson(inst) js should matchJson("""{"a":5,"b":3}""") + sj.fromJson(js) shouldEqual (inst) } it("Either with AS_NULL left policy must work") { val inst = EitherHolder[Int](Left(5), Right(3)) - val js = sj[EitherHolder[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_NULL)).toJson(inst) + val sj = sjCodecOf[EitherHolder[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_NULL)) + val js = sj.toJson(inst) js should matchJson("""{"a":null,"b":3}""") + sj.fromJson(js) shouldEqual (EitherHolder(null, Right(3))) } it("Either with NO_WRITE left policy must work") { val inst = EitherHolder[Int](Left(5), Right(3)) - val js = sj[EitherHolder[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.NO_WRITE)).toJson(inst) + val sj = sjCodecOf[EitherHolder[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.NO_WRITE)) + val js = sj.toJson(inst) js should matchJson("""{"b":3}""") + // This js cannot be read back in becuase it's missing required field 'a', which wasn't written out + // per NO_WRITE policy. This is a 1-way trip... so be advised... } it("Either with ERR_MSG_STRING left policy must work") { val inst = EitherHolder[Int](Left(5), Right(3)) - val js = sj[EitherHolder[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.ERR_MSG_STRING)).toJson(inst) + val sj = sjCodecOf[EitherHolder[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.ERR_MSG_STRING)) + val js = sj.toJson(inst) js should matchJson("""{"a":"Left Error: 5","b":3}""") + sj.fromJson(js) shouldEqual (EitherHolder(Right("Left Error: 5"), Right(3))) + // WARNING! Here a Left(err_msg) was "promoted" to a Right(String) upon read, because Right was of type + // String, and "Left Error: 5" is a valid string. Use with extreme caution. Best to consider this a 1-way + // trip for debugging purposes only. You have been warned. } it("Either with THROW_EXCEPTION left policy must work") { val inst = EitherHolder[Int](Left(5), Right(3)) val caught = intercept[JsonEitherLeftError] { - sj[EitherHolder[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.THROW_EXCEPTION)).toJson(inst) + sjCodecOf[EitherHolder[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.THROW_EXCEPTION)).toJson(inst) } assert(caught.getMessage == "Left Error: 5") } } } + /* describe(colorString("-------------------------------\n: LR Tests :\n-------------------------------", Console.YELLOW)) { describe(colorString("+++ Positive Tests +++")) { it("LR (union) must work with Option (non-None)") { @@ -214,3 +241,4 @@ class OptionLRTrySpec() extends AnyFunSpec with JsonMatchers: } } } + */ diff --git a/src/test/scala/co.blocke.scalajack/json/primitives/JavaPrimSpec.scala b/src/test/scala/co.blocke.scalajack/json/primitives/JavaPrimSpec.scala index 16781ef5..9a2d6c2a 100644 --- a/src/test/scala/co.blocke.scalajack/json/primitives/JavaPrimSpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/primitives/JavaPrimSpec.scala @@ -26,9 +26,9 @@ class JavaPrimSpec() extends AnyFunSpec with JsonMatchers: ), null ) - val js = sj[SampleJBigDecimal].toJson(inst) + val js = sjCodecOf[SampleJBigDecimal].toJson(inst) js should matchJson("""{"bd1":0,"bd2":1,"bd3":10,"bd4":0.1499999999999999944488848768742172978818416595458984375,"bd5":null}""") - // inst shouldEqual ScalaJack.read[SampleJBigDecimal](js) + sjCodecOf[SampleJBigDecimal].fromJson(js) shouldEqual inst } it("BigInteger must work") { @@ -41,16 +41,16 @@ class JavaPrimSpec() extends AnyFunSpec with JsonMatchers: new JBigInteger("0"), null ) - val js = sj[SampleJBigInteger].toJson(inst) + val js = sjCodecOf[SampleJBigInteger].toJson(inst) js should matchJson("""{"bi1":0,"bi2":1,"bi3":10,"bi4":-90182736451928374653345,"bi5":90182736451928374653345,"bi6":0,"bi7":null}""") - // inst shouldEqual ScalaJack.read[SampleJBigInteger](js) + sjCodecOf[SampleJBigInteger].fromJson(js) shouldEqual inst } it("Boolean must work") { val inst = SampleJBoolean(JBoolean.TRUE, JBoolean.FALSE, true, false, null) - val js = sj[SampleJBoolean].toJson(inst) + val js = sjCodecOf[SampleJBoolean].toJson(inst) js should matchJson("""{"bool1":true,"bool2":false,"bool3":true,"bool4":false,"bool5":null}""") - // inst shouldEqual ScalaJack.read[SampleJBoolean](js) + sjCodecOf[SampleJBoolean].fromJson(js) shouldEqual inst } it("Byte must work") { @@ -61,16 +61,16 @@ class JavaPrimSpec() extends AnyFunSpec with JsonMatchers: 64.asInstanceOf[Byte], null ) - val js = sj[SampleJByte].toJson(inst) + val js = sjCodecOf[SampleJByte].toJson(inst) js should matchJson("""{"b1":127,"b2":-128,"b3":0,"b4":64,"b5":null}""") - // inst shouldEqual ScalaJack.read[SampleJByte](js) + sjCodecOf[SampleJByte].fromJson(js) shouldEqual inst } it("Character must work") { val inst = SampleJChar('Z', '\u20A0', null) - val js = sj[SampleJChar].toJson(inst) + val js = sjCodecOf[SampleJChar].toJson(inst) js should matchJson("""{"c1":"Z","c2":"\""" + """u20a0","c3":null}""") - // inst shouldEqual ScalaJack.read[SampleJChar](js) + sjCodecOf[SampleJChar].fromJson(js) shouldEqual inst } it("Double must work") { @@ -81,9 +81,9 @@ class JavaPrimSpec() extends AnyFunSpec with JsonMatchers: -123.4567, null ) - val js = sj[SampleJDouble].toJson(inst) + val js = sjCodecOf[SampleJDouble].toJson(inst) js should matchJson("""{"d1":1.7976931348623157E308,"d2":4.9E-324,"d3":0.0,"d4":-123.4567,"d5":null}""") - // inst shouldEqual ScalaJack.read[SampleJDouble](js) + sjCodecOf[SampleJDouble].fromJson(js) shouldEqual inst } it("Float must work") { @@ -94,23 +94,23 @@ class JavaPrimSpec() extends AnyFunSpec with JsonMatchers: -123.4567f, null ) - val js = sj[SampleJFloat].toJson(inst) + val js = sjCodecOf[SampleJFloat].toJson(inst) js should matchJson("""{"f1":3.4028235E38,"f2":1.4E-45,"f3":0.0,"f4":-123.4567,"f5":null}""") - // inst shouldEqual ScalaJack.read[SampleJFloat](js) + sjCodecOf[SampleJFloat].fromJson(js) shouldEqual inst } it("Integer must work") { val inst = SampleJInt(JInt.MAX_VALUE, JInt.MIN_VALUE, 0, 123, null) - val js = sj[SampleJInt].toJson(inst) + val js = sjCodecOf[SampleJInt].toJson(inst) js should matchJson("""{"i1":2147483647,"i2":-2147483648,"i3":0,"i4":123,"i5":null}""") - // inst shouldEqual ScalaJack.read[SampleJInt](js) + sjCodecOf[SampleJInt].fromJson(js) shouldEqual inst } it("Long must work") { val inst = SampleJLong(JLong.MAX_VALUE, JLong.MIN_VALUE, 0L, 123L, null) - val js = sj[SampleJLong].toJson(inst) + val js = sjCodecOf[SampleJLong].toJson(inst) js should matchJson("""{"l1":9223372036854775807,"l2":-9223372036854775808,"l3":0,"l4":123,"l5":null}""") - // inst shouldEqual ScalaJack.read[SampleJLong](js) + sjCodecOf[SampleJLong].fromJson(js) shouldEqual inst } it("Number must work") { @@ -133,11 +133,11 @@ class JavaPrimSpec() extends AnyFunSpec with JsonMatchers: JFloat.valueOf("0.0"), null ) - val js = sj[SampleJNumber].toJson(inst) + val js = sjCodecOf[SampleJNumber].toJson(inst) js should matchJson( """{"n1":-128,"n2":127,"n3":-32768,"n4":32767,"n5":-2147483648,"n6":2147483647,"n7":-9223372036854775808,"n8":9223372036854755807,"n9":null,"n10":0,"n11":3.4E-38,"n12":3.4E38,"n13":1.7E-308,"n14":1.7E308,"n15":null,"n16":0.0,"n17":null}""" ) - // inst shouldEqual ScalaJack.read[SampleJNumber](js) + sjCodecOf[SampleJNumber].fromJson(js) shouldEqual inst } it("Short must work") { @@ -148,159 +148,141 @@ class JavaPrimSpec() extends AnyFunSpec with JsonMatchers: 123.asInstanceOf[Short], null ) - val js = sj[SampleJShort].toJson(inst) + val js = sjCodecOf[SampleJShort].toJson(inst) js should matchJson("""{"s1":32767,"s2":-32768,"s3":0,"s4":123,"s5":null}""") - // inst shouldEqual ScalaJack.read[SampleJShort](js) + sjCodecOf[SampleJShort].fromJson(js) shouldEqual inst } } - } - -/* - - //-------------------------------------------------------- + // -------------------------------------------------------- - test("BigDecimal must break") { - describe("--- Negative Tests ---") - val js = - """{"bd1":0,"bd2":1,"bd3":10,"bd4":"0.1499999999999999944488848768742172978818416595458984375","bd5":null}""".asInstanceOf[JSON] - val msg = - """Expected a Number here - |{"bd1":0,"bd2":1,"bd3":10,"bd4":"0.149999999999999994448884876874217297881841... - |--------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleJBigDecimal](js) - } - } + describe(colorString("--- Negative Tests ---")) { + it("BigDecimal must break") { + val js = + """{"bd1":0,"bd2":1,"bd3":10,"bd4":"0.1499999999999999944488848768742172978818416595458984375","bd5":null}""" + val msg = + """Expected a numerical value or null here at position [32] + |{"bd1":0,"bd2":1,"bd3":10,"bd4":"0.149999999999999994448884876874217297881841... + |--------------------------------^""".stripMargin + val ex = intercept[JsonParseError](sjCodecOf[SampleJBigDecimal].fromJson(js)) + ex.show shouldEqual msg + } - test("BigInt must break") { - val js = - """{"bi1":"0","bi2":1,"bi3":10,"bi4":-90182736451928374653345,"bi5":90182736451928374653345,"bi6":0,"bi7":null}""".asInstanceOf[JSON] - val msg = - """Expected a Number here - |{"bi1":"0","bi2":1,"bi3":10,"bi4":-90182736451928374653345,"bi5":901827364519... - |-------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleJBigInteger](js) - } - } + it("BigInt must break") { + val js = + """{"bi1":"0","bi2":1,"bi3":10,"bi4":-90182736451928374653345,"bi5":90182736451928374653345,"bi6":0,"bi7":null}""" + val msg = + """Expected a numerical value or null here at position [7] + |{"bi1":"0","bi2":1,"bi3":10,"bi4":-90182736451928374653345,"bi5":901827364519... + |-------^""".stripMargin + val ex = intercept[JsonParseError](sjCodecOf[SampleJBigInteger].fromJson(js)) + ex.show shouldEqual msg + } - test("Boolean must break") { - val js = """{"bool1":true,"bool2":false,"bool3":true,"bool4":"false","bool5":null}""".asInstanceOf[JSON] - val msg = - """Expected a Boolean here - |{"bool1":true,"bool2":false,"bool3":true,"bool4":"false","bool5":null} - |-------------------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleJBoolean](js) - } - } + it("Boolean must break") { + val js = """{"bool1":true,"bool2":false,"bool3":true,"bool4":"false","bool5":null}""" + val msg = + """Expected 'true', 'false', or null here at position [49] + |{"bool1":true,"bool2":false,"bool3":true,"bool4":"false","bool5":null} + |-------------------------------------------------^""".stripMargin + val ex = intercept[JsonParseError](sjCodecOf[SampleJBoolean].fromJson(js)) + ex.show shouldEqual msg + } - test("Byte must break") { - val js = """{"b1":127,"b2":-128,"b3":false,"b4":64,"b5":null}""".asInstanceOf[JSON] - val msg = """Expected a Number here - |{"b1":127,"b2":-128,"b3":false,"b4":64,"b5":null} - |-------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleJByte](js) - } - } + it("Byte must break") { + val js = """{"b1":127,"b2":-128,"b3":false,"b4":64,"b5":null}""" + val msg = """Expected a numerical value or null here at position [25] + |{"b1":127,"b2":-128,"b3":false,"b4":64,"b5":null} + |-------------------------^""".stripMargin + val ex = intercept[JsonParseError](sjCodecOf[SampleJByte].fromJson(js)) + ex.show shouldEqual msg + } - test("Char must break") { - val js = """{"c1":"Z","c2":3,"c3":null}""".asInstanceOf[JSON] - val msg = """Expected a String here - |{"c1":"Z","c2":3,"c3":null} - |---------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleJChar](js) - } - val js2 = """{"c1":"Z","c2":"","c3":null}""".asInstanceOf[JSON] - val msg2 = """Tried to read a Character but empty string found - |{"c1":"Z","c2":"","c3":null} - |----------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ - sj.read[SampleJChar](js2) - } - } + it("Char must break") { + val sj = sjCodecOf[SampleJChar] + val js = """{"c1":"Z","c2":3,"c3":null}""" + val msg = """Expected a String value but got '3' at position [15] + |{"c1":"Z","c2":3,"c3":null} + |---------------^""".stripMargin + val ex = intercept[JsonParseError](sjCodecOf[SampleJChar].fromJson(js)) + ex.show shouldEqual msg + val js2 = """{"c1":"Z","c2":"","c3":null}""" + val msg2 = """Character value expected but empty string found in json at position [15] + |{"c1":"Z","c2":"","c3":null} + |---------------^""".stripMargin + val ex2 = intercept[JsonParseError](sjCodecOf[SampleJChar].fromJson(js2)) + ex2.show shouldEqual msg2 + } - test("Double must break") { - val js = - """{"d1":1.7976931348623157E308,"d2":4.9E-324,"d3":"0.0","d4":-123.4567,"d5":null}""".asInstanceOf[JSON] - val msg = - """Expected a Number here - |{"d1":1.7976931348623157E308,"d2":4.9E-324,"d3":"0.0","d4":-123.4567,"d5":null} - |------------------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleJDouble](js) - } - } + it("Double must break") { + val js = + """{"d1":1.7976931348623157E308,"d2":4.9E-324,"d3":"0.0","d4":-123.4567,"d5":null}""" + val msg = + """Expected a numerical value or null here at position [48] + |{"d1":1.7976931348623157E308,"d2":4.9E-324,"d3":"0.0","d4":-123.4567,"d5":null} + |------------------------------------------------^""".stripMargin + val ex = intercept[JsonParseError](sjCodecOf[SampleJDouble].fromJson(js)) + ex.show shouldEqual msg + } - test("Float must break") { - val js = - """{"f1":3.4028235E38,"f2":"1.4E-45","f3":0.0,"f4":-123.4567,"f5":null}""".asInstanceOf[JSON] - val msg = - """Expected a Number here - |{"f1":3.4028235E38,"f2":"1.4E-45","f3":0.0,"f4":-123.4567,"f5":null} - |------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleJFloat](js) - } - } + it("Float must break") { + val js = + """{"f1":3.4028235E38,"f2":"1.4E-45","f3":0.0,"f4":-123.4567,"f5":null}""" + val msg = + """Expected a numerical value or null here at position [24] + |{"f1":3.4028235E38,"f2":"1.4E-45","f3":0.0,"f4":-123.4567,"f5":null} + |------------------------^""".stripMargin + val ex = intercept[JsonParseError](sjCodecOf[SampleJFloat].fromJson(js)) + ex.show shouldEqual msg + } - test("Int must break") { - val js = """{"i1":2147483647,"i2":-2147483648,"i3":false,"i4":123,"i5":null}""".asInstanceOf[JSON] - val msg = - """Expected a Number here - |{"i1":2147483647,"i2":-2147483648,"i3":false,"i4":123,"i5":null} - |---------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleJInt](js) - } - val js2 = """{"i1":2147483647,"i2":-2147483648,"i3":0.3,"i4":123,"i5":null}""".asInstanceOf[JSON] - interceptMessage[java.lang.NumberFormatException]("For input string: \"0.3\""){ - sj.read[SampleJInt](js2) - } - } + it("Int must break") { + val js = """{"i1":2147483647,"i2":-2147483648,"i3":false,"i4":123,"i5":null}""" + val msg = + """Expected a numerical value or null here at position [39] + |{"i1":2147483647,"i2":-2147483648,"i3":false,"i4":123,"i5":null} + |---------------------------------------^""".stripMargin + val ex = intercept[JsonParseError](sjCodecOf[SampleJInt].fromJson(js)) + ex.show shouldEqual msg + val js2 = """{"i1":2147483647,"i2":-2147483648,"i3":0.3,"i4":123,"i5":null}""" + the[java.lang.NumberFormatException] thrownBy sjCodecOf[SampleJInt].fromJson(js2) should have message "For input string: \"0.3\"" + } - test("Long must break") { - val js = - """{"l1":9223372036854775807,"l2":-9223372036854775808,"l3":"0","l4":123,"l5":null}""".asInstanceOf[JSON] - val msg = - """Expected a Number here - |...23372036854775807,"l2":-9223372036854775808,"l3":"0","l4":123,"l5":null} - |----------------------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleJLong](js) - } - val js2 = - """{"l1":9223372036854775807,"l2":-9223372036854775808,"l3":0.3,"l4":123,"l5":null}""".asInstanceOf[JSON] - interceptMessage[java.lang.NumberFormatException]("For input string: \"0.3\""){ - sj.read[SampleJLong](js2) - } - } + it("Long must break") { + val js = + """{"l1":9223372036854775807,"l2":-9223372036854775808,"l3":"0","l4":123,"l5":null}""" + val msg = + """Expected a numerical value or null here at position [57] + |...23372036854775807,"l2":-9223372036854775808,"l3":"0","l4":123,"l5":null} + |----------------------------------------------------^""".stripMargin + val ex = intercept[JsonParseError](sjCodecOf[SampleJLong].fromJson(js)) + ex.show shouldEqual msg + val js2 = + """{"l1":9223372036854775807,"l2":-9223372036854775808,"l3":0.3,"l4":123,"l5":null}""" + the[java.lang.NumberFormatException] thrownBy sjCodecOf[SampleJLong].fromJson(js2) should have message "For input string: \"0.3\"" + } - test("Number must break") { - val js = """{"n1":-128,"n2":127,"n3":"-32768","n4":32767,"n5":-2147483648,"n6":2147483647,"n7":-9223372036854775808,"n8":9223372036854755807,"n9":9923372036854755810,"n10":0,"n11":3.4E-38,"n12":3.4E38,"n13":1.7E-308,"n14":1.7E308,"n15":1.8E+308,"n16":0.0,"n17":null}""".asInstanceOf[JSON] - val msg = - """Expected a Number here - |{"n1":-128,"n2":127,"n3":"-32768","n4":32767,"n5":-2147483648,"n6":2147483647... - |-------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleJNumber](js) - } - } + it("Number must break") { + val js = + """{"n1":-128,"n2":127,"n3":"-32768","n4":32767,"n5":-2147483648,"n6":2147483647,"n7":-9223372036854775808,"n8":9223372036854755807,"n9":9923372036854755810,"n10":0,"n11":3.4E-38,"n12":3.4E38,"n13":1.7E-308,"n14":1.7E308,"n15":1.8E+308,"n16":0.0,"n17":null}""" + val msg = + """Expected a numerical value or null here at position [25] + |{"n1":-128,"n2":127,"n3":"-32768","n4":32767,"n5":-2147483648,"n6":2147483647... + |-------------------------^""".stripMargin + val ex = intercept[JsonParseError](sjCodecOf[SampleJNumber].fromJson(js)) + ex.show shouldEqual msg + } - test("Short must break") { - val js = """{"s1":false,"s2":-32768,"s3":0,"s4":123,"s5":null}""".asInstanceOf[JSON] - val msg = """Expected a Number here - |{"s1":false,"s2":-32768,"s3":0,"s4":123,"s5":null} - |------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleJShort](js) - } - val js2 = """{"s1":2.3,"s2":-32768,"s3":0,"s4":123,"s5":null}""".asInstanceOf[JSON] - interceptMessage[java.lang.NumberFormatException]("For input string: \"2.3\""){ - sj.read[SampleJShort](js2) + it("Short must break") { + val sj = sjCodecOf[SampleJByte] + val js = """{"s1":false,"s2":-32768,"s3":0,"s4":123,"s5":null}""" + val msg = """Expected a numerical value or null here at position [6] + |{"s1":false,"s2":-32768,"s3":0,"s4":123,"s5":null} + |------^""".stripMargin + val ex = intercept[JsonParseError](sjCodecOf[SampleJShort].fromJson(js)) + ex.show shouldEqual msg + val js2 = """{"s1":2.3,"s2":-32768,"s3":0,"s4":123,"s5":null}""" + the[java.lang.NumberFormatException] thrownBy sjCodecOf[SampleJShort].fromJson(js2) should have message "For input string: \"2.3\"" + } } } - */ diff --git a/src/test/scala/co.blocke.scalajack/json/primitives/ScalaPrimSpec.scala b/src/test/scala/co.blocke.scalajack/json/primitives/ScalaPrimSpec.scala index fa3e29c8..5968f70d 100644 --- a/src/test/scala/co.blocke.scalajack/json/primitives/ScalaPrimSpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/primitives/ScalaPrimSpec.scala @@ -27,9 +27,10 @@ class ScalaPrimSpec() extends AnyFunSpec with JsonMatchers: null ) - val js = sj[SampleBigDecimal].toJson(inst) + val sj = sjCodecOf[SampleBigDecimal] + val js = sj.toJson(inst) js should matchJson("""{"bd1":123,"bd2":1.23,"bd3":0,"bd4":123.456,"bd5":0.1499999999999999944488848768742172978818416595458984375,"bd6":null}""") - // inst shouldEqual ScalaJack.read[SampleBigDecimal](js) + sj.fromJson(js) shouldEqual inst } it("BigInt must work") { @@ -39,85 +40,84 @@ class ScalaPrimSpec() extends AnyFunSpec with JsonMatchers: BigInt(0), null ) - val js = sj[SampleBigInt].toJson(inst) + val sj = sjCodecOf[SampleBigInt] + val js = sj.toJson(inst) js should matchJson("""{"bi1":-90182736451928374653345,"bi2":90182736451928374653345,"bi3":0,"bi4":null}""") - // inst shouldEqual ScalaJack.read[SampleBigInt](js) + sj.fromJson(js) shouldEqual inst } it("Boolean must work (not nullable)") { val inst = SampleBoolean(bool1 = true, bool2 = false) - val js = sj[SampleBoolean].toJson(inst) + val sj = sjCodecOf[SampleBoolean] + val js = sj.toJson(inst) js should matchJson("""{"bool1":true,"bool2":false}""") - // inst shouldEqual ScalaJack.read[SampleBoolean](js) + sj.fromJson(js) shouldEqual inst } it("Byte must work (not nullable)") { val inst = SampleByte(Byte.MaxValue, Byte.MinValue, 0, 64) - val js = sj[SampleByte].toJson(inst) + val sj = sjCodecOf[SampleByte] + val js = sj.toJson(inst) js should matchJson("""{"b1":127,"b2":-128,"b3":0,"b4":64}""") - // inst shouldEqual ScalaJack.read[SampleByte](js) + sj.fromJson(js) shouldEqual inst } it("Char must work (not nullable)") { val inst = SampleChar(Char.MaxValue, 'Z', '\u20A0') - val js = sj[SampleChar].toJson(inst) + val sj = sjCodecOf[SampleChar] + val js = sj.toJson(inst) js should matchJson("""{"c1":"\""" + """uffff","c2":"Z","c3":"\""" + """u20a0"}""") - // inst shouldEqual ScalaJack.read[SampleChar](js) + sj.fromJson(js) shouldEqual inst } it("Double must work (not nullable)") { val inst = SampleDouble(Double.MaxValue, Double.MinValue, 0.0, -123.4567) - val js = sj[SampleDouble].toJson(inst) + val sj = sjCodecOf[SampleDouble] + val js = sj.toJson(inst) js should matchJson("""{"d1":1.7976931348623157E308,"d2":-1.7976931348623157E308,"d3":0.0,"d4":-123.4567}""") - // inst shouldEqual ScalaJack.read[SampleDouble](js) + sj.fromJson(js) shouldEqual inst } it("Float must work (not nullable)") { val inst = SampleFloat(Float.MaxValue, Float.MinValue, 0.0f, -123.4567f) - val js = sj[SampleFloat].toJson(inst) + val sj = sjCodecOf[SampleFloat] + val js = sj.toJson(inst) js should matchJson("""{"f1":3.4028235E38,"f2":-3.4028235E38,"f3":0.0,"f4":-123.4567}""") - // inst shouldEqual ScalaJack.read[SampleFloat](js) + sj.fromJson(js) shouldEqual inst } it("Int must work (not nullable)") { val inst = SampleInt(Int.MaxValue, Int.MinValue, 0, 123) - val js = sj[SampleInt].toJson(inst) + val sj = sjCodecOf[SampleInt] + val js = sj.toJson(inst) js should matchJson("""{"i1":2147483647,"i2":-2147483648,"i3":0,"i4":123}""") - // inst shouldEqual ScalaJack.read[SampleInt](js) + sj.fromJson(js) shouldEqual inst } it("Long must work (not nullable)") { val inst = SampleLong(Long.MaxValue, Long.MinValue, 0L, 123L) - val js = sj[SampleLong].toJson(inst) + val sj = sjCodecOf[SampleLong] + val js = sj.toJson(inst) js should matchJson("""{"l1":9223372036854775807,"l2":-9223372036854775808,"l3":0,"l4":123}""") - // inst shouldEqual ScalaJack.read[SampleLong](js) + sj.fromJson(js) shouldEqual inst } it("Short must work (not nullable)") { val inst = SampleShort(Short.MaxValue, Short.MinValue, 0, 123) - val js = sj[SampleShort].toJson(inst) + val sj = sjCodecOf[SampleShort] + val js = sj.toJson(inst) js should matchJson("""{"s1":32767,"s2":-32768,"s3":0,"s4":123}""") - // inst shouldEqual ScalaJack.read[SampleShort](js) + sj.fromJson(js) shouldEqual inst } it("String must work") { val inst = SampleString("something\b\n\f\r\t☆", "", null) - val js = sj[SampleString].toJson(inst) + val sj = sjCodecOf[SampleString] + val js = sj.toJson(inst) // The weird '+' here is to break up the unicode so it won't be interpreted and wreck the test. js should matchJson("""{"s1":"something\b\n\f\r\t\""" + """u2606","s2":"","s3":null}""") - // inst shouldEqual ScalaJack.read[SampleString](js) - } - - /* - it("UUID must work") { - val inst = SampleUUID( - null, - UUID.fromString("580afe0d-81c0-458f-9e09-4486c7af0fe9") - ) - val js = ScalaJack.write(inst) - js should matchJson("""{"u1":null,"u2":"580afe0d-81c0-458f-9e09-4486c7af0fe9"}""") - inst shouldEqual ScalaJack.read[SampleUUID](js) + sj.fromJson(js) shouldEqual inst } } @@ -128,164 +128,159 @@ class ScalaPrimSpec() extends AnyFunSpec with JsonMatchers: val js = """{"bd1":123,"bd2":1.23,"bd3":0,"bd4":123.456,"bd5":"0.1499999999999999944488848768742172978818416595458984375","bd6":null}""" val msg: String = - """Float/Double expected but couldn't parse from """" + "\"" + """" at position [50] + """Expected a numerical value or null here at position [50] |{"bd1":123,"bd2":1.23,"bd3":0,"bd4":123.456,"bd5":"0.149999999999999994448884... |--------------------------------------------------^""".stripMargin - val thrown = the[JsonParseError] thrownBy ScalaJack.read[SampleBigDecimal](js) - thrown.getMessage should equal(msg) + val ex = intercept[JsonParseError](sjCodecOf[SampleBigDecimal].fromJson(js)) + ex.show shouldEqual msg } it("BigInt must break") { val js = """{"bi1":"-90182736451928374653345","bi2":90182736451928374653345,"bi3":0,"bi4":null}""" val msg = - """Int/Long expected but couldn't parse from """" + "\"" + """" at position [7] + """Expected a numerical value or null here at position [7] |{"bi1":"-90182736451928374653345","bi2":90182736451928374653345,"bi3":0,"bi4"... |-------^""".stripMargin - val thrown = the[JsonParseError] thrownBy ScalaJack.read[SampleBigInt](js) - thrown.getMessage should equal(msg) + val ex = intercept[JsonParseError](sjCodecOf[SampleBigInt].fromJson(js)) + ex.show shouldEqual msg } it("Boolean must break") { val js = """{"bool1":true,"bool2":"false"}""" - val msg = """Unexpected character '"' where beginning of boolean value expected at position [22] + val sj = sjCodecOf[SampleBoolean] + val msg = """Expected 'true' or 'false' at position [22] |{"bool1":true,"bool2":"false"} |----------------------^""".stripMargin - val thrown = the[JsonParseError] thrownBy ScalaJack.read[SampleBoolean](js) - thrown.getMessage should equal(msg) + val ex = intercept[JsonParseError](sj.fromJson(js)) + ex.show shouldEqual msg val js2 = """{"bool1":true,"bool2":123}""" - val msg2 = """Unexpected character '1' where beginning of boolean value expected at position [22] + val msg2 = """Expected 'true' or 'false' at position [22] |{"bool1":true,"bool2":123} |----------------------^""".stripMargin - val thrown2 = the[JsonParseError] thrownBy ScalaJack.read[SampleBoolean](js2) - thrown2.getMessage should equal(msg2) + val ex2 = intercept[JsonParseError](sj.fromJson(js2)) + ex2.show shouldEqual msg2 val js3 = """{"bool1":true,"bool2":null}""" - val msg3 = """Unexpected character 'n' where beginning of boolean value expected at position [22] + val msg3 = """Expected 'true' or 'false' at position [22] |{"bool1":true,"bool2":null} |----------------------^""".stripMargin - val thrown3 = the[JsonParseError] thrownBy ScalaJack.read[SampleBoolean](js3) - thrown3.getMessage should equal(msg3) + val ex3 = intercept[JsonParseError](sj.fromJson(js3)) + ex3.show shouldEqual msg3 } it("Byte must break") { val js = """{"b1":true,"b2":-128,"b3":0,"b4":64}""" - val msg = """Int/Long expected but couldn't parse from "t" at position [6] + val sj = sjCodecOf[SampleByte] + val msg = """Non-numeric character found when integer value expected at position [6] |{"b1":true,"b2":-128,"b3":0,"b4":64} |------^""".stripMargin - val thrown = the[JsonParseError] thrownBy ScalaJack.read[SampleByte](js) - thrown.getMessage should equal(msg) + val ex = intercept[JsonParseError](sj.fromJson(js)) + ex.show shouldEqual msg val js2 = """{"b1":12,"b2":-128,"b3":0,"b4":null}""" - val msg2 = """Int/Long expected but couldn't parse from "n" at position [31] + val msg2 = """Non-numeric character found when integer value expected at position [31] |{"b1":12,"b2":-128,"b3":0,"b4":null} |-------------------------------^""".stripMargin - val thrown2 = the[JsonParseError] thrownBy ScalaJack.read[SampleByte](js2) - thrown2.getMessage should equal(msg2) + val ex2 = intercept[JsonParseError](sj.fromJson(js2)) + ex2.show shouldEqual msg2 } it("Char must break") { val js = """{"c1":null,"c2":"Y","c3":"Z"}""" - val msg = """Char typed values cannot be null at position [10] + val sj = sjCodecOf[SampleChar] + val msg = """Char value cannot be null at position [6] |{"c1":null,"c2":"Y","c3":"Z"} - |----------^""".stripMargin - val thrown = the[JsonParseError] thrownBy ScalaJack.read[SampleChar](js) - thrown.getMessage should equal(msg) + |------^""".stripMargin + val ex = intercept[JsonParseError](sj.fromJson(js)) + ex.show shouldEqual msg val js2 = """{"c1":"","c2":"Y","c3":"Z"}""" - val msg2 = """Cannot convert value '' into a Char at position [8] + val msg2 = """Char value expected but empty string found in json at position [6] |{"c1":"","c2":"Y","c3":"Z"} - |--------^""".stripMargin - val thrown2 = the[JsonParseError] thrownBy ScalaJack.read[SampleChar](js2) - thrown2.getMessage should equal(msg2) + |------^""".stripMargin + val ex2 = intercept[JsonParseError](sj.fromJson(js2)) + ex2.show shouldEqual msg2 } it("Double must break") { val js = """{"d1":1.79769313486E23157E308,"d2":-1.7976931348623157E308,"d3":0.0,"d4":-123.4567}""" val msg = - """Float/Double expected but couldn't parse from "1.79769313486E23157E308" at position [29] + """Expected ',' or '}' but found E at position [25] |{"d1":1.79769313486E23157E308,"d2":-1.7976931348623157E308,"d3":0.0,"d4":-123... - |------^""".stripMargin - val thrown = the[JsonParseError] thrownBy ScalaJack.read[SampleDouble](js) - thrown.getMessage should equal(msg) + |-------------------------^""".stripMargin + val ex = intercept[JsonParseError](sjCodecOf[SampleDouble].fromJson(js)) + ex.show shouldEqual msg } it("Float must break") { val js = """{"f1":3.4028235E38,"f2":"-3.4028235E38","f3":0.0,"f4":-123.4567}""" val msg = - """Float/Double expected but couldn't parse from """" + "\"" + """" at position [24] + """Malformed Float/Double/BigDecimal at position [24] |{"f1":3.4028235E38,"f2":"-3.4028235E38","f3":0.0,"f4":-123.4567} |------------------------^""".stripMargin - val thrown = the[JsonParseError] thrownBy ScalaJack.read[SampleFloat](js) - thrown.getMessage should equal(msg) + val ex = intercept[JsonParseError](sjCodecOf[SampleFloat].fromJson(js)) + ex.show shouldEqual msg } it("Int must break") { + val sj = sjCodecOf[SampleInt] val js = """{"i1":2147483647,"i2":-2147483648,"i3":"0","i4":123}""" - val msg = """Int/Long expected but couldn't parse from """" + "\"" + """" at position [39] + val msg = """Non-numeric character found when integer value expected at position [39] |{"i1":2147483647,"i2":-2147483648,"i3":"0","i4":123} |---------------------------------------^""".stripMargin - val thrown = the[JsonParseError] thrownBy ScalaJack.read[SampleInt](js) - thrown.getMessage should equal(msg) + val ex = intercept[JsonParseError](sj.fromJson(js)) + ex.show shouldEqual msg val js2 = """{"i1":2147483647,"i2":-2147483648,"i3":2.3,"i4":123}""" - val msg2 = """Comma expected at position [40] + val msg2 = """Decimal digit 'e' or '.' found when integer value expected at position [40] |{"i1":2147483647,"i2":-2147483648,"i3":2.3,"i4":123} |----------------------------------------^""".stripMargin - val thrown2 = the[CommaExpected] thrownBy ScalaJack.read[SampleInt](js2) - thrown2.getMessage should equal(msg2) + val ex2 = intercept[JsonParseError](sj.fromJson(js2)) + ex2.show shouldEqual msg2 } it("Long must break") { + val sj = sjCodecOf[SampleLong] val js = """{"l1":9223372036854775807,"l2":-9223372036854775808,"l3":true,"l4":123}""" val msg = - """Int/Long expected but couldn't parse from "t" at position [57] + """Unexpected character in Int/Long value: t at position [57] |...23372036854775807,"l2":-9223372036854775808,"l3":true,"l4":123} |----------------------------------------------------^""".stripMargin - val thrown = the[JsonParseError] thrownBy ScalaJack.read[SampleLong](js) - thrown.getMessage should equal(msg) + val ex = intercept[JsonParseError](sj.fromJson(js)) + ex.show shouldEqual msg val js2 = """{"l1":9223372036854775807,"l2":-9223372036854775808,"l3":0.3,"l4":123}""" val msg2 = - """Comma expected at position [58] + """Expected ',' or '}' but found . at position [58] |...3372036854775807,"l2":-9223372036854775808,"l3":0.3,"l4":123} |----------------------------------------------------^""".stripMargin - val thrown2 = the[CommaExpected] thrownBy ScalaJack.read[SampleLong](js2) - thrown2.getMessage should equal(msg2) + val ex2 = intercept[JsonParseError](sj.fromJson(js2)) + ex2.show shouldEqual msg2 } it("Short must break") { + val sj = sjCodecOf[SampleShort] val js = """{"s1":32767,"s2":true,"s3":0,"s4":123}""" - val msg = """Int/Long expected but couldn't parse from "t" at position [17] + val msg = """Non-numeric character found when integer value expected at position [17] |{"s1":32767,"s2":true,"s3":0,"s4":123} |-----------------^""".stripMargin - val thrown = the[JsonParseError] thrownBy ScalaJack.read[SampleShort](js) - thrown.getMessage should equal(msg) + val ex = intercept[JsonParseError](sj.fromJson(js)) + ex.show shouldEqual msg val js2 = """{"s1":32767,"s2":3.4,"s3":0,"s4":123}""" - val msg2 = """Comma expected at position [18] + val msg2 = """Decimal digit 'e' or '.' found when integer value expected at position [18] |{"s1":32767,"s2":3.4,"s3":0,"s4":123} |------------------^""".stripMargin - val thrown2 = the[CommaExpected] thrownBy ScalaJack.read[SampleShort](js2) - thrown2.getMessage should equal(msg2) + val ex2 = intercept[JsonParseError](sj.fromJson(js2)) + ex2.show shouldEqual msg2 } it("String must break") { val js = """{"s1":"something","s2":-19,"s3":null}""" - val msg = """Unexpected character '-' where beginning of a string expected at position [23] + val msg = """Expected a String value but got '-' at position [23] |{"s1":"something","s2":-19,"s3":null} |-----------------------^""".stripMargin - val thrown = the[JsonParseError] thrownBy ScalaJack.read[SampleString](js) - thrown.getMessage should equal(msg) - } - - it("UUID must break") { - val js = - """{"u1":"bogus","u2":"580afe0d-81c0-458f-9e09-4486c7af0fe9"}""" - val msg = """Unable to marshal UUID from value 'bogus' at position [13] - |{"u1":"bogus","u2":"580afe0d-81c0-458f-9e09-4486c7af0fe9"} - |-------------^""".stripMargin - val thrown = the[JsonParseError] thrownBy ScalaJack.read[SampleUUID](js) - thrown.getMessage should equal(msg) + val ex = intercept[JsonParseError](sjCodecOf[SampleString].fromJson(js)) + ex.show shouldEqual msg } - */ } } diff --git a/src/test/scala/co.blocke.scalajack/json/primitives/SimpleSpec.scala b/src/test/scala/co.blocke.scalajack/json/primitives/SimpleSpec.scala index 065ef4bc..d47e767d 100644 --- a/src/test/scala/co.blocke.scalajack/json/primitives/SimpleSpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/primitives/SimpleSpec.scala @@ -16,11 +16,12 @@ class SimpleSpec() extends AnyFunSpec with JsonMatchers: describe(colorString("-----------------------\n: Simple Type Tests :\n-----------------------", Console.YELLOW)) { describe(colorString("+++ Positive Tests +++")) { + it("Duration must work") { val inst = SampleDuration(Duration.ZERO, Duration.parse("P2DT3H4M"), null) - val js = sj[SampleDuration].toJson(inst) + val js = sjCodecOf[SampleDuration].toJson(inst) js should matchJson("""{"d1":"PT0S","d2":"PT51H4M","d3":null}""") - // inst shouldEqual ScalaJack.read[SampleDuration](js) + sjCodecOf[SampleDuration].fromJson(js) shouldEqual (inst) } it("Instant must work") { @@ -31,21 +32,9 @@ class SimpleSpec() extends AnyFunSpec with JsonMatchers: Instant.parse("2007-12-03T10:15:30.00Z"), null ) - val js = sj[SampleInstant].toJson(inst) + val js = sjCodecOf[SampleInstant].toJson(inst) js should matchJson("""{"i1":"1970-01-01T00:00:00Z","i2":"+1000000000-12-31T23:59:59.999999999Z","i3":"-1000000000-01-01T00:00:00Z","i4":"2007-12-03T10:15:30Z","i5":null}""") - // inst shouldEqual ScalaJack.read[SampleInstant](js) - } - - it("LocalDate must work") { - val inst = SampleLocalDate( - LocalDate.MAX, - LocalDate.MIN, - LocalDate.parse("2007-12-03"), - null - ) - val js = sj[SampleLocalDate].toJson(inst) - js should matchJson("""{"d1":"+999999999-12-31","d2":"-999999999-01-01","d3":"2007-12-03","d4":null}""") - // inst shouldEqual ScalaJack.read[SampleLocalDate](js) + sjCodecOf[SampleInstant].fromJson(js) shouldEqual (inst) } it("LocalDateTime must work") { @@ -55,9 +44,21 @@ class SimpleSpec() extends AnyFunSpec with JsonMatchers: LocalDateTime.parse("2007-12-03T10:15:30"), null ) - val js = sj[SampleLocalDateTime].toJson(inst) + val js = sjCodecOf[SampleLocalDateTime].toJson(inst) js should matchJson("""{"d1":"+999999999-12-31T23:59:59.999999999","d2":"-999999999-01-01T00:00:00","d3":"2007-12-03T10:15:30","d4":null}""") - // inst shouldEqual ScalaJack.read[SampleLocalDateTime](js) + sjCodecOf[SampleLocalDateTime].fromJson(js) shouldEqual (inst) + } + + it("LocalDate must work") { + val inst = SampleLocalDate( + LocalDate.MAX, + LocalDate.MIN, + LocalDate.parse("2007-12-03"), + null + ) + val js = sjCodecOf[SampleLocalDate].toJson(inst) + js should matchJson("""{"d1":"+999999999-12-31","d2":"-999999999-01-01","d3":"2007-12-03","d4":null}""") + sjCodecOf[SampleLocalDate].fromJson(js) shouldEqual (inst) } it("LocalTime must work") { @@ -69,9 +70,9 @@ class SimpleSpec() extends AnyFunSpec with JsonMatchers: LocalTime.parse("10:15:30"), null ) - val js = sj[SampleLocalTime].toJson(inst) + val js = sjCodecOf[SampleLocalTime].toJson(inst) js should matchJson("""{"d1":"23:59:59.999999999","d2":"00:00:00","d3":"00:00:00","d4":"12:00:00","d5":"10:15:30","d6":null}""") - // inst shouldEqual ScalaJack.read[SampleLocalTime](js) + sjCodecOf[SampleLocalTime].fromJson(js) shouldEqual (inst) } it("MonthDay must work") { @@ -79,9 +80,9 @@ class SimpleSpec() extends AnyFunSpec with JsonMatchers: MonthDay.of(7, 1), null ) - val js = sj[SampleMonthDay].toJson(inst) + val js = sjCodecOf[SampleMonthDay].toJson(inst) js should matchJson("""{"m1":"--07-01","m2":null}""") - // inst shouldEqual ScalaJack.read[SampleMonthDay](js) + sjCodecOf[SampleMonthDay].fromJson(js) shouldEqual (inst) } it("OffsetDateTime must work") { @@ -91,9 +92,9 @@ class SimpleSpec() extends AnyFunSpec with JsonMatchers: OffsetDateTime.parse("2007-12-03T10:15:30+01:00"), null ) - val js = sj[SampleOffsetDateTime].toJson(inst) + val js = sjCodecOf[SampleOffsetDateTime].toJson(inst) js should matchJson("""{"o1":"+999999999-12-31T23:59:59.999999999-18:00","o2":"-999999999-01-01T00:00:00+18:00","o3":"2007-12-03T10:15:30+01:00","o4":null}""") - // inst shouldEqual ScalaJack.read[SampleOffsetDateTime](js) + sjCodecOf[SampleOffsetDateTime].fromJson(js) shouldEqual (inst) } it("OffsetTime must work") { @@ -103,30 +104,30 @@ class SimpleSpec() extends AnyFunSpec with JsonMatchers: OffsetTime.parse("10:15:30+01:00"), null ) - val js = sj[SampleOffsetTime].toJson(inst) + val js = sjCodecOf[SampleOffsetTime].toJson(inst) js should matchJson("""{"o1":"23:59:59.999999999-18:00","o2":"00:00:00+18:00","o3":"10:15:30+01:00","o4":null}""") - // inst shouldEqual ScalaJack.read[SampleOffsetTime](js) + sjCodecOf[SampleOffsetTime].fromJson(js) shouldEqual (inst) } it("Period must work") { val inst = SamplePeriod(Period.ZERO, Period.parse("P1Y2M3D"), null) - val js = sj[SamplePeriod].toJson(inst) + val js = sjCodecOf[SamplePeriod].toJson(inst) js should matchJson("""{"p1":"P0D","p2":"P1Y2M3D","p3":null}""") - // inst shouldEqual ScalaJack.read[SamplePeriod](js) + sjCodecOf[SamplePeriod].fromJson(js) shouldEqual (inst) } it("Year must work") { val inst = SampleYear(Year.of(Year.MAX_VALUE), Year.of(Year.MIN_VALUE), Year.parse("2020"), null) - val js = sj[SampleYear].toJson(inst) + val js = sjCodecOf[SampleYear].toJson(inst) js should matchJson("""{"y1":"999999999","y2":"-999999999","y3":"2020","y4":null}""") - // inst shouldEqual ScalaJack.read[SampleYear](js) + sjCodecOf[SampleYear].fromJson(js) shouldEqual (inst) } it("YearMonth must work") { val inst = SampleYearMonth(YearMonth.of(2020, 7), null) - val js = sj[SampleYearMonth].toJson(inst) + val js = sjCodecOf[SampleYearMonth].toJson(inst) js should matchJson("""{"y1":"2020-07","y2":null}""") - // inst shouldEqual ScalaJack.read[SampleYearMonth](js) + sjCodecOf[SampleYearMonth].fromJson(js) shouldEqual (inst) } it("ZonedDateTime must work") { @@ -134,9 +135,9 @@ class SimpleSpec() extends AnyFunSpec with JsonMatchers: ZonedDateTime.parse("2007-12-03T10:15:30+01:00[Europe/Paris]"), null ) - val js = sj[SampleZonedDateTime].toJson(inst) + val js = sjCodecOf[SampleZonedDateTime].toJson(inst) js should matchJson("""{"o1":"2007-12-03T10:15:30+01:00[Europe/Paris]","o2":null}""") - // inst shouldEqual ScalaJack.read[SampleZonedDateTime](js) + sjCodecOf[SampleZonedDateTime].fromJson(js) shouldEqual (inst) } it("ZonedId must work") { @@ -144,9 +145,9 @@ class SimpleSpec() extends AnyFunSpec with JsonMatchers: ZoneId.of("America/Puerto_Rico"), null ) - val js = sj[SampleZoneId].toJson(inst) + val js = sjCodecOf[SampleZoneId].toJson(inst) js should matchJson("""{"z1":"America/Puerto_Rico","z2":null}""") - // inst shouldEqual ScalaJack.read[SampleZoneId](js) + sjCodecOf[SampleZoneId].fromJson(js) shouldEqual (inst) } it("ZoneOffset must work") { @@ -157,9 +158,9 @@ class SimpleSpec() extends AnyFunSpec with JsonMatchers: null, zoneOffSet ) - val js = sj[SampleZoneOffset].toJson(inst) + val js = sjCodecOf[SampleZoneOffset].toJson(inst) js should matchJson("""{"z1":null,"z2":"+01:00"}""") - // inst shouldEqual ScalaJack.read[SampleZoneOffset](js) + sjCodecOf[SampleZoneOffset].fromJson(js) shouldEqual (inst) } it("Net types URL and URI must work") { @@ -169,9 +170,9 @@ class SimpleSpec() extends AnyFunSpec with JsonMatchers: null, new URI("https://www.foom.com") ) - val js = sj[SampleNet].toJson(inst) - println(js) + val js = sjCodecOf[SampleNet].toJson(inst) js should matchJson("""{"u1":null,"u2":"https://www.foom.com","u3":null,"u4":"https://www.foom.com"}""") + sjCodecOf[SampleNet].fromJson(js) shouldEqual (inst) } it("UUID must work") { @@ -179,192 +180,198 @@ class SimpleSpec() extends AnyFunSpec with JsonMatchers: null, UUID.fromString("580afe0d-81c0-458f-9e09-4486c7af0fe9") ) - val js = sj[SampleUUID].toJson(inst) + val js = sjCodecOf[SampleUUID].toJson(inst) js should matchJson("""{"u1":null,"u2":"580afe0d-81c0-458f-9e09-4486c7af0fe9"}""") - // inst shouldEqual ScalaJack.read[SampleUUID](js) + sjCodecOf[SampleUUID].fromJson(js) shouldEqual (inst) } } - } -/* + describe(colorString("+++ Negative Tests +++")) { + it("Duration must break") { + val js = """{"d1":"PT0S","d2":21,"d3":null}""" + val msg = """Expected a String value but got '2' at position [18] + |{"d1":"PT0S","d2":21,"d3":null} + |------------------^""".stripMargin + val ex = intercept[co.blocke.scalajack.json.JsonParseError](sjCodecOf[SampleDuration].fromJson(js)) + ex.show shouldEqual msg + val js2 = """{"d1":"PT0S","d2":"bogus","d3":null}""" + val msg2 = """Failed to parse Duration from input 'bogus' + |{"d1":"PT0S","d2":"bogus","d3":null} + |------------------------^""".stripMargin + the[java.time.format.DateTimeParseException] thrownBy sjCodecOf[SampleDuration].fromJson(js2) should have message """Text cannot be parsed to a Duration""" + } - test("Duration must break") { - describe("--- Negative Tests ---") + it("Instant must break") { + val js = + """{"i1":"1970-01-01T00:00:00Z","i2":false,"i3":"-1000000000-01-01T00:00:00Z","i4":"2007-12-03T10:15:30Z","i5":null}""" + val msg = + """Expected a String value but got 'f' at position [34] + |{"i1":"1970-01-01T00:00:00Z","i2":false,"i3":"-1000000000-01-01T00:00:00Z","i... + |----------------------------------^""".stripMargin + val ex = intercept[co.blocke.scalajack.json.JsonParseError](sjCodecOf[SampleInstant].fromJson(js)) + ex.show shouldEqual msg - val js = """{"d1":"PT0S","d2":21,"d3":null}""".asInstanceOf[JSON] - val msg = """Expected a String here - |{"d1":"PT0S","d2":21,"d3":null} - |------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleDuration](js) - } - val js2 = """{"d1":"PT0S","d2":"bogus","d3":null}""".asInstanceOf[JSON] - val msg2 = """Failed to parse Duration from input 'bogus' - |{"d1":"PT0S","d2":"bogus","d3":null} - |------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ - sj.read[SampleDuration](js2) - } - } + val js2 = + """{"i1":"1970-01-01T00:00:00Z","i2":"bogus","i3":"-1000000000-01-01T00:00:00Z","i4":"2007-12-03T10:15:30Z","i5":null}""" + the[java.time.format.DateTimeParseException] thrownBy sjCodecOf[SampleInstant].fromJson(js2) should have message """Text 'bogus' could not be parsed at index 0""" + } - test("Instant must break") { - val js = - """{"i1":"1970-01-01T00:00:00Z","i2":false,"i3":"-1000000000-01-01T00:00:00Z","i4":"2007-12-03T10:15:30Z","i5":null}""".asInstanceOf[JSON] - val msg = - """Expected a String here - |{"i1":"1970-01-01T00:00:00Z","i2":false,"i3":"-1000000000-01-01T00:00:00Z","i... - |----------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleInstant](js) - } - val js2 = - """{"i1":"1970-01-01T00:00:00Z","i2":"bogus","i3":"-1000000000-01-01T00:00:00Z","i4":"2007-12-03T10:15:30Z","i5":null}""".asInstanceOf[JSON] - val msg2 = - """Failed to parse Instant from input 'bogus' - |{"i1":"1970-01-01T00:00:00Z","i2":"bogus","i3":"-1000000000-01-01T00:00:00Z",... - |----------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ - sj.read[SampleInstant](js2) - } - } + it("LocalDateTime must break") { + val js = + """{"d1":-1,"d2":"-999999999-01-01T00:00:00","d3":"2007-12-03T10:15:30","d4":null}""" + val msg = + """Expected a String value but got '-' at position [6] + |{"d1":-1,"d2":"-999999999-01-01T00:00:00","d3":"2007-12-03T10:15:30","d4":null} + |------^""".stripMargin + val ex = intercept[co.blocke.scalajack.json.JsonParseError](sjCodecOf[SampleLocalDateTime].fromJson(js)) + ex.show shouldEqual msg + val js2 = + """{"d1":"bogus","d2":"-999999999-01-01T00:00:00","d3":"2007-12-03T10:15:30","d1":null}""" + the[java.time.format.DateTimeParseException] thrownBy sjCodecOf[SampleLocalDateTime].fromJson(js2) should have message """Text 'bogus' could not be parsed at index 0""" + } - test("LocalDateTime must break") { - val js = - """{"d1":-1,"d2":"-999999999-01-01T00:00:00","d3":"2007-12-03T10:15:30","d4":null}""".asInstanceOf[JSON] - val msg = - """Expected a String here - |{"d1":-1,"d2":"-999999999-01-01T00:00:00","d3":"2007-12-03T10:15:30","d4":null} - |------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleLocalDateTime](js) - } - val js2 = - """{"d1":"bogus","d2":"-999999999-01-01T00:00:00","d3":"2007-12-03T10:15:30","d1":null}""".asInstanceOf[JSON] - val msg2 = - """Failed to parse LocalDateTime from input 'bogus' - |{"d1":"bogus","d2":"-999999999-01-01T00:00:00","d3":"2007-12-03T10:15:30","d1... - |------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ - sj.read[SampleLocalDateTime](js2) - } - } + it("LocalDate must break") { + val js = + """{"d1":-1,"d2":"-999999999-01-01","d3":"2007-12-03","d4":null}""" + val msg = """Expected a String value but got '-' at position [6] + |{"d1":-1,"d2":"-999999999-01-01","d3":"2007-12-03","d4":null} + |------^""".stripMargin + val ex = intercept[co.blocke.scalajack.json.JsonParseError](sjCodecOf[SampleLocalDate].fromJson(js)) + ex.show shouldEqual msg + val js2 = + """{"d1":"bogus","d2":"-999999999-01-01","d3":"2007-12-03","d4":null}""" + the[java.time.format.DateTimeParseException] thrownBy sjCodecOf[SampleLocalDate].fromJson(js2) should have message """Text 'bogus' could not be parsed at index 0""" + } - test("LocalDate must break") { - val js = - """{"d1":-1,"d2":"-999999999-01-01","d3":"2007-12-03","d4":null}""".asInstanceOf[JSON] - val msg = """Expected a String here - |{"d1":-1,"d2":"-999999999-01-01","d3":"2007-12-03","d4":null} - |------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleLocalDate](js) - } - val js2 = - """{"d1":"bogus","d2":"-999999999-01-01","d3":"2007-12-03","d4":null}""".asInstanceOf[JSON] - val msg2 = - """Failed to parse LocalDate from input 'bogus' - |{"d1":"bogus","d2":"-999999999-01-01","d3":"2007-12-03","d4":null} - |------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ - sj.read[SampleLocalDate](js2) - } - } + it("LocalTime must break") { + val js = + """{"d1":"23:59:59.999999999","d2":"00:00:00","d3":"00:00:00","d4":"12:00:00","d5":false,"d6":null}""" + val msg = + """Expected a String value but got 'f' at position [80] + |...:"00:00:00","d3":"00:00:00","d4":"12:00:00","d5":false,"d6":null} + |----------------------------------------------------^""".stripMargin + val ex = intercept[co.blocke.scalajack.json.JsonParseError](sjCodecOf[SampleLocalTime].fromJson(js)) + ex.show shouldEqual msg + val js2 = + """{"d1":"23:59:59.999999999","d2":"00:00:00","d3":"00:00:00","d4":"12:00:00","d5":"Bogus","d6":null}""" + the[java.time.format.DateTimeParseException] thrownBy sjCodecOf[SampleLocalTime].fromJson(js2) should have message """Text 'Bogus' could not be parsed at index 0""" + } - test("LocalTime must break") { - val js = - """{"d1":"23:59:59.999999999","d2":"00:00:00","d3":"00:00:00","d4":"12:00:00","d5":false,"d6":null}""".asInstanceOf[JSON] - val msg = - """Expected a String here - |...:"00:00:00","d3":"00:00:00","d4":"12:00:00","d5":false,"d6":null} - |----------------------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleLocalTime](js) - } - val js2 = - """{"d1":"23:59:59.999999999","d2":"00:00:00","d3":"00:00:00","d4":"12:00:00","d5":"Bogus","d6":null}""".asInstanceOf[JSON] - val msg2 = - """Failed to parse LocalTime from input 'Bogus' - |...0:00","d3":"00:00:00","d4":"12:00:00","d5":"Bogus","d6":null} - |----------------------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ - sj.read[SampleLocalTime](js2) - } - } + it("MonthDay must break") { + val js = """{"m1":25,"m2":null}""" + val msg = """Expected a String value but got '2' at position [6] + |{"m1":25,"m2":null} + |------^""".stripMargin + val ex = intercept[co.blocke.scalajack.json.JsonParseError](sjCodecOf[SampleMonthDay].fromJson(js)) + ex.show shouldEqual msg + val js2 = """{"m1":"R-07-01","m2":null}""" + the[java.time.format.DateTimeParseException] thrownBy sjCodecOf[SampleMonthDay].fromJson(js2) should have message """Text 'R-07-01' could not be parsed at index 0""" + } - test("OffsetDateTime must break") { - val js = - """{"o1":"+999999999-12-31T23:59:59.999999999-18:00","o2":2,"o3":"2007-12-03T10:15:30+01:00","o4":null}""".asInstanceOf[JSON] - val msg = - """Expected a String here - |..."+999999999-12-31T23:59:59.999999999-18:00","o2":2,"o3":"2007-12-03T10:15:30... - |----------------------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleOffsetDateTime](js) - } - val js2 = - """{"o1":"+999999999-12-31T23:59:59.999999999-18:00","o2":"-999999999-01T00:00:00+18:00","o3":"2007-12-03T10:15:30+01:00","o4":null}""".asInstanceOf[JSON] - val msg2 = - """Failed to parse OffsetDateTime from input '-999999999-01T00:00:00+18:00' - |...9999999-18:00","o2":"-999999999-01T00:00:00+18:00","o3":"2007-12-03T10:15:30... - |----------------------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ - sj.read[SampleOffsetDateTime](js2) - } - } + it("OffsetDateTime must break") { + val js = + """{"o1":"+999999999-12-31T23:59:59.999999999-18:00","o2":2,"o3":"2007-12-03T10:15:30+01:00","o4":null}""" + val msg = + """Expected a String value but got '2' at position [55] + |..."+999999999-12-31T23:59:59.999999999-18:00","o2":2,"o3":"2007-12-03T10:15:30... + |----------------------------------------------------^""".stripMargin + val ex = intercept[co.blocke.scalajack.json.JsonParseError](sjCodecOf[SampleOffsetDateTime].fromJson(js)) + ex.show shouldEqual msg + val js2 = + """{"o1":"+999999999-12-31T23:59:59.999999999-18:00","o2":"-999999999-01T00:00:00+18:00","o3":"2007-12-03T10:15:30+01:00","o4":null}""" + the[java.time.format.DateTimeParseException] thrownBy sjCodecOf[SampleOffsetDateTime].fromJson(js2) should have message """Text '-999999999-01T00:00:00+18:00' could not be parsed at index 13""" + } - test("OffsetTime must break") { - val js = - """{"o1":"23:59:59.999999999-18:00","o2":false,"o3":"10:15:30+01:00","o4":null}""".asInstanceOf[JSON] - val msg = - """Expected a String here - |{"o1":"23:59:59.999999999-18:00","o2":false,"o3":"10:15:30+01:00","o4":null} - |--------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleOffsetTime](js) - } - val js2 = - """{"o1":"23:59:59.999999999-18:00","o2":"00:00:00:00+18:00","o3":"10:15:30+01:00","o4":null}""".asInstanceOf[JSON] - val msg2 = - """Failed to parse OffsetTime from input '00:00:00:00+18:00' - |...23:59:59.999999999-18:00","o2":"00:00:00:00+18:00","o3":"10:15:30+01:00","o4... - |----------------------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ - sj.read[SampleOffsetTime](js2) - } - } + it("OffsetTime must break") { + val js = + """{"o1":"23:59:59.999999999-18:00","o2":false,"o3":"10:15:30+01:00","o4":null}""" + val msg = + """Expected a String value but got 'f' at position [38] + |{"o1":"23:59:59.999999999-18:00","o2":false,"o3":"10:15:30+01:00","o4":null} + |--------------------------------------^""".stripMargin + val ex = intercept[co.blocke.scalajack.json.JsonParseError](sjCodecOf[SampleOffsetTime].fromJson(js)) + ex.show shouldEqual msg + val js2 = + """{"o1":"23:59:59.999999999-18:00","o2":"00:00:00:00+18:00","o3":"10:15:30+01:00","o4":null}""" + the[java.time.format.DateTimeParseException] thrownBy sjCodecOf[SampleOffsetTime].fromJson(js2) should have message """Text '00:00:00:00+18:00' could not be parsed at index 8""" + } - test("Period must break") { - val js = """{"p1":"P0D","p2":5,"p3":null}""".asInstanceOf[JSON] - val msg = """Expected a String here - |{"p1":"P0D","p2":5,"p3":null} - |-----------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SamplePeriod](js) - } - val js2 = """{"p1":"P0D","p2":"bogus","p3":null}""".asInstanceOf[JSON] - val msg2 = """Failed to parse Period from input 'bogus' - |{"p1":"P0D","p2":"bogus","p3":null} - |-----------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ - sj.read[SamplePeriod](js2) - } - } + it("Period must break") { + val js = """{"p1":"P0D","p2":5,"p3":null}""" + val msg = """Expected a String value but got '5' at position [17] + |{"p1":"P0D","p2":5,"p3":null} + |-----------------^""".stripMargin + val ex = intercept[co.blocke.scalajack.json.JsonParseError](sjCodecOf[SamplePeriod].fromJson(js)) + ex.show shouldEqual msg + val js2 = """{"p1":"P0D","p2":"bogus","p3":null}""" + the[java.time.format.DateTimeParseException] thrownBy sjCodecOf[SamplePeriod].fromJson(js2) should have message """Text cannot be parsed to a Period""" + } - test("ZonedDateTime must break") { - val js = """{"o1":true,"o2":null}""".asInstanceOf[JSON] - val msg = """Expected a String here - |{"o1":true,"o2":null} - |------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleZonedDateTime](js) - } - val js2 = """{"o1":"2007-12-03T10:15:30+01:00 Earth","o2":null}""".asInstanceOf[JSON] - val msg2 = - """Failed to parse ZonedDateTime from input '2007-12-03T10:15:30+01:00 Earth' - |{"o1":"2007-12-03T10:15:30+01:00 Earth","o2":null} - |--------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ - sj.read[SampleZonedDateTime](js2) + it("Year must break") { + val js = """{"y1":"999999999","y2":5,"y3":"2020","y4":null}""" + val msg = """Expected a String value but got '5' at position [23] + |{"y1":"999999999","y2":5,"y3":"2020","y4":null} + |-----------------------^""".stripMargin + val ex = intercept[co.blocke.scalajack.json.JsonParseError](sjCodecOf[SampleYear].fromJson(js)) + ex.show shouldEqual msg + val js2 = """{"y1":"999999999","y2":"bogus","y3":"2020","y4":null}""" + the[java.time.format.DateTimeParseException] thrownBy sjCodecOf[SampleYear].fromJson(js2) should have message """Text 'bogus' could not be parsed at index 0""" + } + + it("YearMonth must break") { + val js = """{"y1":true,"y2":null}""" + val msg = """Expected a String value but got 't' at position [6] + |{"y1":true,"y2":null} + |------^""".stripMargin + val ex = intercept[co.blocke.scalajack.json.JsonParseError](sjCodecOf[SampleYearMonth].fromJson(js)) + ex.show shouldEqual msg + val js2 = """{"y1":"bogus","y2":null}""" + the[java.time.format.DateTimeParseException] thrownBy sjCodecOf[SampleYearMonth].fromJson(js2) should have message """Text 'bogus' could not be parsed at index 0""" + } + + it("ZonedId must break") { + val js = """{"z1":-3,"z2":null}""" + val msg = """Expected a String value but got '-' at position [6] + |{"z1":-3,"z2":null} + |------^""".stripMargin + val ex = intercept[co.blocke.scalajack.json.JsonParseError](sjCodecOf[SampleZoneId].fromJson(js)) + ex.show shouldEqual msg + val js2 = """{"z1":"Mars/Puerto_Rico","z2":null}""" + the[java.time.zone.ZoneRulesException] thrownBy sjCodecOf[SampleZoneId].fromJson(js2) should have message """Unknown time-zone ID: Mars/Puerto_Rico""" + } + + it("ZonedDateTime must break") { + val js = """{"o1":true,"o2":null}""" + val msg = """Expected a String value but got 't' at position [6] + |{"o1":true,"o2":null} + |------^""".stripMargin + val ex = intercept[co.blocke.scalajack.json.JsonParseError](sjCodecOf[SampleZonedDateTime].fromJson(js)) + ex.show shouldEqual msg + val js2 = """{"o1":"2007-12-03T10:15:30+01:00 Earth","o2":null}""" + the[java.time.format.DateTimeParseException] thrownBy sjCodecOf[SampleZonedDateTime].fromJson(js2) should have message """Text '2007-12-03T10:15:30+01:00 Earth' could not be parsed, unparsed text found at index 25""" + } + + it("Net types URL and URI must break") { + val js = """{"u1":null,"u2":13,"u3":null,"u4":"https://www.foom.com"}""" + val msg = """Expected a String value but got '1' at position [16] + |{"u1":null,"u2":13,"u3":null,"u4":"https://www.foom.com"} + |----------------^""".stripMargin + val ex = intercept[co.blocke.scalajack.json.JsonParseError](sjCodecOf[SampleNet].fromJson(js)) + ex.show shouldEqual msg + val js2 = """{"u1":null,"u2":"httpwww.foom.com","u3":null,"u4":"https://www.foom.com"}""" + the[java.net.MalformedURLException] thrownBy sjCodecOf[SampleNet].fromJson(js2) should have message """no protocol: httpwww.foom.com""" + } + + it("UUID must break") { + val js = """{"u1":null,"u2":[1,2,3]}""" + val msg = """Expected a String value but got '[' at position [16] + |{"u1":null,"u2":[1,2,3]} + |----------------^""".stripMargin + val ex = intercept[co.blocke.scalajack.json.JsonParseError](sjCodecOf[SampleUUID].fromJson(js)) + ex.show shouldEqual msg + val js2 = """{"u1":null,"u2":"bogus"}""" + the[java.lang.IllegalArgumentException] thrownBy sjCodecOf[SampleUUID].fromJson(js2) should have message """Invalid UUID string: bogus""" + } } } - */ From db8a60d85339ba955bfab59fa0b1f17617b4d40c Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Wed, 27 Mar 2024 23:40:12 -0500 Subject: [PATCH 51/65] more read cases implement --- benchmark/src/main/scala/co.blocke/Run.scala | 10 +- .../json/JsonCodecMaker.scala | 486 +++++++++++++----- .../json/reading/JsonSource.scala | 17 + .../scala/co.blocke.scalajack/run/Play.scala | 12 +- .../{MapSpec.scalax => MapSpec.scala} | 114 ++-- .../json/collections/Model.scala | 3 + .../json/collections/TupleSpec.scala | 59 +++ .../json/collections/TupleSpec.scalax | 34 -- .../json/misc/EnumSpec.scalax | 54 ++ .../json/misc/LRSpec.scala | 125 +++++ .../co.blocke.scalajack/json/misc/Model.scala | 30 +- .../json/misc/OptionLRTrySpec.scala | 244 --------- .../json/misc/OptionSpec.scala | 73 +++ .../json/misc/TrySpec.scalax | 73 +++ .../json/primitives/Model.scala | 36 +- 15 files changed, 854 insertions(+), 516 deletions(-) rename src/test/scala/co.blocke.scalajack/json/collections/{MapSpec.scalax => MapSpec.scala} (54%) create mode 100644 src/test/scala/co.blocke.scalajack/json/collections/TupleSpec.scala delete mode 100644 src/test/scala/co.blocke.scalajack/json/collections/TupleSpec.scalax create mode 100644 src/test/scala/co.blocke.scalajack/json/misc/EnumSpec.scalax create mode 100644 src/test/scala/co.blocke.scalajack/json/misc/LRSpec.scala delete mode 100644 src/test/scala/co.blocke.scalajack/json/misc/OptionLRTrySpec.scala create mode 100644 src/test/scala/co.blocke.scalajack/json/misc/OptionSpec.scala create mode 100644 src/test/scala/co.blocke.scalajack/json/misc/TrySpec.scalax diff --git a/benchmark/src/main/scala/co.blocke/Run.scala b/benchmark/src/main/scala/co.blocke/Run.scala index f7855dc4..4ad296e3 100644 --- a/benchmark/src/main/scala/co.blocke/Run.scala +++ b/benchmark/src/main/scala/co.blocke/Run.scala @@ -8,10 +8,10 @@ object RunMe extends App: import co.blocke.scalajack.ScalaJack.* import co.blocke.scalajack.* - // given codec: JsonValueCodec[Who.Type] = JsonCodecMaker.make - - // implicit val blah: ScalaJack[co.blocke.scalajack.json.run.Yippy] = sj[co.blocke.scalajack.json.run.Yippy] - - // println( ScalaJack[co.blocke.scalajack.json.run.Yippy].toJson(yippy) ) + //case class MapHolder( m: Map[Pet2, String]) + // val mh = MapHolder( Map(Pet2("Mindy","Frenchie",4)->"a", Pet2("Rosie","Terrier",8)->"b")) + // given codec: JsonValueCodec[Pet2] = JsonCodecMaker.make + // given codec2: JsonValueCodec[MapHolder] = JsonCodecMaker.make + // println(writeToString(mh)) println("\nDone") \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala b/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala index d03d4b3c..59419e1a 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala @@ -104,6 +104,16 @@ object JsonCodecMaker: // --------------------------------------------------------------------------------------------- + def testValidMapKey(testRef: RTypeRef[?]): Boolean = + val isValid = testRef match + case _: PrimitiveRef => true + case _: TimeRef => true + case _: NetRef => true + case a: AliasRef[?] => testValidMapKey(a.unwrappedType) + case _ => false + if !isValid then throw new JsonTypeError(s"For JSON serialization, map keys must be a simple type. ${testRef.name} is too complex.") + isValid + def maybeWrite[T](label: String, aE: Expr[T], ref: RTypeRef[T], out: Expr[JsonOutput], cfg: JsonConfig): Expr[Unit] = val labelE = Expr(label) _maybeWrite[T]( @@ -120,7 +130,7 @@ object JsonCodecMaker: _maybeWrite[V]( '{ $out.maybeComma() - ${ genWriteVal(keyE.asExprOf[k], keyRef.asInstanceOf[RTypeRef[k]], out, true) } + ${ genWriteVal(keyE.asExprOf[k], keyRef.asInstanceOf[RTypeRef[k]], out, false, true) } $out.colon() }, valueE, @@ -402,7 +412,48 @@ object JsonCodecMaker: $out.startObject() $tin.foreach { case (key, value) => ${ - maybeWriteMap[k, v]('{ key }, '{ value }.asExprOf[v], t.elementRef.asInstanceOf[RTypeRef[k]], t.elementRef2.asInstanceOf[RTypeRef[v]], out, cfg) + (t.elementRef, t.elementRef2) match + case (aliasK: AliasRef[?], aliasV: AliasRef[?]) => + aliasK.unwrappedType.refType match + case '[ak] => + aliasV.unwrappedType.refType match + case '[av] => + testValidMapKey(aliasK.unwrappedType) + maybeWriteMap[ak, av]( + '{ key.asInstanceOf[ak] }, + '{ value.asInstanceOf[av] }, + aliasK.unwrappedType.asInstanceOf[RTypeRef[ak]], + aliasV.unwrappedType.asInstanceOf[RTypeRef[av]], + out, + cfg + ) + case (_, aliasV: AliasRef[?]) => + aliasV.unwrappedType.refType match + case '[av] => + testValidMapKey(t.elementRef) + maybeWriteMap[k, av]( + '{ key }.asExprOf[k], + '{ value.asInstanceOf[av] }, + t.elementRef.asInstanceOf[RTypeRef[k]], + aliasV.unwrappedType.asInstanceOf[RTypeRef[av]], + out, + cfg + ) + case (aliasK: AliasRef[?], _) => + aliasK.unwrappedType.refType match + case '[ak] => + testValidMapKey(aliasK.unwrappedType) + maybeWriteMap[ak, v]( + '{ key.asInstanceOf[ak] }, + '{ value }.asExprOf[v], + aliasK.unwrappedType.asInstanceOf[RTypeRef[ak]], + t.elementRef2.asInstanceOf[RTypeRef[v]], + out, + cfg + ) + case (_, _) => + testValidMapKey(t.elementRef) + maybeWriteMap[k, v]('{ key }, '{ value }.asExprOf[v], t.elementRef.asInstanceOf[RTypeRef[k]], t.elementRef2.asInstanceOf[RTypeRef[v]], out, cfg) } } $out.endObject() @@ -656,7 +707,8 @@ object JsonCodecMaker: aE: Expr[T], ref: RTypeRef[T], out: Expr[JsonOutput], - inTuple: Boolean = false + inTuple: Boolean = false, + isMapKey: Boolean = false // only primitive or primitive-equiv types can be Map keys )(using Quotes): Expr[Unit] = val methodKey = MethodKey(ref, false) writeMethodSyms @@ -667,29 +719,69 @@ object JsonCodecMaker: .getOrElse( ref match // First cover all primitive and simple types... - case t: BigDecimalRef => '{ $out.value(${ aE.asExprOf[scala.math.BigDecimal] }) } - case t: BigIntRef => '{ $out.value(${ aE.asExprOf[scala.math.BigInt] }) } - case t: BooleanRef => '{ $out.value(${ aE.asExprOf[Boolean] }) } - case t: ByteRef => '{ $out.value(${ aE.asExprOf[Byte] }) } - case t: CharRef => '{ $out.value(${ aE.asExprOf[Char] }) } - case t: DoubleRef => '{ $out.value(${ aE.asExprOf[Double] }) } - case t: FloatRef => '{ $out.value(${ aE.asExprOf[Float] }) } - case t: IntRef => '{ $out.value(${ aE.asExprOf[Int] }) } - case t: LongRef => '{ $out.value(${ aE.asExprOf[Long] }) } - case t: ShortRef => '{ $out.value(${ aE.asExprOf[Short] }) } - case t: StringRef => '{ $out.valueEscaped(${ aE.asExprOf[String] }) } - - case t: JBigDecimalRef => '{ $out.value(${ aE.asExprOf[java.math.BigDecimal] }) } - case t: JBigIntegerRef => '{ $out.value(${ aE.asExprOf[java.math.BigInteger] }) } - case t: JBooleanRef => '{ $out.value(${ aE.asExprOf[java.lang.Boolean] }) } - case t: JByteRef => '{ $out.value(${ aE.asExprOf[java.lang.Byte] }) } - case t: JCharacterRef => '{ $out.value(${ aE.asExprOf[java.lang.Character] }) } - case t: JDoubleRef => '{ $out.value(${ aE.asExprOf[java.lang.Double] }) } - case t: JFloatRef => '{ $out.value(${ aE.asExprOf[java.lang.Float] }) } - case t: JIntegerRef => '{ $out.value(${ aE.asExprOf[java.lang.Integer] }) } - case t: JLongRef => '{ $out.value(${ aE.asExprOf[java.lang.Long] }) } - case t: JShortRef => '{ $out.value(${ aE.asExprOf[java.lang.Short] }) } - case t: JNumberRef => '{ $out.value(${ aE.asExprOf[java.lang.Number] }) } + case t: BigDecimalRef => + if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[scala.math.BigDecimal] }) } + else '{ $out.value(${ aE.asExprOf[scala.math.BigDecimal] }) } + case t: BigIntRef => + if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[scala.math.BigInt] }) } + else '{ $out.value(${ aE.asExprOf[scala.math.BigInt] }) } + case t: BooleanRef => + if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[Boolean] }) } + else '{ $out.value(${ aE.asExprOf[Boolean] }) } + case t: ByteRef => + if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[Byte] }) } + else '{ $out.value(${ aE.asExprOf[Byte] }) } + case t: CharRef => + '{ $out.value(${ aE.asExprOf[Char] }) } + case t: DoubleRef => + if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[Double] }) } + else '{ $out.value(${ aE.asExprOf[Double] }) } + case t: FloatRef => + if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[Float] }) } + else '{ $out.value(${ aE.asExprOf[Float] }) } + case t: IntRef => + if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[Int] }) } + else '{ $out.value(${ aE.asExprOf[Int] }) } + case t: LongRef => + if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[Long] }) } + else '{ $out.value(${ aE.asExprOf[Long] }) } + case t: ShortRef => + if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[Short] }) } + else '{ $out.value(${ aE.asExprOf[Short] }) } + case t: StringRef => '{ $out.valueEscaped(${ aE.asExprOf[String] }) } + + case t: JBigDecimalRef => + if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[java.math.BigDecimal] }) } + else '{ $out.value(${ aE.asExprOf[java.math.BigDecimal] }) } + case t: JBigIntegerRef => + if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[java.math.BigInteger] }) } + else '{ $out.value(${ aE.asExprOf[java.math.BigInteger] }) } + case t: JBooleanRef => + if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[java.lang.Boolean] }) } + else '{ $out.value(${ aE.asExprOf[java.lang.Boolean] }) } + case t: JByteRef => + if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[java.lang.Byte] }) } + else '{ $out.value(${ aE.asExprOf[java.lang.Byte] }) } + case t: JCharacterRef => + '{ $out.value(${ aE.asExprOf[java.lang.Character] }) } + case t: JDoubleRef => + if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[java.lang.Double] }) } + else '{ $out.value(${ aE.asExprOf[java.lang.Double] }) } + case t: JFloatRef => + if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[java.lang.Float] }) } + else '{ $out.value(${ aE.asExprOf[java.lang.Float] }) } + case t: JIntegerRef => + if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[java.lang.Integer] }) } + else '{ $out.value(${ aE.asExprOf[java.lang.Integer] }) } + case t: JLongRef => + if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[java.lang.Long] }) } + else '{ $out.value(${ aE.asExprOf[java.lang.Long] }) } + case t: JShortRef => + if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[java.lang.Short] }) } + else '{ $out.value(${ aE.asExprOf[java.lang.Short] }) } + case t: JNumberRef => + if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[java.lang.Number] }) } + else '{ $out.value(${ aE.asExprOf[java.lang.Number] }) } case t: DurationRef => '{ $out.value(${ aE.asExprOf[java.time.Duration] }) } case t: InstantRef => '{ $out.value(${ aE.asExprOf[java.time.Instant] }) } @@ -728,7 +820,9 @@ object JsonCodecMaker: case Some(list) if list.contains(t.name) => true case _ => false val rtype = t.expr - if enumAsId then '{ $out.value($rtype.asInstanceOf[EnumRType[_]].ordinal($aE.toString).get) } + if enumAsId then + if isMapKey then '{ $out.value($rtype.asInstanceOf[EnumRType[_]].ordinal($aE.toString).get.toString) } // stringified id + else '{ $out.value($rtype.asInstanceOf[EnumRType[_]].ordinal($aE.toString).get) } // int value of id else '{ $out.value($aE.toString) } // NeoType is a bit of a puzzle-box. To get the correct underlying base type, I had to dig into @@ -868,7 +962,8 @@ object JsonCodecMaker: // default: Expr[T], // needed? This should already be in ref... ref: RTypeRef[T], in: Expr[JsonSource], - inTuple: Boolean = false // not sure if needed... + inTuple: Boolean = false, // not sure if needed... + isMapKey: Boolean = false )(using Quotes): Expr[T] = val methodKey = MethodKey(ref, false) readMethodSyms @@ -880,21 +975,37 @@ object JsonCodecMaker: ref match // First cover all primitive and simple types... case t: BigDecimalRef => - '{ - $in.expectNumberOrNull() match - case null => null - case s => scala.math.BigDecimal(s) - }.asExprOf[T] + if isMapKey then + '{ + $in.expectString() match + case null => null + case s => scala.math.BigDecimal(s) + }.asExprOf[T] + else + '{ + $in.expectNumberOrNull() match + case null => null + case s => scala.math.BigDecimal(s) + }.asExprOf[T] case t: BigIntRef => - '{ - $in.expectNumberOrNull() match - case null => null - case s => scala.math.BigInt(s) - }.asExprOf[T] + if isMapKey then + '{ + $in.expectString() match + case null => null + case s => scala.math.BigInt(s) + }.asExprOf[T] + else + '{ + $in.expectNumberOrNull() match + case null => null + case s => scala.math.BigInt(s) + }.asExprOf[T] case t: BooleanRef => - '{ $in.expectBoolean() }.asExprOf[T] + if isMapKey then '{ $in.expectString().toBoolean }.asExprOf[T] + else '{ $in.expectBoolean() }.asExprOf[T] case t: ByteRef => - '{ $in.expectInt().toByte }.asExprOf[T] + if isMapKey then '{ $in.expectString().toInt.toByte }.asExprOf[T] + else '{ $in.expectInt().toByte }.asExprOf[T] case t: CharRef => '{ $in.expectString() match @@ -911,38 +1022,65 @@ object JsonCodecMaker: case c => c.charAt(0) }.asExprOf[T] case t: DoubleRef => - '{ $in.expectDouble() }.asExprOf[T] + if isMapKey then '{ $in.expectString().toDouble }.asExprOf[T] + else '{ $in.expectDouble() }.asExprOf[T] case t: FloatRef => - '{ $in.expectFloat() }.asExprOf[T] + if isMapKey then '{ $in.expectString().toFloat }.asExprOf[T] + else '{ $in.expectFloat() }.asExprOf[T] case t: IntRef => - '{ $in.expectInt() }.asExprOf[T] + if isMapKey then '{ $in.expectString().toInt }.asExprOf[T] + else '{ $in.expectInt() }.asExprOf[T] case t: LongRef => - '{ $in.expectLong() }.asExprOf[T] + if isMapKey then '{ $in.expectString().toLong }.asExprOf[T] + else '{ $in.expectLong() }.asExprOf[T] case t: ShortRef => - '{ $in.expectInt().toShort }.asExprOf[T] + if isMapKey then '{ $in.expectString().toShort }.asExprOf[T] + else '{ $in.expectInt().toShort }.asExprOf[T] case t: StringRef => '{ $in.expectString() }.asExprOf[T] case t: JBigDecimalRef => - '{ - $in.expectNumberOrNull() match - case null => null - case n => new java.math.BigDecimal(n) - }.asExprOf[T] + if isMapKey then + '{ + $in.expectString() match + case null => null + case n => new java.math.BigDecimal(n) + }.asExprOf[T] + else + '{ + $in.expectNumberOrNull() match + case null => null + case n => new java.math.BigDecimal(n) + }.asExprOf[T] case t: JBigIntegerRef => - '{ - $in.expectNumberOrNull() match - case null => null - case n => new java.math.BigInteger(n) - }.asExprOf[T] + if isMapKey then + '{ + $in.expectString() match + case null => null + case n => new java.math.BigInteger(n) + }.asExprOf[T] + else + '{ + $in.expectNumberOrNull() match + case null => null + case n => new java.math.BigInteger(n) + }.asExprOf[T] case t: JBooleanRef => - '{ $in.expectJavaBoolean() }.asExprOf[T] + if isMapKey then '{ java.lang.Boolean.valueOf($in.expectString()) }.asExprOf[T] + else '{ $in.expectJavaBoolean() }.asExprOf[T] case t: JByteRef => - '{ - $in.expectNumberOrNull() match - case null => null - case n => java.lang.Byte.valueOf(n) - }.asExprOf[T] + if isMapKey then + '{ + $in.expectString() match + case null => null + case n => java.lang.Byte.valueOf(n) + }.asExprOf[T] + else + '{ + $in.expectNumberOrNull() match + case null => null + case n => java.lang.Byte.valueOf(n) + }.asExprOf[T] case t: JCharacterRef => '{ val c = $in.expectString() @@ -954,50 +1092,101 @@ object JsonCodecMaker: else java.lang.Character.valueOf(c.charAt(0)) }.asExprOf[T] case t: JDoubleRef => - '{ - $in.expectNumberOrNull() match - case null => null - case n => java.lang.Double.valueOf(n) - }.asExprOf[T] + if isMapKey then + '{ + $in.expectString() match + case null => null + case n => java.lang.Double.valueOf(n) + }.asExprOf[T] + else + '{ + $in.expectNumberOrNull() match + case null => null + case n => java.lang.Double.valueOf(n) + }.asExprOf[T] case t: JFloatRef => - '{ - $in.expectNumberOrNull() match - case null => null - case n => java.lang.Float.valueOf(n) - }.asExprOf[T] + if isMapKey then + '{ + $in.expectString() match + case null => null + case n => java.lang.Float.valueOf(n) + }.asExprOf[T] + else + '{ + $in.expectNumberOrNull() match + case null => null + case n => java.lang.Float.valueOf(n) + }.asExprOf[T] case t: JIntegerRef => - '{ - $in.expectNumberOrNull() match - case null => null - case n => java.lang.Integer.valueOf(n) - }.asExprOf[T] + if isMapKey then + '{ + $in.expectString() match + case null => null + case n => java.lang.Integer.valueOf(n) + }.asExprOf[T] + else + '{ + $in.expectNumberOrNull() match + case null => null + case n => java.lang.Integer.valueOf(n) + }.asExprOf[T] case t: JLongRef => - '{ - $in.expectNumberOrNull() match - case null => null - case n => java.lang.Long.valueOf(n) - }.asExprOf[T] + if isMapKey then + '{ + $in.expectString() match + case null => null + case n => java.lang.Long.valueOf(n) + }.asExprOf[T] + else + '{ + $in.expectNumberOrNull() match + case null => null + case n => java.lang.Long.valueOf(n) + }.asExprOf[T] case t: JShortRef => - '{ - $in.expectNumberOrNull() match - case null => null - case n => java.lang.Short.valueOf(n) - }.asExprOf[T] + if isMapKey then + '{ + $in.expectString() match + case null => null + case n => java.lang.Short.valueOf(n) + }.asExprOf[T] + else + '{ + $in.expectNumberOrNull() match + case null => null + case n => java.lang.Short.valueOf(n) + }.asExprOf[T] case t: JNumberRef => - '{ - $in.expectNumberOrNull() match - case null => null - case n => - scala.math.BigDecimal(n) match { - case d if d.isValidByte => java.lang.Byte.valueOf(d.toByteExact) - case d if d.isValidShort => java.lang.Short.valueOf(d.toShortExact) - case d if d.isValidInt => java.lang.Integer.valueOf(d.toIntExact) - case d if d.isValidLong => java.lang.Long.valueOf(d.toLongExact) - case d if d.isDecimalFloat => java.lang.Float.valueOf(d.toFloat) - case d if d.isDecimalDouble => java.lang.Double.valueOf(d.toDouble) - case d => d - } - }.asExprOf[T] + if isMapKey then + '{ + $in.expectString() match + case null => null + case n => + scala.math.BigDecimal(n) match { + case d if d.isValidByte => java.lang.Byte.valueOf(d.toByteExact) + case d if d.isValidShort => java.lang.Short.valueOf(d.toShortExact) + case d if d.isValidInt => java.lang.Integer.valueOf(d.toIntExact) + case d if d.isValidLong => java.lang.Long.valueOf(d.toLongExact) + case d if d.isDecimalFloat => java.lang.Float.valueOf(d.toFloat) + case d if d.isDecimalDouble => java.lang.Double.valueOf(d.toDouble) + case d => d + } + }.asExprOf[T] + else + '{ + $in.expectNumberOrNull() match + case null => null + case n => + scala.math.BigDecimal(n) match { + case d if d.isValidByte => java.lang.Byte.valueOf(d.toByteExact) + case d if d.isValidShort => java.lang.Short.valueOf(d.toShortExact) + case d if d.isValidInt => java.lang.Integer.valueOf(d.toIntExact) + case d if d.isValidLong => java.lang.Long.valueOf(d.toLongExact) + case d if d.isDecimalFloat => java.lang.Float.valueOf(d.toFloat) + case d if d.isDecimalDouble => java.lang.Double.valueOf(d.toDouble) + case d => d + } + }.asExprOf[T] case t: DurationRef => '{ $in.expectString(java.time.Duration.parse) }.asExprOf[T] case t: InstantRef => '{ $in.expectString(java.time.Instant.parse) }.asExprOf[T] @@ -1029,11 +1218,16 @@ object JsonCodecMaker: else t.unwrappedType.refType match case '[e] => - genReadVal[e]( - t.unwrappedType.asInstanceOf[RTypeRef[e]], - in, - inTuple - ).asExprOf[T] + '{ + ${ + genReadVal[e]( + t.unwrappedType.asInstanceOf[RTypeRef[e]], + in, + inTuple, + isMapKey + ) + }.asInstanceOf[T] + } // -------------------- // Options... @@ -1069,30 +1263,25 @@ object JsonCodecMaker: $in.retract() throw JsonParseError("Failed to read either side of Either type", $in) }.asExprOf[T] - /* - Either: - def read(parser: Parser): Either[L, R] = { - val savedReader = parser.mark() - if (parser.peekForNull) - null - else - Try(rightTypeAdapter.read(parser)) match { - case Success(rightValue) => - Right(rightValue.asInstanceOf[R]) - case Failure(_) => // Right parse failed... try left - parser.revertToMark(savedReader) - Try(leftTypeAdapter.read(parser)) match { - case Success(leftValue) => - Left(leftValue.asInstanceOf[L]) - case Failure(x) => - parser.backspace() - throw new ScalaJackError( - parser.showError(s"Failed to read either side of Either") - ) - } - } - } + case t: LeftRightRef[?] if t.lrkind == LRKind.UNION => + import quotes.reflect.* + t.leftRef.refType match + case '[l] => + t.rightRef.refType match + case '[r] => + '{ + scala.util.Try(${ genReadVal[l](t.leftRef.asInstanceOf[RTypeRef[l]], in, true) }) match + case Success(lval) => lval + case Failure(f) => + scala.util.Try(${ genReadVal[r](t.rightRef.asInstanceOf[RTypeRef[r]], in, true) }) match + case Success(rval) => rval + case Failure(_) => + $in.retract() + throw JsonParseError("Failed to read either side of Union type", $in) + }.asExprOf[T] + /* + TODO Intersection: val syntheticTA = taCache.typeAdapterOf[L] syntheticTA.write(t.asInstanceOf[L], writer, out) @@ -1248,6 +1437,24 @@ syntheticTA.write(t.asInstanceOf[L], writer, out) else null }.asExprOf[T] + case t: MapRef[?] => + t.elementRef.refType match + case '[k] => + t.elementRef2.refType match + case '[v] => + testValidMapKey(t.elementRef) + '{ + if $in.expectNull() then null + else + $in.expectToken('{') + $in.parseMap[k, v]( + () => ${ genReadVal[k](t.elementRef.asInstanceOf[RTypeRef[k]], in, inTuple, true) }, + () => ${ genReadVal[v](t.elementRef2.asInstanceOf[RTypeRef[v]], in, inTuple) }, + Map.empty[k, v], + true + ) + }.asExprOf[T] + // -------------------- // Tuples... // -------------------- @@ -1275,24 +1482,21 @@ syntheticTA.write(t.asInstanceOf[L], writer, out) tpart.refType match case '[e] => if i == 0 then genReadVal[e](tpart.asInstanceOf[RTypeRef[e]], in, true).asTerm - else if i < t.tupleRefs.length - 1 then - '{ - $in.expectToken(',') - ${ genReadVal[e](tpart.asInstanceOf[RTypeRef[e]], in, true) } - }.asTerm else '{ $in.expectToken(',') - val res = ${ genReadVal[e](tpart.asInstanceOf[RTypeRef[e]], in, true) } - $in.expectToken(']') - res + ${ genReadVal[e](tpart.asInstanceOf[RTypeRef[e]], in, true) } }.asTerm } '{ - $in.expectToken('[') - ${ - Apply(TypeApply(Select.unique(New(Inferred(tpe)), ""), indexedTypes.map(x => Inferred(x))), tupleTerms).asExpr - } + if $in.expectNull() then null + else + $in.expectToken('[') + val tv: T = ${ + Apply(TypeApply(Select.unique(New(Inferred(tpe)), ""), indexedTypes.map(x => Inferred(x))), tupleTerms).asExprOf[T] + } + $in.expectToken(']') + tv }.asExprOf[T] case _ => @@ -1328,5 +1532,5 @@ syntheticTA.write(t.asInstanceOf[L], writer, out) // others here??? Refer to Jsoniter file JsonCodecMaker.scala classFieldMatrixValDefs ++ writeMethodDefs ++ readMethodDefs val codec = Block(neededDefs.toList, codecDef).asExprOf[JsonCodec[T]] - println(s"Codec: ${codec.show}") + // println(s"Codec: ${codec.show}") codec diff --git a/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala b/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala index 632335b9..92ed70de 100644 --- a/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala +++ b/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala @@ -126,6 +126,23 @@ case class JsonSource(js: CharSequence): if readToken() != ':' then throw new JsonParseError(s"Expected ':' field separator but found $here", this) fieldNameMatrix.first(bs) + @tailrec + final def parseMap[K, V](kf: () => K, vf: () => V, acc: Map[K, V], isFirst: Boolean = true): Map[K, V] = // initial '{' already consumed + readToken() match + case '}' => acc + case ',' => + val key = kf() + expectToken(':') + val value = vf() + parseMap[K, V](kf, vf, acc + (key -> value), false) + case _ if isFirst => + retract() + val key = kf() + expectToken(':') + val value = vf() + parseMap[K, V](kf, vf, acc + (key -> value), false) + case c => throw JsonParseError(s"Expected either object end '}' or field separator ',' here but got '$c'", this) + // Array and Tuple... // ======================================================= diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index aabb0c2a..07a0adb7 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -74,11 +74,11 @@ object RunMe extends App: // val c: Pizza = ScalaJack[Pizza].fromJson("\"READY\"") // println("Pizza: " + c) - // case class Group(t: (Int, String, Boolean)) - implicit val blah: ScalaJack[Group] = sjCodecOf[Group] - val g = Group((5, "Greg", true)) - val js = ScalaJack[Group].toJson(g) - println(js) - println(ScalaJack[Group].fromJson(js)) + opaque type OnOff = Boolean + opaque type Counter = Short + + val m: Map[OnOff, Counter] = Map(true.asInstanceOf[OnOff] -> 1.asInstanceOf[Counter]) + val n: Map[Boolean, Short] = m.asInstanceOf[Map[Boolean, Short]] + println(n) println("done.") diff --git a/src/test/scala/co.blocke.scalajack/json/collections/MapSpec.scalax b/src/test/scala/co.blocke.scalajack/json/collections/MapSpec.scala similarity index 54% rename from src/test/scala/co.blocke.scalajack/json/collections/MapSpec.scalax rename to src/test/scala/co.blocke.scalajack/json/collections/MapSpec.scala index 0119ae8a..f02d316a 100644 --- a/src/test/scala/co.blocke.scalajack/json/collections/MapSpec.scalax +++ b/src/test/scala/co.blocke.scalajack/json/collections/MapSpec.scala @@ -17,70 +17,103 @@ class MapSpec() extends AnyFunSpec with JsonMatchers: describe(colorString("+++ Positive Tests +++")) { it("Map is null must work") { val inst = MapHolder[Int, Int](null) - val js = sj[MapHolder[Int, Int]].toJson(inst) + val sj = sjCodecOf[MapHolder[Int, Int]] + val js = sj.toJson(inst) js should matchJson("""{"a":null}""") + sj.fromJson(js) shouldEqual(inst) } it("Map key of string must work") { val inst = MapHolder[String, Int](Map("x" -> 1, "y" -> 2)) - val js = sj[MapHolder[String, Int]].toJson(inst) + val sj = sjCodecOf[MapHolder[String, Int]] + val js = sj.toJson(inst) js should matchJson("""{"a":{"x":1,"y":2}}""") + sj.fromJson(js) shouldEqual(inst) } it("Map key of long must work") { val inst = MapHolder[Long, Int](Map(15L -> 1, 25L -> 2)) - val js = sj[MapHolder[Long, Int]].toJson(inst) + val sj = sjCodecOf[MapHolder[Long, Int]] + val js = sj.toJson(inst) js should matchJson("""{"a":{"15":1,"25":2}}""") + sj.fromJson(js) shouldEqual(inst) } it("Map key of boolean must work") { val inst = MapHolder[Boolean, Int](Map(true -> 1, false -> 2)) - val js = sj[MapHolder[Boolean, Int]].toJson(inst) + val sj = sjCodecOf[MapHolder[Boolean, Int]] + val js = sj.toJson(inst) js should matchJson("""{"a":{"true":1,"false":2}}""") + sj.fromJson(js) shouldEqual(inst) } it("Map key of uuid must work") { val inst = MapHolder[UUID, String](Map(UUID.fromString("1b9ab03f-26a3-4ec5-a8dd-d5122ff86b03") -> "x", UUID.fromString("09abdeb1-8b07-4683-8f97-1f5621696008") -> "y")) - val js = sj[MapHolder[UUID, String]].toJson(inst) + val sj = sjCodecOf[MapHolder[UUID, String]] + val js = sj.toJson(inst) js should matchJson("""{"a":{"1b9ab03f-26a3-4ec5-a8dd-d5122ff86b03":"x","09abdeb1-8b07-4683-8f97-1f5621696008":"y"}}""") + sj.fromJson(js) shouldEqual(inst) } - it("Map value of string must work") { val inst = MapHolder[String, String](Map("w" -> "x", "y" -> "z")) - val js = sj[MapHolder[String, String]].toJson(inst) + val sj = sjCodecOf[MapHolder[String, String]] + val js = sj.toJson(inst) js should matchJson("""{"a":{"w":"x","y":"z"}}""") + sj.fromJson(js) shouldEqual(inst) } it("Map value of long must work") { val inst = MapHolder[String, Long](Map("w" -> 3L, "y" -> 4L)) - val js = sj[MapHolder[String, Long]].toJson(inst) + val sj = sjCodecOf[MapHolder[String, Long]] + val js = sj.toJson(inst) js should matchJson("""{"a":{"w":3,"y":4}}""") + sj.fromJson(js) shouldEqual(inst) } it("Map value of boolean must work") { val inst = MapHolder[String, Boolean](Map("w" -> true, "y" -> false)) - val js = sj[MapHolder[String, Boolean]].toJson(inst) + val sj = sjCodecOf[MapHolder[String, Boolean]] + val js = sj.toJson(inst) js should matchJson("""{"a":{"w":true,"y":false}}""") + sj.fromJson(js) shouldEqual(inst) + } + it("Map key and value of opaque alias type must work") { + val inst = MapHolder[OnOff, Counter](Map(true.asInstanceOf[OnOff] -> 1.asInstanceOf[Counter], false.asInstanceOf[OnOff] -> 0.asInstanceOf[Counter])) + val sj = sjCodecOf[MapHolder[OnOff, Counter]] + val js = sj.toJson(inst) + js should matchJson("""{"a":{"true":1,"false":0}}""") + sj.fromJson(js) shouldEqual(inst) } it("Map value of uuid must work") { val inst = MapHolder[String, UUID](Map("x" -> UUID.fromString("1b9ab03f-26a3-4ec5-a8dd-d5122ff86b03"), "y" -> UUID.fromString("09abdeb1-8b07-4683-8f97-1f5621696008"))) - val js = sj[MapHolder[String, UUID]].toJson(inst) + val sj = sjCodecOf[MapHolder[String, UUID]] + val js = sj.toJson(inst) js should matchJson("""{"a":{"x":"1b9ab03f-26a3-4ec5-a8dd-d5122ff86b03","y":"09abdeb1-8b07-4683-8f97-1f5621696008"}}""") + sj.fromJson(js) shouldEqual(inst) } it("Map value of Seq must work") { val inst = MapHolder[String, List[Int]](Map("w" -> List(1, 2), "y" -> List(3, 4))) - val js = sj[MapHolder[String, List[Int]]].toJson(inst) + val sj = sjCodecOf[MapHolder[String, List[Int]]] + val js = sj.toJson(inst) js should matchJson("""{"a":{"w":[1,2],"y":[3,4]}}""") + sj.fromJson(js) shouldEqual(inst) } it("Map value of Map (nested) must work") { val inst = MapHolder[String, Map[String, Int]](Map("w" -> Map("r" -> 3, "t" -> 4), "y" -> Map("s" -> 7, "q" -> 9))) - val js = sj[MapHolder[String, Map[String, Int]]].toJson(inst) + val sj = sjCodecOf[MapHolder[String, Map[String, Int]]] + val js = sj.toJson(inst) js should matchJson("""{"a":{"w":{"r":3,"t":4},"y":{"s":7,"q":9}}}""") + sj.fromJson(js) shouldEqual(inst) } + it("Map value of union type must work") { + val inst = MapHolder[String, Int | List[String]](Map("w" -> 3, "y" -> List("wow", "blah"))) + val sj = sjCodecOf[MapHolder[String, Int | List[String]]] + val js = sj.toJson(inst) + js should matchJson("""{"a":{"w":3,"y":["wow","blah"]}}""") + sj.fromJson(js) shouldEqual(inst) + } + /* + * Keys of value class (Distance) must work + it("Map value of class must work") { val inst = MapHolder[String, Person](Map("w" -> Person("Bob", 34), "y" -> Person("Sally", 25))) val js = sj[MapHolder[String, Person]].toJson(inst) js should matchJson("""{"a":{"w":{"name":"Bob","age":34},"y":{"name":"Sally","age":25}}}""") } - it("Map value of union type must work") { - val inst = MapHolder[String, Int | List[String]](Map("w" -> 3, "y" -> List("wow", "blah"))) - val js = sj[MapHolder[String, Int | List[String]]].toJson(inst) - js should matchJson("""{"a":{"w":3,"y":["wow","blah"]}}""") - } it("Map value of value class must work") { val inst = MapHolder[String, Distance](Map("w" -> new Distance(1.23), "y" -> Distance(4.56))) val js = sj[MapHolder[String, Distance]].toJson(inst) @@ -88,47 +121,14 @@ class MapSpec() extends AnyFunSpec with JsonMatchers: } it("Mutable Map value must work") { val inst = MMapHolder[String, Distance](scala.collection.mutable.HashMap("w" -> new Distance(1.23), "y" -> Distance(4.56))) - val js = sj[MMapHolder[String, Distance]].toJson(inst) + val sj = sjCodecOf[MapHolder[String, Distance]] + val js = sj.toJson(inst) js should matchJson("""{"a":{"w":1.23,"y":4.56}}""") + sj.fromJson(js) shouldEqual(inst) } - - it("Enum as Map key and value must work") { - val inst = MapHolder[Color, Color](Map(Color.Red -> Color.Blue, Color.Green -> Color.Red)) - val js = sj[MapHolder[Color, Color]].toJson(inst) - js should matchJson("""{"a":{"Red":"Blue","Green":"Red"}}""") - } - it("Enum as Map key and value must work (using id)") { - val inst = MapHolder[Color, Color](Map(Color.Red -> Color.Blue, Color.Green -> Color.Red)) - val js = sj[MapHolder[Color, Color]](JsonConfig.withEnumsAsIds(Some(Nil))).toJson(inst) - js should matchJson("""{"a":{"0":2,"1":0}}""") - } - it("Enumeration as Map key and value must work") { - import Permissions.* - val inst = MapHolder[Permissions, Permissions](Map(Permissions.READ -> Permissions.WRITE, Permissions.EXEC -> Permissions.NONE)) - val js = sj[MapHolder[Permissions, Permissions]].toJson(inst) - js should matchJson("""{"a":{"READ":"WRITE","EXEC":"NONE"}}""") - } - it("Enumeration as Map key and value must work (using id)") { - import Permissions.* - val inst = MapHolder[Permissions, Permissions](Map(Permissions.READ -> Permissions.WRITE, Permissions.EXEC -> Permissions.NONE)) - val js = sj[MapHolder[Permissions, Permissions]](JsonConfig.withEnumsAsIds(Some(Nil))).toJson(inst) - js should matchJson("""{"a":{"0":1,"2":3}}""") - } - it("Java Enumeration as Map key and value must work") { - val inst = MapHolder[CarEnum, CarEnum](Map(CarEnum.VW -> CarEnum.PORSCHE, CarEnum.PORSCHE -> CarEnum.TOYOTA)) - val js = sj[MapHolder[CarEnum, CarEnum]].toJson(inst) - js should matchJson("""{"a":{"VW":"PORSCHE","PORSCHE":"TOYOTA"}}""") - } - it("Java Enumeration as Map key and value must work (using id)") { - val inst = MapHolder[CarEnum, CarEnum](Map(CarEnum.VW -> CarEnum.PORSCHE, CarEnum.PORSCHE -> CarEnum.TOYOTA)) - val js = sj[MapHolder[CarEnum, CarEnum]](JsonConfig.withEnumsAsIds(Some(Nil))).toJson(inst) - js should matchJson("""{"a":{"1":2,"2":0}}""") - } - it("Enum/Enumeration mix of enum as value must work") { - import Permissions.* - val inst = MapHolder[Color, Permissions](Map(Color.Red -> Permissions.WRITE, Color.Blue -> Permissions.NONE)) - val js = sj[MapHolder[Color, Permissions]](JsonConfig.withEnumsAsIds(Some(List("co.blocke.scalajack.json.collections.Color")))).toJson(inst) - js should matchJson("""{"a":{"0":"WRITE","2":"NONE"}}""") - } + */ } + + // describe(colorString("--- Negative Tests ---")) { + // } } diff --git a/src/test/scala/co.blocke.scalajack/json/collections/Model.scala b/src/test/scala/co.blocke.scalajack/json/collections/Model.scala index ed1d450e..79ea22b0 100644 --- a/src/test/scala/co.blocke.scalajack/json/collections/Model.scala +++ b/src/test/scala/co.blocke.scalajack/json/collections/Model.scala @@ -6,6 +6,9 @@ import java.util.{ArrayList, Map as JMap, Set as JSet} case class Person(name: String, age: Int) +opaque type OnOff = Boolean +opaque type Counter = Short + case class SeqHolder[T](a: Seq[T]) case class SetHolder[T](a: Set[T]) case class ArrayHolder[T](a: Array[T]) diff --git a/src/test/scala/co.blocke.scalajack/json/collections/TupleSpec.scala b/src/test/scala/co.blocke.scalajack/json/collections/TupleSpec.scala new file mode 100644 index 00000000..25017c8d --- /dev/null +++ b/src/test/scala/co.blocke.scalajack/json/collections/TupleSpec.scala @@ -0,0 +1,59 @@ +package co.blocke.scalajack +package json +package collections + +import ScalaJack.* +import co.blocke.scala_reflection.* +import org.scalatest.funspec.AnyFunSpec +import org.scalatest.matchers.should.Matchers.* +import org.scalatest.* +import TestUtil.* + +import java.util.UUID + +class TupleSpec() extends AnyFunSpec with JsonMatchers: + + describe(colorString("-------------------------------\n: Tuple Tests :\n-------------------------------", Console.YELLOW)) { + describe(colorString("+++ Positive Tests +++")) { + it("Tuple is null must work") { + val inst = TupleHolder[Int, String, Boolean](null) + val sj = sjCodecOf[TupleHolder[Int, String, Boolean]] + val js = sj.toJson(inst) + js should matchJson("""{"a":null}""") + sj.fromJson(js) shouldEqual inst + } + it("Tuple of simple types must work") { + val inst = TupleHolder[Int, String, Boolean]((15, "wow", true)) + val sj = sjCodecOf[TupleHolder[Int, String, Boolean]] + val js = sj.toJson(inst) + js should matchJson("""{"a":[15,"wow",true]}""") + sj.fromJson(js) shouldEqual inst + } + // it("Tuple of collecitons (including another tuple) must work") { + // val inst = TupleHolder[Seq[Int], Map[String, Long], (Double, Char, Boolean)]((List(1, 2), Map("a" -> 3L, "b" -> 4L), (1.23d, 'X', true))) + // val js = sjCodecOf[TupleHolder[Seq[Int], Map[String, Long], (Double, Char, Boolean)]].toJson(inst) + // js should matchJson("""{"a":[[1,2],{"a":3,"b":4},[1.23,"X",true]]}""") + // } + } + + describe(colorString("--- Negative Tests ---")) { + it("Wrong number of elements in a tuple") { + val js = """{"a":[15,"wow",true,12.34]}""" + val msg = + """Expected ']' here at position [19] + |{"a":[15,"wow",true,12.34]} + |-------------------^""".stripMargin + val ex = intercept[JsonParseError](sjCodecOf[TupleHolder[Int, String, Boolean]].fromJson(js)) + ex.show shouldEqual msg + } + it("Wrong type of elements in tuple") { + val js = """{"a":[15,true,true]}""" + val msg = + """Expected a String value but got 't' at position [9] + |{"a":[15,true,true]} + |---------^""".stripMargin + val ex = intercept[JsonParseError](sjCodecOf[TupleHolder[Int, String, Boolean]].fromJson(js)) + ex.show shouldEqual msg + } + } + } diff --git a/src/test/scala/co.blocke.scalajack/json/collections/TupleSpec.scalax b/src/test/scala/co.blocke.scalajack/json/collections/TupleSpec.scalax deleted file mode 100644 index ef31ceab..00000000 --- a/src/test/scala/co.blocke.scalajack/json/collections/TupleSpec.scalax +++ /dev/null @@ -1,34 +0,0 @@ -package co.blocke.scalajack -package json -package collections - -import ScalaJack.* -import co.blocke.scala_reflection.* -import org.scalatest.funspec.AnyFunSpec -import org.scalatest.matchers.should.Matchers.* -import org.scalatest.* -import TestUtil.* - -import java.util.UUID - -class TupleSpec() extends AnyFunSpec with JsonMatchers: - - describe(colorString("-------------------------------\n: Tuple Tests :\n-------------------------------", Console.YELLOW)) { - describe(colorString("+++ Positive Tests +++")) { - it("Tuple is null must work") { - val inst = TupleHolder[Int, String, Boolean](null) - val js = sj[TupleHolder[Int, String, Boolean]].toJson(inst) - js should matchJson("""{"a":null}""") - } - it("Tuple of simple types must work") { - val inst = TupleHolder[Int, String, Boolean]((15, "wow", true)) - val js = sj[TupleHolder[Int, String, Boolean]].toJson(inst) - js should matchJson("""{"a":[15,"wow",true]}""") - } - it("Tuple of collecitons (including another tuple) must work") { - val inst = TupleHolder[Seq[Int], Map[String, Long], (Double, Char, Boolean)]((List(1, 2), Map("a" -> 3L, "b" -> 4L), (1.23d, 'X', true))) - val js = sj[TupleHolder[Seq[Int], Map[String, Long], (Double, Char, Boolean)]].toJson(inst) - js should matchJson("""{"a":[[1,2],{"a":3,"b":4},[1.23,"X",true]]}""") - } - } - } diff --git a/src/test/scala/co.blocke.scalajack/json/misc/EnumSpec.scalax b/src/test/scala/co.blocke.scalajack/json/misc/EnumSpec.scalax new file mode 100644 index 00000000..07d2dd73 --- /dev/null +++ b/src/test/scala/co.blocke.scalajack/json/misc/EnumSpec.scalax @@ -0,0 +1,54 @@ +TBD + +Test: + +* Enumeration +* Enum +* Java Enumeration +* Sealed trait w/case object +* Sealed trait w/case classes + +* Try permutations of Map having keys of each of the above types, including ordinal (int) values where available + + +Partial: + + + it("Enum as Map key and value must work") { + val inst = MapHolder[Color, Color](Map(Color.Red -> Color.Blue, Color.Green -> Color.Red)) + val js = sj[MapHolder[Color, Color]].toJson(inst) + js should matchJson("""{"a":{"Red":"Blue","Green":"Red"}}""") + } + it("Enum as Map key and value must work (using id)") { + val inst = MapHolder[Color, Color](Map(Color.Red -> Color.Blue, Color.Green -> Color.Red)) + val js = sj[MapHolder[Color, Color]](JsonConfig.withEnumsAsIds(Some(Nil))).toJson(inst) + js should matchJson("""{"a":{"0":2,"1":0}}""") + } + it("Enumeration as Map key and value must work") { + import Permissions.* + val inst = MapHolder[Permissions, Permissions](Map(Permissions.READ -> Permissions.WRITE, Permissions.EXEC -> Permissions.NONE)) + val js = sj[MapHolder[Permissions, Permissions]].toJson(inst) + js should matchJson("""{"a":{"READ":"WRITE","EXEC":"NONE"}}""") + } + it("Enumeration as Map key and value must work (using id)") { + import Permissions.* + val inst = MapHolder[Permissions, Permissions](Map(Permissions.READ -> Permissions.WRITE, Permissions.EXEC -> Permissions.NONE)) + val js = sj[MapHolder[Permissions, Permissions]](JsonConfig.withEnumsAsIds(Some(Nil))).toJson(inst) + js should matchJson("""{"a":{"0":1,"2":3}}""") + } + it("Java Enumeration as Map key and value must work") { + val inst = MapHolder[CarEnum, CarEnum](Map(CarEnum.VW -> CarEnum.PORSCHE, CarEnum.PORSCHE -> CarEnum.TOYOTA)) + val js = sj[MapHolder[CarEnum, CarEnum]].toJson(inst) + js should matchJson("""{"a":{"VW":"PORSCHE","PORSCHE":"TOYOTA"}}""") + } + it("Java Enumeration as Map key and value must work (using id)") { + val inst = MapHolder[CarEnum, CarEnum](Map(CarEnum.VW -> CarEnum.PORSCHE, CarEnum.PORSCHE -> CarEnum.TOYOTA)) + val js = sj[MapHolder[CarEnum, CarEnum]](JsonConfig.withEnumsAsIds(Some(Nil))).toJson(inst) + js should matchJson("""{"a":{"1":2,"2":0}}""") + } + it("Enum/Enumeration mix of enum as value must work") { + import Permissions.* + val inst = MapHolder[Color, Permissions](Map(Color.Red -> Permissions.WRITE, Color.Blue -> Permissions.NONE)) + val js = sj[MapHolder[Color, Permissions]](JsonConfig.withEnumsAsIds(Some(List("co.blocke.scalajack.json.collections.Color")))).toJson(inst) + js should matchJson("""{"a":{"0":"WRITE","2":"NONE"}}""") + } \ No newline at end of file diff --git a/src/test/scala/co.blocke.scalajack/json/misc/LRSpec.scala b/src/test/scala/co.blocke.scalajack/json/misc/LRSpec.scala new file mode 100644 index 00000000..289f5df7 --- /dev/null +++ b/src/test/scala/co.blocke.scalajack/json/misc/LRSpec.scala @@ -0,0 +1,125 @@ +package co.blocke.scalajack +package json +package misc + +import ScalaJack.* +import co.blocke.scala_reflection.* +import org.scalatest.funspec.AnyFunSpec +import org.scalatest.matchers.should.Matchers.* +import org.scalatest.* +import scala.util.* +import TestUtil.* + +import java.util.UUID + +class LRSpec() extends AnyFunSpec with JsonMatchers: + + describe(colorString("-------------------------------\n: Either Tests :\n-------------------------------", Console.YELLOW)) { + it("Complex Either/Option must work (non-None)") { + val inst = ComplexEither[Int](Some(Right(Some(3)))) + val sj = sjCodecOf[ComplexEither[Int]] + val js = sj.toJson(inst) + js should matchJson("""{"a":3}""") + sj.fromJson(js) shouldEqual (inst) + } + it("Complex Either/Option must work (None no-write default)") { + val inst = ComplexEither[Int](Some(Right(None))) + val sj = sjCodecOf[ComplexEither[Int]] + val js = sj.toJson(inst) + js should matchJson("""{}""") + sj.fromJson(js) shouldEqual (ComplexEither(null)) + } + it("Complex Either/Option must work (NoneAsNull)") { + val inst = ComplexEither[Int](Some(Right(None))) + val sj = sjCodecOf[ComplexEither[Int]](JsonConfig.withNoneAsNull()) + val js = sj.toJson(inst) + js should matchJson("""{"a":null}""") + sj.fromJson(js) shouldEqual (ComplexEither(None)) // None here because value existed, but was null with NoneAsNull + } + it("Complex Either/Option must work (Left-NO_WRITE)") { + val inst = ComplexEither[Int](Some(Left("err"))) + val sj = sjCodecOf[ComplexEither[Int]] + val js = sjCodecOf[ComplexEither[Int]].toJson(inst) + js should matchJson("""{}""") + sj.fromJson(js) shouldEqual (ComplexEither(null)) // Null because value didn't exist at all + } + it("Complex Either/Option must work (Left-AS_VALUE)") { + val inst = ComplexEither[Int](Some(Left("err"))) + val sj = sjCodecOf[ComplexEither[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)) + val js = sj.toJson(inst) + js should matchJson("""{"a":"err"}""") + sj.fromJson(js) shouldEqual (inst) + } + it("Either with AS_VALUE left policy must work") { + val inst = EitherHolder[Int](Left(5), Right(3)) + val sj = sjCodecOf[EitherHolder[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)) + val js = sj.toJson(inst) + js should matchJson("""{"a":5,"b":3}""") + sj.fromJson(js) shouldEqual (inst) + } + it("Either with AS_NULL left policy must work") { + val inst = EitherHolder[Int](Left(5), Right(3)) + val sj = sjCodecOf[EitherHolder[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_NULL)) + val js = sj.toJson(inst) + js should matchJson("""{"a":null,"b":3}""") + sj.fromJson(js) shouldEqual (EitherHolder(null, Right(3))) + } + it("Either with NO_WRITE left policy must work") { + val inst = EitherHolder[Int](Left(5), Right(3)) + val sj = sjCodecOf[EitherHolder[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.NO_WRITE)) + val js = sj.toJson(inst) + js should matchJson("""{"b":3}""") + // This js cannot be read back in becuase it's missing required field 'a', which wasn't written out + // per NO_WRITE policy. This is a 1-way trip... so be advised... + } + it("Either with ERR_MSG_STRING left policy must work") { + val inst = EitherHolder[Int](Left(5), Right(3)) + val sj = sjCodecOf[EitherHolder[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.ERR_MSG_STRING)) + val js = sj.toJson(inst) + js should matchJson("""{"a":"Left Error: 5","b":3}""") + sj.fromJson(js) shouldEqual (EitherHolder(Right("Left Error: 5"), Right(3))) + // WARNING! Here a Left(err_msg) was "promoted" to a Right(String) upon read, because Right was of type + // String, and "Left Error: 5" is a valid string. Use with extreme caution. Best to consider this a 1-way + // trip for debugging purposes only. You have been warned. + } + it("Either with THROW_EXCEPTION left policy must work") { + val inst = EitherHolder[Int](Left(5), Right(3)) + val caught = + intercept[JsonEitherLeftError] { + sjCodecOf[EitherHolder[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.THROW_EXCEPTION)).toJson(inst) + } + assert(caught.getMessage == "Left Error: 5") + } + } + + describe(colorString("----------------------------------\n: Union Tests :\n----------------------------------", Console.YELLOW)) { + it("LR (union) must work with Option (non-None)") { + val inst = LRUnionHolder[Option[Int], String](List(Some(5), "x"), ("y", Some(10))) + val sj = sjCodecOf[LRUnionHolder[Option[Int], String]] + val js = sj.toJson(inst) + js should matchJson("""{"a":[5,"x"],"b":["y",10]}""") + sj.fromJson(js) shouldEqual inst + } + it("LR (union) must work with Option (None)") { + val inst = LRUnionHolder[Option[Int], String](List(None, "x"), ("y", None)) + val sj = sjCodecOf[LRUnionHolder[Option[Int], String]] + val js = sj.toJson(inst) + js should matchJson("""{"a":["x"],"b":["y",null]}""") + sj.fromJson(js) shouldEqual LRUnionHolder[Option[Int], String](List("x"), ("y", None)) + } + // it("LR (union) must work with Try of Option (non-None)") { + // val inst = LRHolder[Try[Option[Int]], String](List(Success(Some(5)), "x"), ("y", Success(Some(10)))) + // val js = sj[LRHolder[Try[Option[Int]], String]].toJson(inst) + // js should matchJson("""{"a":[5,"x"],"b":["y",10]}""") + // } + // it("LR (union) must work with Try of Option (Success(None))") { + // val inst = LRHolder[Try[Option[Int]], String](List(Success(None), "x"), ("y", Success(None))) + // val js = sj[LRHolder[Try[Option[Int]], String]].toJson(inst) + // js should matchJson("""{"a":["x"],"b":["y",null]}""") + // } + // it("LR (union) must work with Try of Option (Failure)") { + // val inst = LRHolder[Try[Option[Int]], String](List(Failure(new Exception("boom")), "x"), ("y", Failure(new Exception("boom2")))) + // val js = sj[LRHolder[Try[Option[Int]], String]].toJson(inst) + // js should matchJson("""{"a":["x"],"b":["y",null]}""") + // } + } \ No newline at end of file diff --git a/src/test/scala/co.blocke.scalajack/json/misc/Model.scala b/src/test/scala/co.blocke.scalajack/json/misc/Model.scala index aae61c3b..8d1c122e 100644 --- a/src/test/scala/co.blocke.scalajack/json/misc/Model.scala +++ b/src/test/scala/co.blocke.scalajack/json/misc/Model.scala @@ -24,7 +24,7 @@ case class OptionHolder[T]( case class TryHolder[T](a: Try[T]) case class TryHolder2[T](a: Seq[Try[T]], b: (Try[T], Try[T])) -case class LRHolder[T, U](a: Seq[T | U], b: (T | U, T | U)) +case class LRUnionHolder[T, U](a: Seq[T | U], b: (T | U, T | U)) case class EitherHolder[T](a: Either[T, String], b: Either[String, T]) case class ComplexEither[T](a: Option[Either[String, Option[T]]]) @@ -62,3 +62,31 @@ case class AnyHolder( whichOneL: Any, // Either[String,Int] <- left bunch: Any // (Some('a'),None,Some('b')) ) + +object Size extends Enumeration { + val Small, Medium, Large = Value +} +object SizeWithType extends Enumeration { + type SizeWithType = Value + val Little, Grand = Value +} +import SizeWithType.* +case class SampleEnum(e1: Size.Value, e2: Size.Value, e3: Size.Value, e4: Size.Value, e5: Size.Value, e6: SizeWithType) + +enum Color { + case Red, Blue, Green +} +case class TVColors(color1: Color, color2: Color) + +sealed trait Flavor +case object Vanilla extends Flavor +case object Chocolate extends Flavor +case object Bourbon extends Flavor + +sealed trait Vehicle +case class Truck(numberOfWheels: Int) extends Vehicle +case class Car(numberOfWheels: Int, color: String) extends Vehicle +case class Plane(numberOfEngines: Int) extends Vehicle + +case class Ride(wheels: Vehicle) +case class Favorite(flavor: Flavor) diff --git a/src/test/scala/co.blocke.scalajack/json/misc/OptionLRTrySpec.scala b/src/test/scala/co.blocke.scalajack/json/misc/OptionLRTrySpec.scala deleted file mode 100644 index 1165e091..00000000 --- a/src/test/scala/co.blocke.scalajack/json/misc/OptionLRTrySpec.scala +++ /dev/null @@ -1,244 +0,0 @@ -package co.blocke.scalajack -package json -package misc - -import ScalaJack.* -import co.blocke.scala_reflection.* -import org.scalatest.funspec.AnyFunSpec -import org.scalatest.matchers.should.Matchers.* -import org.scalatest.* -import scala.util.* -import TestUtil.* - -import java.util.UUID - -class OptionLRTrySpec() extends AnyFunSpec with JsonMatchers: - - /* - describe(colorString("-------------------------------\n: Option Tests :\n-------------------------------", Console.YELLOW)) { - describe(colorString("+++ Positive Tests +++")) { - it("Non-empty Options must work") { - val inst = OptionHolder[Int]( - Some(5), // straight Option - (Some(1), "ok"), // tuple w/Option - List(Some(1), None, Some(3)), // Seq of Option - Map(1 -> Some(2), 3 -> Some(4)), // Map of Option - Some(99), // Union of Option (R) - Some(100), // Union of Option (L) - Some(Some(0)), // Nested Option - Some(Person("BoB", 34)), // Option of class - Right(Some(15)), // Either of Option (R) - Left(Some(-3)) // Either of Option (L) - ) - val js = sjCodecOf[OptionHolder[Int]].toJson(inst) - js should matchJson("""{"a":5,"b":[1,"ok"],"c":[1,3],"d":{"1":2,"3":4},"e":99,"f":100,"g":0,"h":{"name":"BoB","age":34},"i":15}""") - } - it("Empty Options must work (default)") { - val inst = OptionHolder[Int]( - None, // straight Option - (None, "ok"), // tuple w/Option - List(None, None, None), // Seq of Option - Map(1 -> None, 3 -> None), // Map of Option - None, // Union of Option (R) - None, // Union of Option (L) - Some(None), // Nested Option - None, // Option of class - Right(None), // Either of Option (R) - Left(None) // Either of Option (L) - ) - val js = sjCodecOf[OptionHolder[Int]].toJson(inst) - js should matchJson("""{"b":[null,"ok"],"c":[],"d":{}}""") - } - /* - it("Empty Options must work (config noneAsNull = true)") { - val inst = OptionHolder[Int]( - None, // straight Option - (None, "ok"), // tuple w/Option - List(None, None, None), // Seq of Option - Map(1 -> None, 3 -> None), // Map of Option - None, // Union of Option (R) - None, // Union of Option (L) - Some(None), // Nested Option - None, // Option of class - Right(None), // Either of Option (R) - Left(None) // Either of Option (L) - ) - val js = sj[OptionHolder[Int]]( - JsonConfig.withNoneAsNull().withEitherLeftHandling(EitherLeftPolicy.AS_VALUE) - ).toJson(inst) - js should matchJson("""{"a":null,"b":[null,"ok"],"c":[null,null,null],"d":{"1":null,"3":null},"e":null,"f":null,"g":null,"h":null,"i":null,"j":null}""") - } - */ - } - } - */ - - describe(colorString("-------------------------------\n: Either Tests :\n-------------------------------", Console.YELLOW)) { - describe(colorString("+++ Positive Tests +++")) { - it("Complex Either/Option must work (non-None)") { - val inst = ComplexEither[Int](Some(Right(Some(3)))) - val sj = sjCodecOf[ComplexEither[Int]] - val js = sj.toJson(inst) - js should matchJson("""{"a":3}""") - sj.fromJson(js) shouldEqual (inst) - } - it("Complex Either/Option must work (None no-write default)") { - val inst = ComplexEither[Int](Some(Right(None))) - val sj = sjCodecOf[ComplexEither[Int]] - val js = sj.toJson(inst) - js should matchJson("""{}""") - sj.fromJson(js) shouldEqual (ComplexEither(null)) - } - it("Complex Either/Option must work (NoneAsNull)") { - val inst = ComplexEither[Int](Some(Right(None))) - val sj = sjCodecOf[ComplexEither[Int]](JsonConfig.withNoneAsNull()) - val js = sj.toJson(inst) - js should matchJson("""{"a":null}""") - sj.fromJson(js) shouldEqual (ComplexEither(None)) // None here because value existed, but was null with NoneAsNull - } - it("Complex Either/Option must work (Left-NO_WRITE)") { - val inst = ComplexEither[Int](Some(Left("err"))) - val sj = sjCodecOf[ComplexEither[Int]] - val js = sjCodecOf[ComplexEither[Int]].toJson(inst) - js should matchJson("""{}""") - sj.fromJson(js) shouldEqual (ComplexEither(null)) // Null because value didn't exist at all - } - it("Complex Either/Option must work (Left-AS_VALUE)") { - val inst = ComplexEither[Int](Some(Left("err"))) - val sj = sjCodecOf[ComplexEither[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)) - val js = sj.toJson(inst) - js should matchJson("""{"a":"err"}""") - sj.fromJson(js) shouldEqual (inst) - } - it("Either with AS_VALUE left policy must work") { - val inst = EitherHolder[Int](Left(5), Right(3)) - val sj = sjCodecOf[EitherHolder[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)) - val js = sj.toJson(inst) - js should matchJson("""{"a":5,"b":3}""") - sj.fromJson(js) shouldEqual (inst) - } - it("Either with AS_NULL left policy must work") { - val inst = EitherHolder[Int](Left(5), Right(3)) - val sj = sjCodecOf[EitherHolder[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_NULL)) - val js = sj.toJson(inst) - js should matchJson("""{"a":null,"b":3}""") - sj.fromJson(js) shouldEqual (EitherHolder(null, Right(3))) - } - it("Either with NO_WRITE left policy must work") { - val inst = EitherHolder[Int](Left(5), Right(3)) - val sj = sjCodecOf[EitherHolder[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.NO_WRITE)) - val js = sj.toJson(inst) - js should matchJson("""{"b":3}""") - // This js cannot be read back in becuase it's missing required field 'a', which wasn't written out - // per NO_WRITE policy. This is a 1-way trip... so be advised... - } - it("Either with ERR_MSG_STRING left policy must work") { - val inst = EitherHolder[Int](Left(5), Right(3)) - val sj = sjCodecOf[EitherHolder[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.ERR_MSG_STRING)) - val js = sj.toJson(inst) - js should matchJson("""{"a":"Left Error: 5","b":3}""") - sj.fromJson(js) shouldEqual (EitherHolder(Right("Left Error: 5"), Right(3))) - // WARNING! Here a Left(err_msg) was "promoted" to a Right(String) upon read, because Right was of type - // String, and "Left Error: 5" is a valid string. Use with extreme caution. Best to consider this a 1-way - // trip for debugging purposes only. You have been warned. - } - it("Either with THROW_EXCEPTION left policy must work") { - val inst = EitherHolder[Int](Left(5), Right(3)) - val caught = - intercept[JsonEitherLeftError] { - sjCodecOf[EitherHolder[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.THROW_EXCEPTION)).toJson(inst) - } - assert(caught.getMessage == "Left Error: 5") - } - } - } - - /* - describe(colorString("-------------------------------\n: LR Tests :\n-------------------------------", Console.YELLOW)) { - describe(colorString("+++ Positive Tests +++")) { - it("LR (union) must work with Option (non-None)") { - val inst = LRHolder[Option[Int], String](List(Some(5), "x"), ("y", Some(10))) - val js = sj[LRHolder[Option[Int], String]].toJson(inst) - js should matchJson("""{"a":[5,"x"],"b":["y",10]}""") - } - it("LR (union) must work with Option (None)") { - val inst = LRHolder[Option[Int], String](List(None, "x"), ("y", None)) - val js = sj[LRHolder[Option[Int], String]].toJson(inst) - js should matchJson("""{"a":["x"],"b":["y",null]}""") - } - it("LR (union) must work with Try of Option (non-None)") { - val inst = LRHolder[Try[Option[Int]], String](List(Success(Some(5)), "x"), ("y", Success(Some(10)))) - val js = sj[LRHolder[Try[Option[Int]], String]].toJson(inst) - js should matchJson("""{"a":[5,"x"],"b":["y",10]}""") - } - it("LR (union) must work with Try of Option (Success(None))") { - val inst = LRHolder[Try[Option[Int]], String](List(Success(None), "x"), ("y", Success(None))) - val js = sj[LRHolder[Try[Option[Int]], String]].toJson(inst) - js should matchJson("""{"a":["x"],"b":["y",null]}""") - } - it("LR (union) must work with Try of Option (Failure)") { - val inst = LRHolder[Try[Option[Int]], String](List(Failure(new Exception("boom")), "x"), ("y", Failure(new Exception("boom2")))) - val js = sj[LRHolder[Try[Option[Int]], String]].toJson(inst) - js should matchJson("""{"a":["x"],"b":["y",null]}""") - } - } - } - - describe(colorString("-------------------------------\n: Try Tests :\n-------------------------------", Console.YELLOW)) { - describe(colorString("+++ Positive Tests +++")) { - it("Try must work (Success)") { - val inst = TryHolder(Success(15)) - val js = sj[TryHolder[Int]].toJson(inst) - js should matchJson("""{"a":15}""") - } - it("Try of Option (non-None) must work (Success)") { - val inst = TryHolder[Option[Int]](Success(Some(15))) - val js = sj[TryHolder[Option[Int]]].toJson(inst) - js should matchJson("""{"a":15}""") - } - it("Try of Option (None) must work (Success)") { - val inst = TryHolder[Option[Int]](Success(None)) - val js = sj[TryHolder[Option[Int]]].toJson(inst) - js should matchJson("""{}""") - } - it("Try w/policy AS_NULL must work (Failure)") { - val inst = TryHolder[Int](Failure(new Exception("boom"))) - val js = sj[TryHolder[Int]](JsonConfig.withTryFailureHandling(TryPolicy.AS_NULL)).toJson(inst) - js should matchJson("""{"a":null}""") - } - it("Try w/policy NO_WRITE must work (Failure)") { - val inst = TryHolder[Int](Failure(new Exception("boom"))) - val js = sj[TryHolder[Int]](JsonConfig.withTryFailureHandling(TryPolicy.NO_WRITE)).toJson(inst) - js should matchJson("""{}""") - } - it("Try w/policy ERR_MSG_STRING must work (Failure)") { - val inst = TryHolder[Int](Failure(new Exception("boom"))) - val js = sj[TryHolder[Int]](JsonConfig.withTryFailureHandling(TryPolicy.ERR_MSG_STRING)).toJson(inst) - js should matchJson("""{"a":"Try Failure with msg: boom"}""") - } - it("Try w/policy ATHROW_EXCEPTIONS_NULL must work (Failure)") { - val inst = TryHolder[Int](Failure(new Exception("boom"))) - val caught = - intercept[java.lang.Exception] { - sj[TryHolder[Int]](JsonConfig.withTryFailureHandling(TryPolicy.THROW_EXCEPTION)).toJson(inst) - } - assert(caught.getMessage == "boom") - } - it("Seq and Tuple of Try must work for AS_NULL (Failure)") { - val inst = TryHolder2[Int](List(Success(1), Failure(new Exception("boom")), Success(3)), (Failure(new Exception("boom")), Success(0))) - val js = sj[TryHolder2[Int]](JsonConfig.withTryFailureHandling(TryPolicy.AS_NULL)).toJson(inst) - js should matchJson("""{"a":[1,null,3],"b":[null,0]}""") - } - it("Seq and Tuple of Try must work for NO_WRITE (Failure)") { - val inst = TryHolder2[Int](List(Success(1), Failure(new Exception("boom")), Success(3)), (Failure(new Exception("boom")), Success(0))) - val js = sj[TryHolder2[Int]](JsonConfig.withTryFailureHandling(TryPolicy.NO_WRITE)).toJson(inst) - js should matchJson("""{"a":[1,3],"b":[null,0]}""") - } - it("Seq and Tuple of Try of an Option must work for NO_WRITE (Failure)") { - val inst = TryHolder2[Option[Int]](List(Success(None), Failure(new Exception("boom")), Success(Some(3))), (Failure(new Exception("boom")), Success(None))) - val js = sj[TryHolder2[Option[Int]]](JsonConfig.withTryFailureHandling(TryPolicy.NO_WRITE)).toJson(inst) - js should matchJson("""{"a":[3],"b":[null,null]}""") - } - } - } - */ diff --git a/src/test/scala/co.blocke.scalajack/json/misc/OptionSpec.scala b/src/test/scala/co.blocke.scalajack/json/misc/OptionSpec.scala new file mode 100644 index 00000000..fabcda59 --- /dev/null +++ b/src/test/scala/co.blocke.scalajack/json/misc/OptionSpec.scala @@ -0,0 +1,73 @@ +package co.blocke.scalajack +package json +package misc + +import ScalaJack.* +import co.blocke.scala_reflection.* +import org.scalatest.funspec.AnyFunSpec +import org.scalatest.matchers.should.Matchers.* +import org.scalatest.* +import scala.util.* +import TestUtil.* + +import java.util.UUID + +class OptionTrySpec() extends AnyFunSpec with JsonMatchers: + + describe(colorString("-------------------------------\n: Option Tests :\n-------------------------------", Console.YELLOW)) { + /* + it("Non-empty Options must work") { + val inst = OptionHolder[Int]( + Some(5), // straight Option + (Some(1), "ok"), // tuple w/Option + List(Some(1), None, Some(3)), // Seq of Option + Map(1 -> Some(2), 3 -> Some(4)), // Map of Option + Some(99), // Union of Option (R) + Some(100), // Union of Option (L) + Some(Some(0)), // Nested Option + Some(Person("BoB", 34)), // Option of class + Right(Some(15)), // Either of Option (R) + Left(Some(-3)) // Either of Option (L) + ) + val js = sjCodecOf[OptionHolder[Int]].toJson(inst) + js should matchJson("""{"a":5,"b":[1,"ok"],"c":[1,3],"d":{"1":2,"3":4},"e":99,"f":100,"g":0,"h":{"name":"BoB","age":34},"i":15}""") + } + it("Empty Options must work (default)") { + val inst = OptionHolder[Int]( + None, // straight Option + (None, "ok"), // tuple w/Option + List(None, None, None), // Seq of Option + Map(1 -> None, 3 -> None), // Map of Option + None, // Union of Option (R) + None, // Union of Option (L) + Some(None), // Nested Option + None, // Option of class + Right(None), // Either of Option (R) + Left(None) // Either of Option (L) + ) + val js = sjCodecOf[OptionHolder[Int]].toJson(inst) + js should matchJson("""{"b":[null,"ok"],"c":[],"d":{}}""") + } + it("Empty Options must work (config noneAsNull = true)") { + val inst = OptionHolder[Int]( + None, // straight Option + (None, "ok"), // tuple w/Option + List(None, None, None), // Seq of Option + Map(1 -> None, 3 -> None), // Map of Option + None, // Union of Option (R) + None, // Union of Option (L) + Some(None), // Nested Option + None, // Option of class + Right(None), // Either of Option (R) + Left(None) // Either of Option (L) + ) + val js = sj[OptionHolder[Int]]( + JsonConfig.withNoneAsNull().withEitherLeftHandling(EitherLeftPolicy.AS_VALUE) + ).toJson(inst) + js should matchJson("""{"a":null,"b":[null,"ok"],"c":[null,null,null],"d":{"1":null,"3":null},"e":null,"f":null,"g":null,"h":null,"i":null,"j":null}""") + } + it("Java Optional must work") { + ??? + } + */ + } diff --git a/src/test/scala/co.blocke.scalajack/json/misc/TrySpec.scalax b/src/test/scala/co.blocke.scalajack/json/misc/TrySpec.scalax new file mode 100644 index 00000000..efcdb1b9 --- /dev/null +++ b/src/test/scala/co.blocke.scalajack/json/misc/TrySpec.scalax @@ -0,0 +1,73 @@ +package co.blocke.scalajack +package json +package misc + +import ScalaJack.* +import co.blocke.scala_reflection.* +import org.scalatest.funspec.AnyFunSpec +import org.scalatest.matchers.should.Matchers.* +import org.scalatest.* +import scala.util.* +import TestUtil.* + +import java.util.UUID + +class TrySpec() extends AnyFunSpec with JsonMatchers: + + describe(colorString("-------------------------------\n: Try Tests :\n-------------------------------", Console.YELLOW)) { + describe(colorString("+++ Positive Tests +++")) { + it("Try must work (Success)") { + val inst = TryHolder(Success(15)) + val js = sj[TryHolder[Int]].toJson(inst) + js should matchJson("""{"a":15}""") + } + it("Try of Option (non-None) must work (Success)") { + val inst = TryHolder[Option[Int]](Success(Some(15))) + val js = sj[TryHolder[Option[Int]]].toJson(inst) + js should matchJson("""{"a":15}""") + } + it("Try of Option (None) must work (Success)") { + val inst = TryHolder[Option[Int]](Success(None)) + val js = sj[TryHolder[Option[Int]]].toJson(inst) + js should matchJson("""{}""") + } + it("Try w/policy AS_NULL must work (Failure)") { + val inst = TryHolder[Int](Failure(new Exception("boom"))) + val js = sj[TryHolder[Int]](JsonConfig.withTryFailureHandling(TryPolicy.AS_NULL)).toJson(inst) + js should matchJson("""{"a":null}""") + } + it("Try w/policy NO_WRITE must work (Failure)") { + val inst = TryHolder[Int](Failure(new Exception("boom"))) + val js = sj[TryHolder[Int]](JsonConfig.withTryFailureHandling(TryPolicy.NO_WRITE)).toJson(inst) + js should matchJson("""{}""") + } + it("Try w/policy ERR_MSG_STRING must work (Failure)") { + val inst = TryHolder[Int](Failure(new Exception("boom"))) + val js = sj[TryHolder[Int]](JsonConfig.withTryFailureHandling(TryPolicy.ERR_MSG_STRING)).toJson(inst) + js should matchJson("""{"a":"Try Failure with msg: boom"}""") + } + it("Try w/policy ATHROW_EXCEPTIONS_NULL must work (Failure)") { + val inst = TryHolder[Int](Failure(new Exception("boom"))) + val caught = + intercept[java.lang.Exception] { + sj[TryHolder[Int]](JsonConfig.withTryFailureHandling(TryPolicy.THROW_EXCEPTION)).toJson(inst) + } + assert(caught.getMessage == "boom") + } + it("Seq and Tuple of Try must work for AS_NULL (Failure)") { + val inst = TryHolder2[Int](List(Success(1), Failure(new Exception("boom")), Success(3)), (Failure(new Exception("boom")), Success(0))) + val js = sj[TryHolder2[Int]](JsonConfig.withTryFailureHandling(TryPolicy.AS_NULL)).toJson(inst) + js should matchJson("""{"a":[1,null,3],"b":[null,0]}""") + } + it("Seq and Tuple of Try must work for NO_WRITE (Failure)") { + val inst = TryHolder2[Int](List(Success(1), Failure(new Exception("boom")), Success(3)), (Failure(new Exception("boom")), Success(0))) + val js = sj[TryHolder2[Int]](JsonConfig.withTryFailureHandling(TryPolicy.NO_WRITE)).toJson(inst) + js should matchJson("""{"a":[1,3],"b":[null,0]}""") + } + it("Seq and Tuple of Try of an Option must work for NO_WRITE (Failure)") { + val inst = TryHolder2[Option[Int]](List(Success(None), Failure(new Exception("boom")), Success(Some(3))), (Failure(new Exception("boom")), Success(None))) + val js = sj[TryHolder2[Option[Int]]](JsonConfig.withTryFailureHandling(TryPolicy.NO_WRITE)).toJson(inst) + js should matchJson("""{"a":[3],"b":[null,null]}""") + } + } + } diff --git a/src/test/scala/co.blocke.scalajack/json/primitives/Model.scala b/src/test/scala/co.blocke.scalajack/json/primitives/Model.scala index 8ce7b642..db85415f 100644 --- a/src/test/scala/co.blocke.scalajack/json/primitives/Model.scala +++ b/src/test/scala/co.blocke.scalajack/json/primitives/Model.scala @@ -8,34 +8,6 @@ import java.math.{BigDecimal as JBigDecimal, BigInteger as JBigInteger} import java.time.* import scala.math.* -object Size extends Enumeration { - val Small, Medium, Large = Value -} -object SizeWithType extends Enumeration { - type SizeWithType = Value - val Little, Grand = Value -} -import SizeWithType.* -case class SampleEnum(e1: Size.Value, e2: Size.Value, e3: Size.Value, e4: Size.Value, e5: Size.Value, e6: SizeWithType) - -enum Color { - case Red, Blue, Green -} -case class TVColors(color1: Color, color2: Color) - -sealed trait Flavor -case object Vanilla extends Flavor -case object Chocolate extends Flavor -case object Bourbon extends Flavor - -sealed trait Vehicle -case class Truck(numberOfWheels: Int) extends Vehicle -case class Car(numberOfWheels: Int, color: String) extends Vehicle -case class Plane(numberOfEngines: Int) extends Vehicle - -case class Ride(wheels: Vehicle) -case class Favorite(flavor: Flavor) - // === Scala case class SampleBigDecimal(bd1: BigDecimal, bd2: BigDecimal, bd3: BigDecimal, bd4: BigDecimal, bd5: BigDecimal, bd6: BigDecimal) case class SampleBigInt(bi1: BigInt, bi2: BigInt, bi3: BigInt, bi4: BigInt) @@ -84,6 +56,14 @@ case class SampleZoneOffset(z1: ZoneOffset, z2: ZoneOffset) // === Any primitives case class AnyShell(a: Any) +object Size extends Enumeration { + val Small, Medium, Large = Value +} + +enum Color { + case Red, Blue, Green +} + // === Value Classes case class VCBigDecimal(vc: BigDecimal) extends AnyVal case class VCBigInt(vc: BigInt) extends AnyVal From 3b9a37f18dbf92cecae24528e8d61c8b2ad10c3d Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Sun, 31 Mar 2024 00:19:21 -0500 Subject: [PATCH 52/65] fine tuning either and option --- build.sbt | 2 +- .../json/JsonCodecMaker.scala | 93 ++++++---- .../co.blocke.scalajack/json/JsonConfig.scala | 36 +--- .../json/reading/JsonSource.scala | 51 +++--- .../json/reading/Numbers.scala | 6 +- .../json/writing/AnyWriter.scala | 2 - .../json/writing/JsonOutput.scala | 5 + .../scala/co.blocke.scalajack/run/Play.scala | 26 ++- .../co.blocke.scalajack/run/Record.scala | 2 +- .../json/collections/MapSpec.scala | 30 +-- .../json/misc/LRSpec.scala | 171 +++++++++--------- .../co.blocke.scalajack/json/misc/Model.scala | 1 + .../json/misc/OptionSpec.scala | 49 ++++- .../json/primitives/ScalaPrimSpec.scala | 4 +- 14 files changed, 263 insertions(+), 215 deletions(-) diff --git a/build.sbt b/build.sbt index 441eac88..12a6de43 100644 --- a/build.sbt +++ b/build.sbt @@ -35,7 +35,7 @@ lazy val root = project Test / parallelExecution := false, scalafmtOnCompile := !isCI, libraryDependencies ++= Seq( - "co.blocke" %% "scala-reflection" % "2.0.3", + "co.blocke" %% "scala-reflection" % "fixOptionUnit_ea63ce", //"2.0.3", "org.apache.commons" % "commons-text" % "1.11.0", "io.github.kitlangton" %% "neotype" % "0.0.9", "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core" % "2.24.5-SNAPSHOT", diff --git a/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala b/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala index 59419e1a..75b56595 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala @@ -10,7 +10,7 @@ import reading.JsonSource import scala.jdk.CollectionConverters.* import scala.quoted.* import scala.reflect.ClassTag -import scala.annotation.switch +import scala.annotation.{switch, tailrec} import scala.collection.Factory import scala.util.{Failure, Success, Try} import dotty.tools.dotc.ast.Trees.EmptyTree @@ -176,7 +176,6 @@ object JsonCodecMaker: $prefix $out.burpNull() } - case TryPolicy.NO_WRITE => '{ () } case TryPolicy.ERR_MSG_STRING => '{ $prefix @@ -193,13 +192,6 @@ object JsonCodecMaker: t.rightRef.refType match case '[rt] => cfg.eitherLeftHandling match - case EitherLeftPolicy.NO_WRITE => - '{ - if $tin == null then $out.burpNull() - $tin match - case Left(_) => () - case Right(v) => ${ _maybeWrite[rt](prefix, '{ v.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out, cfg) } - } case EitherLeftPolicy.AS_NULL => '{ if $tin == null then $out.burpNull() @@ -231,10 +223,12 @@ object JsonCodecMaker: '{ if $tin == null then $out.burpNull() $tin match - case Left(v) => ${ _maybeWrite[lt](prefix, '{ v.asInstanceOf[lt] }, t.leftRef.asInstanceOf[RTypeRef[lt]], out, cfg) } - case Right(v) => ${ _maybeWrite[rt](prefix, '{ v.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out, cfg) } + case Left(v) => + ${ _maybeWrite[lt](prefix, '{ v.asInstanceOf[lt] }, t.leftRef.asInstanceOf[RTypeRef[lt]], out, cfg) } + case Right(v) => + ${ _maybeWrite[rt](prefix, '{ v.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out, cfg) } } - case t: LeftRightRef[?] if !cfg.noneAsNull && t.lrkind != LRKind.EITHER && (t.leftRef.isInstanceOf[OptionRef[_]] || t.rightRef.isInstanceOf[OptionRef[_]]) => + case t: LeftRightRef[?] if t.lrkind != LRKind.EITHER => t.refType match case '[e] => t.rightRef.refType match @@ -243,14 +237,14 @@ object JsonCodecMaker: case '[lt] => val tin = aE.asExprOf[e] '{ - if $tin == None then () + if $tin == null then $out.burpNull() else $out.mark() scala.util.Try { ${ _maybeWrite[rt](prefix, '{ $tin.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out, cfg) } } match case scala.util.Success(_) => () - case scala.util.Failure(_) => + case scala.util.Failure(f) => $out.revert() ${ _maybeWrite[lt](prefix, '{ $tin.asInstanceOf[lt] }, t.leftRef.asInstanceOf[RTypeRef[lt]], out, cfg) } } @@ -616,15 +610,6 @@ object JsonCodecMaker: case Right(v) => ${ genWriteVal[rt]('{ v.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out, inTuple = inTuple) } } - case EitherLeftPolicy.NO_WRITE => - '{ - if $tin == null then $out.burpNull() - else - $tin match - case Left(v) => () - case Right(v) => - ${ genWriteVal[rt]('{ v.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out, inTuple = inTuple) } - } case EitherLeftPolicy.ERR_MSG_STRING => '{ if $tin == null then $out.burpNull() @@ -671,7 +656,6 @@ object JsonCodecMaker: cfg.tryFailureHandling match case _ if inTuple => '{ $out.burpNull() } case TryPolicy.AS_NULL => '{ $out.burpNull() } - case TryPolicy.NO_WRITE => '{ () } case TryPolicy.ERR_MSG_STRING => '{ $out.value("Try Failure with msg: " + v.getMessage()) } case TryPolicy.THROW_EXCEPTION => '{ throw v } } @@ -860,6 +844,16 @@ object JsonCodecMaker: // --------------------------------------------------------------------------------------------- + def lrHasOptionChild(lr: LeftRightRef[?]): String = + lr.rightRef match + case t: OptionRef[?] => "r" + case t: LeftRightRef[?] => "r" + lrHasOptionChild(t) + case _ => + lr.leftRef match + case t: OptionRef[?] => "l" + case t: LeftRightRef[?] => "l" + lrHasOptionChild(t) + case _ => "" + def genDecFnBody[T: Type](r: RTypeRef[?], in: Expr[JsonSource])(using Quotes): Expr[Unit] = import quotes.reflect.* @@ -905,11 +899,31 @@ object JsonCodecMaker: // if dvMembers.isEmpty then (ValDef(sym, Some(oneField.fieldRef.unitVal.asTerm)), caseDef, fieldSymRef) if dvMembers.isEmpty then // no default... required? Not if Option/Optional, or a collection - oneField.fieldRef match { - case _: OptionRef[?] => // not required - case _ => required = required | math.pow(2, oneField.index).toInt // required + val unitVal = oneField.fieldRef match { + case _: OptionRef[?] => + oneField.fieldRef.unitVal.asTerm // not required + case r: LeftRightRef[?] if r.lrkind == LRKind.EITHER => // maybe required + val optionRecipe = lrHasOptionChild(r) + if optionRecipe.length == 0 then + required = required | math.pow(2, oneField.index).toInt // required + oneField.fieldRef.unitVal.asTerm + else + val recipeE = Expr(optionRecipe) + '{ + $recipeE.foldRight(None: Any)((c, acc) => if c == 'r' then Right(acc) else Left(acc)).asInstanceOf[f] + }.asTerm + case r: LeftRightRef[?] => // maybe required + val optionRecipe = lrHasOptionChild(r) + if optionRecipe.length == 0 then // no Option children -> required + required = required | math.pow(2, oneField.index).toInt // required + oneField.fieldRef.unitVal.asTerm + else // at least one Option child -> optional + '{ None }.asTerm + case _ => + required = required | math.pow(2, oneField.index).toInt // required + oneField.fieldRef.unitVal.asTerm } - (ValDef(sym, Some(oneField.fieldRef.unitVal.asTerm)), caseDef, fieldSymRef) + (ValDef(sym, Some(unitVal)), caseDef, fieldSymRef) else val methodSymbol = dvMembers.head val dvSelectNoTArgs = Ref(companionModule).select(methodSymbol) @@ -1010,14 +1024,14 @@ object JsonCodecMaker: '{ $in.expectString() match case null => - $in.retract() - $in.retract() - $in.retract() - $in.retract() + $in.backspace() + $in.backspace() + $in.backspace() + $in.backspace() throw JsonParseError("Char value cannot be null", $in) case "" => - $in.retract() - $in.retract() + $in.backspace() + $in.backspace() throw JsonParseError("Char value expected but empty string found in json", $in) case c => c.charAt(0) }.asExprOf[T] @@ -1086,8 +1100,8 @@ object JsonCodecMaker: val c = $in.expectString() if c == null then null else if c.length == 0 then - $in.retract() - $in.retract() + $in.backspace() + $in.backspace() throw JsonParseError("Character value expected but empty string found in json", $in) else java.lang.Character.valueOf(c.charAt(0)) }.asExprOf[T] @@ -1257,10 +1271,11 @@ object JsonCodecMaker: case Success(rval) => Right(rval) case Failure(f) => + $in.revertToMark() scala.util.Try(${ genReadVal[l](t.leftRef.asInstanceOf[RTypeRef[l]], in, inTuple) }) match case Success(lval) => Left(lval) case Failure(_) => - $in.retract() + $in.backspace() throw JsonParseError("Failed to read either side of Either type", $in) }.asExprOf[T] @@ -1271,13 +1286,15 @@ object JsonCodecMaker: t.rightRef.refType match case '[r] => '{ + $in.mark() scala.util.Try(${ genReadVal[l](t.leftRef.asInstanceOf[RTypeRef[l]], in, true) }) match case Success(lval) => lval case Failure(f) => + $in.revertToMark() scala.util.Try(${ genReadVal[r](t.rightRef.asInstanceOf[RTypeRef[r]], in, true) }) match case Success(rval) => rval case Failure(_) => - $in.retract() + $in.backspace() throw JsonParseError("Failed to read either side of Union type", $in) }.asExprOf[T] /* diff --git a/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala b/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala index 24845fb2..4952ed9d 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala @@ -8,11 +8,9 @@ import scala.quoted.* class JsonConfig private[scalajack] ( val noneAsNull: Boolean, - // val forbidNullsInInput: Boolean = false, val tryFailureHandling: TryPolicy, val eitherLeftHandling: EitherLeftPolicy, // val undefinedFieldHandling: UndefinedValueOption = UndefinedValueOption.THROW_EXCEPTION, - // val _allowQuotedPrimitives: Boolean, val writeNonConstructorFields: Boolean, // -------------------------- val typeHintLabel: String, @@ -31,7 +29,6 @@ class JsonConfig private[scalajack] ( def withEnumsAsIds(asIds: Option[List[String]]): JsonConfig = copy(enumsAsIds = asIds) def suppressEscapedStrings(): JsonConfig = copy(_suppressEscapedStrings = true) def suppressTypeHints(): JsonConfig = copy(_suppressTypeHints = true) - // def allowQuotedPrimitives(): JsonConfig = copy(_allowQuotedPrimitives = true) private[this] def copy( noneAsNull: Boolean = noneAsNull, @@ -43,12 +40,10 @@ class JsonConfig private[scalajack] ( enumsAsIds: Option[List[String]] = enumsAsIds, _suppressEscapedStrings: Boolean = _suppressEscapedStrings, _suppressTypeHints: Boolean = _suppressTypeHints - // _allowQuotedPrimitives: Boolean = _allowQuotedPrimitives ): JsonConfig = new JsonConfig( noneAsNull, tryFailureHandling, eitherLeftHandling, - // _allowQuotedPrimitives, writeNonConstructorFields, typeHintLabel, typeHintPolicy, @@ -58,10 +53,10 @@ class JsonConfig private[scalajack] ( ) enum TryPolicy: - case AS_NULL, NO_WRITE, ERR_MSG_STRING, THROW_EXCEPTION + case AS_NULL, ERR_MSG_STRING, THROW_EXCEPTION enum EitherLeftPolicy: - case AS_VALUE, AS_NULL, NO_WRITE, ERR_MSG_STRING, THROW_EXCEPTION + case AS_VALUE, AS_NULL, ERR_MSG_STRING, THROW_EXCEPTION // enum UndefinedValueOption: // case AS_NULL, AS_SYMBOL, THROW_EXCEPTION @@ -72,9 +67,8 @@ enum TypeHintPolicy: object JsonConfig extends JsonConfig( noneAsNull = false, - tryFailureHandling = TryPolicy.NO_WRITE, - eitherLeftHandling = EitherLeftPolicy.NO_WRITE, - // _allowQuotedPrimitives = false writeNonConstructorFields = true, + tryFailureHandling = TryPolicy.AS_NULL, + eitherLeftHandling = EitherLeftPolicy.AS_VALUE, writeNonConstructorFields = false, typeHintLabel = "_hint", typeHintPolicy = TypeHintPolicy.SIMPLE_CLASSNAME, @@ -94,7 +88,6 @@ object JsonConfig .withTypeHintLabel(${ Expr(x.typeHintLabel) }) .withTypeHintPolicy(${ Expr(x.typeHintPolicy) }) .withEnumsAsIds(${ Expr(x.enumsAsIds) }) - // .allowQuotedPrimitives(${ Expr(x._allowQuotedPrimitives) }) val jc2 = ${ if x.noneAsNull then '{ jc.withNoneAsNull() } else '{ jc } @@ -124,12 +117,9 @@ object JsonConfig case '{ JsonConfig( $noneAsNullE, - // $forbitNullsInInputE, $tryFailureHandlerE, $eitherLeftHandlerE, - // $allowQuotedPrimitivesE, // $undefinedFieldHandlingE, - // $permissivePrimitivesE, $writeNonConstructorFieldsE, $typeHintLabelE, $typeHintPolicyE, @@ -142,14 +132,9 @@ object JsonConfig Some( JsonConfig( extract("noneAsNull", noneAsNullE), - // extract("forbitNullsInInput", forbitNullsInInputE), extract("tryFailureHandler", tryFailureHandlerE), extract("eitherLeftHandler", eitherLeftHandlerE), - // extract("_allowQuotedPrimitives", allowQuotedPrimtiviesE), - // extract("undefinedFieldHandling", undefinedFieldHandlingE), - // extract("permissivePrimitives", permissivePrimitivesE), extract("writeNonConstructorFields", writeNonConstructorFieldsE), - // extract2[String]("typeHintLabel", x) extract("typeHintLabel", typeHintLabelE), extract("typeHintPolicy", typeHintPolicyE), extract("enumsAsIds", enumsAsIdsE), @@ -162,11 +147,10 @@ object JsonConfig println("ERROR: " + x.getMessage) None } - case '{ JsonConfig } => Some(JsonConfig) - case '{ ($x: JsonConfig).withNoneAsNull() } => Some(x.valueOrAbort.withNoneAsNull()) - case '{ ($x: JsonConfig).withTryFailureHandling($v) } => Some(x.valueOrAbort.withTryFailureHandling(v.valueOrAbort)) - case '{ ($x: JsonConfig).withEitherLeftHandling($v) } => Some(x.valueOrAbort.withEitherLeftHandling(v.valueOrAbort)) - // case '{ ($x: JsonConfig).allowQuotedPrimitives() } => Some(x.valueOrAbort.allowQuotedPrimities()) + case '{ JsonConfig } => Some(JsonConfig) + case '{ ($x: JsonConfig).withNoneAsNull() } => Some(x.valueOrAbort.withNoneAsNull()) + case '{ ($x: JsonConfig).withTryFailureHandling($v) } => Some(x.valueOrAbort.withTryFailureHandling(v.valueOrAbort)) + case '{ ($x: JsonConfig).withEitherLeftHandling($v) } => Some(x.valueOrAbort.withEitherLeftHandling(v.valueOrAbort)) case '{ ($x: JsonConfig).withWriteNonConstructorFields($v) } => Some(x.valueOrAbort.withWriteNonConstructorFields(v.valueOrAbort)) case '{ ($x: JsonConfig).withTypeHintLabel($v) } => Some(x.valueOrAbort.withTypeHintLabel(v.valueOrAbort)) case '{ ($x: JsonConfig).withTypeHintPolicy($v) } => Some(x.valueOrAbort.withTypeHintPolicy(v.valueOrAbort)) @@ -180,7 +164,6 @@ object JsonConfig x match case TryPolicy.AS_NULL => '{ TryPolicy.AS_NULL } case TryPolicy.ERR_MSG_STRING => '{ TryPolicy.ERR_MSG_STRING } - case TryPolicy.NO_WRITE => '{ TryPolicy.NO_WRITE } case TryPolicy.THROW_EXCEPTION => '{ TryPolicy.THROW_EXCEPTION } } @@ -190,7 +173,6 @@ object JsonConfig case EitherLeftPolicy.AS_VALUE => '{ EitherLeftPolicy.AS_VALUE } case EitherLeftPolicy.AS_NULL => '{ EitherLeftPolicy.AS_NULL } case EitherLeftPolicy.ERR_MSG_STRING => '{ EitherLeftPolicy.ERR_MSG_STRING } - case EitherLeftPolicy.NO_WRITE => '{ EitherLeftPolicy.NO_WRITE } case EitherLeftPolicy.THROW_EXCEPTION => '{ EitherLeftPolicy.THROW_EXCEPTION } } @@ -207,7 +189,6 @@ object JsonConfig import quotes.reflect.* x match case '{ TryPolicy.AS_NULL } => Some(TryPolicy.AS_NULL) - case '{ TryPolicy.NO_WRITE } => Some(TryPolicy.NO_WRITE) case '{ TryPolicy.ERR_MSG_STRING } => Some(TryPolicy.ERR_MSG_STRING) case '{ TryPolicy.THROW_EXCEPTION } => Some(TryPolicy.THROW_EXCEPTION) } @@ -218,7 +199,6 @@ object JsonConfig x match case '{ EitherLeftPolicy.AS_VALUE } => Some(EitherLeftPolicy.AS_VALUE) case '{ EitherLeftPolicy.AS_NULL } => Some(EitherLeftPolicy.AS_NULL) - case '{ EitherLeftPolicy.NO_WRITE } => Some(EitherLeftPolicy.NO_WRITE) case '{ EitherLeftPolicy.ERR_MSG_STRING } => Some(EitherLeftPolicy.ERR_MSG_STRING) case '{ EitherLeftPolicy.THROW_EXCEPTION } => Some(EitherLeftPolicy.THROW_EXCEPTION) } diff --git a/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala b/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala index 92ed70de..cbc8b0d9 100644 --- a/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala +++ b/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala @@ -27,9 +27,10 @@ case class JsonSource(js: CharSequence): inline def here = js.charAt(i) - inline def retract() = i -= 1 + inline def backspace() = i -= 1 inline def mark() = _mark = i + inline def revertToMark() = i = _mark inline def captureMark(): String = js.subSequence(_mark, i).toString @tailrec @@ -49,7 +50,7 @@ case class JsonSource(js: CharSequence): i += 1 if !(b == ' ' || b == '\n' || b == '\t' || (b | 0x4) == '\r') then if b != t then - retract() + backspace() throw JsonParseError(s"Expected '$t' here", this) else () else expectToken(t) @@ -59,7 +60,7 @@ case class JsonSource(js: CharSequence): readChars(JsonSource.ull, "null") true else - retract() + backspace() false // Enum... @@ -67,7 +68,7 @@ case class JsonSource(js: CharSequence): def expectEnum(): Int | String = readToken() match case t if t >= '0' && t <= '9' => - retract() + backspace() expectInt() case t if t == '"' => val endI = parseString(i) @@ -108,8 +109,8 @@ case class JsonSource(js: CharSequence): else throw new JsonParseError(s"Expected object field name but found '$tt'", this) else if t == '}' then None else - retract() - throw new JsonParseError(s"Expected ',' or '}' but found $t", this) + backspace() + throw new JsonParseError(s"Expected ',' or '}' but found '$t'", this) final def parseObjectKey(fieldNameMatrix: StringMatrix): Int = // returns index of field name or -1 if not found var fi: Int = 0 @@ -136,7 +137,7 @@ case class JsonSource(js: CharSequence): val value = vf() parseMap[K, V](kf, vf, acc + (key -> value), false) case _ if isFirst => - retract() + backspace() val key = kf() expectToken(':') val value = vf() @@ -147,23 +148,21 @@ case class JsonSource(js: CharSequence): // ======================================================= @tailrec - final private def addAllArray[E](s: scala.collection.mutable.ListBuffer[E], f: () => E): scala.collection.mutable.ListBuffer[E] = + final private def addAllArray[E](s: scala.collection.mutable.ListBuffer[E], f: () => E, isFirst: Boolean): scala.collection.mutable.ListBuffer[E] = if i == max then throw JsonParseError("Unexpected end of buffer", this) - s.addOne(f()) val tt = readToken() - if tt == ']' then - retract() - s - else if tt != ',' then throw JsonParseError(s"Expected ',' or ']' got '$tt'", this) - else addAllArray(s, f) + if tt == ']' then s + else if !isFirst && tt != ',' then throw JsonParseError(s"Expected ',' or ']' got '$tt'", this) + else + if isFirst then backspace() + s.addOne(f()) + addAllArray(s, f, false) def expectArray[E](f: () => E): scala.collection.mutable.ListBuffer[E] = val t = readToken() if t == '[' then val seq = scala.collection.mutable.ListBuffer.empty[E] - skipWS() - addAllArray(seq, f) - i += 1 + addAllArray(seq, f, true) seq else if t == 'n' then readChars(JsonSource.ull, "null") @@ -285,7 +284,7 @@ case class JsonSource(js: CharSequence): readChars(JsonSource.ull, "null") null.asInstanceOf[java.lang.Boolean] else - retract() + backspace() throw JsonParseError(s"Expected 'true', 'false', or null here", this) // Characters... @@ -324,12 +323,12 @@ case class JsonSource(js: CharSequence): def expectFloat(): Float = val result = UnsafeNumbers.float_(this, false, 32) - retract() + backspace() result def expectDouble(): Double = val result = UnsafeNumbers.double_(this, false, 64) - retract() + backspace() result def expectNumberOrNull(): String = @@ -341,7 +340,7 @@ case class JsonSource(js: CharSequence): readChars(JsonSource.ull, "null") null else - retract() + backspace() throw new JsonParseError("Expected a numerical value or null here", this) def expectInt(): Int = @@ -351,7 +350,7 @@ case class JsonSource(js: CharSequence): b = readChar() s = 0 if b < '0' || b > '9' then - retract() + backspace() throw JsonParseError("Non-numeric character found when integer value expected", this) var x = '0' - b while { b = readChar(); b >= '0' && b <= '9' } do @@ -364,14 +363,14 @@ case class JsonSource(js: CharSequence): x -= s if (s & x) == -2147483648 then throw JsonParseError("Integer value overflow", this) if (b | 0x20) == 'e' || b == '.' then - retract() + backspace() throw JsonParseError("Decimal digit 'e' or '.' found when integer value expected", this) - retract() + backspace() x def expectLong(): Long = val result = UnsafeNumbers.long_(this, false) - retract() + backspace() result // Skip things... @@ -399,7 +398,7 @@ case class JsonSource(js: CharSequence): @tailrec final def skipNumber(): Unit = - if !isNumber(readChar()) then retract() + if !isNumber(readChar()) then backspace() else skipNumber() @tailrec diff --git a/src/main/scala/co.blocke.scalajack/json/reading/Numbers.scala b/src/main/scala/co.blocke.scalajack/json/reading/Numbers.scala index aafeda44..0dc05c95 100644 --- a/src/main/scala/co.blocke.scalajack/json/reading/Numbers.scala +++ b/src/main/scala/co.blocke.scalajack/json/reading/Numbers.scala @@ -589,7 +589,7 @@ object UnsafeNumbers { } if !isDigit(current) then - in.retract() + in.backspace() throw JsonParseError("Unexpected character in Int/Long value: " + current.toChar, in) var accum: Long = 0L @@ -632,7 +632,7 @@ object UnsafeNumbers { while i < len do { current = in.readChar() if current != s(i) then - in.retract() + in.backspace() throw JsonParseError("Unexpected character in Int/Long value: " + current.toChar, in) i += 1 } @@ -817,7 +817,7 @@ object UnsafeNumbers { } if sig < 0 then - in.retract() + in.backspace() throw JsonParseError("Malformed Float/Double/BigDecimal", in) // no significand if current == 'E' || current == 'e' then exp = int_(in, consume) diff --git a/src/main/scala/co.blocke.scalajack/json/writing/AnyWriter.scala b/src/main/scala/co.blocke.scalajack/json/writing/AnyWriter.scala index 45303ead..ecb3243a 100644 --- a/src/main/scala/co.blocke.scalajack/json/writing/AnyWriter.scala +++ b/src/main/scala/co.blocke.scalajack/json/writing/AnyWriter.scala @@ -144,7 +144,6 @@ object AnyWriter: case Failure(e) => cfg.tryFailureHandling match case TryPolicy.AS_NULL => Some("null") - case TryPolicy.NO_WRITE => None case TryPolicy.ERR_MSG_STRING => Some("Try Failure with msg: " + e.getMessage()) case TryPolicy.THROW_EXCEPTION => throw e @@ -152,7 +151,6 @@ object AnyWriter: cfg.eitherLeftHandling match case EitherLeftPolicy.AS_VALUE => Some(v) case EitherLeftPolicy.AS_NULL => Some("null") - case EitherLeftPolicy.NO_WRITE => None case EitherLeftPolicy.ERR_MSG_STRING => Some("Left Error: " + v.toString) case EitherLeftPolicy.THROW_EXCEPTION => throw new JsonEitherLeftError("Left Error: " + v.toString) case Some(v) => isOkToWrite(v, cfg) diff --git a/src/main/scala/co.blocke.scalajack/json/writing/JsonOutput.scala b/src/main/scala/co.blocke.scalajack/json/writing/JsonOutput.scala index 60c2cecb..108c39ef 100644 --- a/src/main/scala/co.blocke.scalajack/json/writing/JsonOutput.scala +++ b/src/main/scala/co.blocke.scalajack/json/writing/JsonOutput.scala @@ -14,6 +14,7 @@ case class JsonOutput(): private var comma: Boolean = false private var savePoint: Int = 0 + private var saveComma: Boolean = false def result = internal.result @@ -21,13 +22,16 @@ case class JsonOutput(): internal.clear() comma = false savePoint = 0 + saveComma = false this def mark() = savePoint = internal.length + saveComma = comma def revert() = // delete everything after the set savePoint internal.setLength(savePoint) + comma = saveComma inline def startObject(): Unit = maybeComma() @@ -60,6 +64,7 @@ case class JsonOutput(): internal.append("null") comma = true + // Problem: for unions, if left fails to write, comma is not reset to true for the attempt to write right side inline def label(s: String): Unit = maybeComma() internal.append('"') diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index 07a0adb7..0abdb81f 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -74,11 +74,25 @@ object RunMe extends App: // val c: Pizza = ScalaJack[Pizza].fromJson("\"READY\"") // println("Pizza: " + c) - opaque type OnOff = Boolean - opaque type Counter = Short - - val m: Map[OnOff, Counter] = Map(true.asInstanceOf[OnOff] -> 1.asInstanceOf[Counter]) - val n: Map[Boolean, Short] = m.asInstanceOf[Map[Boolean, Short]] - println(n) + implicit val blah: ScalaJack[Decide] = sjCodecOf[Decide] + // 012345678 + val c: Decide = ScalaJack[Decide].fromJson("""{"a":[1,2,3]}""") + println(c) println("done.") + + /* + + Option[Left(5)] -> None + + Either[Err,Option[String]] + + Left-Policy Class Field Option-Wrapped In Collection In Tuple + ---------------- -------------- --------------- --------------- ------------ + NO_WRITE Null None () Null <-- KILL NO_WRITE!! A symantic mess! + AS_VALUE Value Value Value Value + AS_NULL Null Null Null Null + ERR_MSG_STRING Err string Err String Err String Err String + THROW_EXCEPTION Throw Exception Throw Exception Throw Exception Throw Excpeiton + + */ diff --git a/src/main/scala/co.blocke.scalajack/run/Record.scala b/src/main/scala/co.blocke.scalajack/run/Record.scala index 05c5d49e..23eea00c 100644 --- a/src/main/scala/co.blocke.scalajack/run/Record.scala +++ b/src/main/scala/co.blocke.scalajack/run/Record.scala @@ -100,7 +100,7 @@ case class Person2(age: XList) case class Foom(a: schema.Schema) -case class Group(t: (Int, String, Boolean)) +case class Decide(a: List[Int]) sealed trait Candy: val isSweet: Boolean diff --git a/src/test/scala/co.blocke.scalajack/json/collections/MapSpec.scala b/src/test/scala/co.blocke.scalajack/json/collections/MapSpec.scala index f02d316a..6b35e11f 100644 --- a/src/test/scala/co.blocke.scalajack/json/collections/MapSpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/collections/MapSpec.scala @@ -20,94 +20,94 @@ class MapSpec() extends AnyFunSpec with JsonMatchers: val sj = sjCodecOf[MapHolder[Int, Int]] val js = sj.toJson(inst) js should matchJson("""{"a":null}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Map key of string must work") { val inst = MapHolder[String, Int](Map("x" -> 1, "y" -> 2)) val sj = sjCodecOf[MapHolder[String, Int]] val js = sj.toJson(inst) js should matchJson("""{"a":{"x":1,"y":2}}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Map key of long must work") { val inst = MapHolder[Long, Int](Map(15L -> 1, 25L -> 2)) val sj = sjCodecOf[MapHolder[Long, Int]] val js = sj.toJson(inst) js should matchJson("""{"a":{"15":1,"25":2}}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Map key of boolean must work") { val inst = MapHolder[Boolean, Int](Map(true -> 1, false -> 2)) val sj = sjCodecOf[MapHolder[Boolean, Int]] val js = sj.toJson(inst) js should matchJson("""{"a":{"true":1,"false":2}}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Map key of uuid must work") { val inst = MapHolder[UUID, String](Map(UUID.fromString("1b9ab03f-26a3-4ec5-a8dd-d5122ff86b03") -> "x", UUID.fromString("09abdeb1-8b07-4683-8f97-1f5621696008") -> "y")) val sj = sjCodecOf[MapHolder[UUID, String]] val js = sj.toJson(inst) js should matchJson("""{"a":{"1b9ab03f-26a3-4ec5-a8dd-d5122ff86b03":"x","09abdeb1-8b07-4683-8f97-1f5621696008":"y"}}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Map value of string must work") { val inst = MapHolder[String, String](Map("w" -> "x", "y" -> "z")) val sj = sjCodecOf[MapHolder[String, String]] val js = sj.toJson(inst) js should matchJson("""{"a":{"w":"x","y":"z"}}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Map value of long must work") { val inst = MapHolder[String, Long](Map("w" -> 3L, "y" -> 4L)) val sj = sjCodecOf[MapHolder[String, Long]] val js = sj.toJson(inst) js should matchJson("""{"a":{"w":3,"y":4}}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Map value of boolean must work") { val inst = MapHolder[String, Boolean](Map("w" -> true, "y" -> false)) val sj = sjCodecOf[MapHolder[String, Boolean]] val js = sj.toJson(inst) js should matchJson("""{"a":{"w":true,"y":false}}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Map key and value of opaque alias type must work") { val inst = MapHolder[OnOff, Counter](Map(true.asInstanceOf[OnOff] -> 1.asInstanceOf[Counter], false.asInstanceOf[OnOff] -> 0.asInstanceOf[Counter])) val sj = sjCodecOf[MapHolder[OnOff, Counter]] val js = sj.toJson(inst) js should matchJson("""{"a":{"true":1,"false":0}}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Map value of uuid must work") { val inst = MapHolder[String, UUID](Map("x" -> UUID.fromString("1b9ab03f-26a3-4ec5-a8dd-d5122ff86b03"), "y" -> UUID.fromString("09abdeb1-8b07-4683-8f97-1f5621696008"))) val sj = sjCodecOf[MapHolder[String, UUID]] val js = sj.toJson(inst) js should matchJson("""{"a":{"x":"1b9ab03f-26a3-4ec5-a8dd-d5122ff86b03","y":"09abdeb1-8b07-4683-8f97-1f5621696008"}}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Map value of Seq must work") { val inst = MapHolder[String, List[Int]](Map("w" -> List(1, 2), "y" -> List(3, 4))) val sj = sjCodecOf[MapHolder[String, List[Int]]] val js = sj.toJson(inst) js should matchJson("""{"a":{"w":[1,2],"y":[3,4]}}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Map value of Map (nested) must work") { val inst = MapHolder[String, Map[String, Int]](Map("w" -> Map("r" -> 3, "t" -> 4), "y" -> Map("s" -> 7, "q" -> 9))) val sj = sjCodecOf[MapHolder[String, Map[String, Int]]] val js = sj.toJson(inst) js should matchJson("""{"a":{"w":{"r":3,"t":4},"y":{"s":7,"q":9}}}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Map value of union type must work") { val inst = MapHolder[String, Int | List[String]](Map("w" -> 3, "y" -> List("wow", "blah"))) val sj = sjCodecOf[MapHolder[String, Int | List[String]]] val js = sj.toJson(inst) js should matchJson("""{"a":{"w":3,"y":["wow","blah"]}}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } /* - * Keys of value class (Distance) must work + * Keys of value class (Distance) must work it("Map value of class must work") { val inst = MapHolder[String, Person](Map("w" -> Person("Bob", 34), "y" -> Person("Sally", 25))) @@ -126,7 +126,7 @@ class MapSpec() extends AnyFunSpec with JsonMatchers: js should matchJson("""{"a":{"w":1.23,"y":4.56}}""") sj.fromJson(js) shouldEqual(inst) } - */ + */ } // describe(colorString("--- Negative Tests ---")) { diff --git a/src/test/scala/co.blocke.scalajack/json/misc/LRSpec.scala b/src/test/scala/co.blocke.scalajack/json/misc/LRSpec.scala index 289f5df7..8d9016c5 100644 --- a/src/test/scala/co.blocke.scalajack/json/misc/LRSpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/misc/LRSpec.scala @@ -15,97 +15,100 @@ import java.util.UUID class LRSpec() extends AnyFunSpec with JsonMatchers: describe(colorString("-------------------------------\n: Either Tests :\n-------------------------------", Console.YELLOW)) { - it("Complex Either/Option must work (non-None)") { - val inst = ComplexEither[Int](Some(Right(Some(3)))) - val sj = sjCodecOf[ComplexEither[Int]] - val js = sj.toJson(inst) - js should matchJson("""{"a":3}""") - sj.fromJson(js) shouldEqual (inst) - } - it("Complex Either/Option must work (None no-write default)") { - val inst = ComplexEither[Int](Some(Right(None))) - val sj = sjCodecOf[ComplexEither[Int]] - val js = sj.toJson(inst) - js should matchJson("""{}""") - sj.fromJson(js) shouldEqual (ComplexEither(null)) - } - it("Complex Either/Option must work (NoneAsNull)") { - val inst = ComplexEither[Int](Some(Right(None))) - val sj = sjCodecOf[ComplexEither[Int]](JsonConfig.withNoneAsNull()) - val js = sj.toJson(inst) - js should matchJson("""{"a":null}""") - sj.fromJson(js) shouldEqual (ComplexEither(None)) // None here because value existed, but was null with NoneAsNull - } - it("Complex Either/Option must work (Left-NO_WRITE)") { - val inst = ComplexEither[Int](Some(Left("err"))) - val sj = sjCodecOf[ComplexEither[Int]] - val js = sjCodecOf[ComplexEither[Int]].toJson(inst) - js should matchJson("""{}""") - sj.fromJson(js) shouldEqual (ComplexEither(null)) // Null because value didn't exist at all - } - it("Complex Either/Option must work (Left-AS_VALUE)") { - val inst = ComplexEither[Int](Some(Left("err"))) - val sj = sjCodecOf[ComplexEither[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)) - val js = sj.toJson(inst) - js should matchJson("""{"a":"err"}""") - sj.fromJson(js) shouldEqual (inst) - } - it("Either with AS_VALUE left policy must work") { - val inst = EitherHolder[Int](Left(5), Right(3)) - val sj = sjCodecOf[EitherHolder[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)) - val js = sj.toJson(inst) - js should matchJson("""{"a":5,"b":3}""") - sj.fromJson(js) shouldEqual (inst) - } - it("Either with AS_NULL left policy must work") { - val inst = EitherHolder[Int](Left(5), Right(3)) - val sj = sjCodecOf[EitherHolder[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_NULL)) - val js = sj.toJson(inst) - js should matchJson("""{"a":null,"b":3}""") - sj.fromJson(js) shouldEqual (EitherHolder(null, Right(3))) - } - it("Either with NO_WRITE left policy must work") { - val inst = EitherHolder[Int](Left(5), Right(3)) - val sj = sjCodecOf[EitherHolder[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.NO_WRITE)) - val js = sj.toJson(inst) - js should matchJson("""{"b":3}""") - // This js cannot be read back in becuase it's missing required field 'a', which wasn't written out - // per NO_WRITE policy. This is a 1-way trip... so be advised... - } - it("Either with ERR_MSG_STRING left policy must work") { - val inst = EitherHolder[Int](Left(5), Right(3)) - val sj = sjCodecOf[EitherHolder[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.ERR_MSG_STRING)) - val js = sj.toJson(inst) - js should matchJson("""{"a":"Left Error: 5","b":3}""") - sj.fromJson(js) shouldEqual (EitherHolder(Right("Left Error: 5"), Right(3))) - // WARNING! Here a Left(err_msg) was "promoted" to a Right(String) upon read, because Right was of type - // String, and "Left Error: 5" is a valid string. Use with extreme caution. Best to consider this a 1-way - // trip for debugging purposes only. You have been warned. + describe(colorString("""/// Right Tests ///""")) { + it("Complex Either/Option must work (non-None)") { + val inst = ComplexEither[Int](Some(Right(Some(3)))) + val sj = sjCodecOf[ComplexEither[Int]] + val js = sj.toJson(inst) + js should matchJson("""{"a":3}""") + sj.fromJson(js) shouldEqual (inst) + } + it("Complex Either/Option must work (None -> null for Either)") { + val inst = ComplexEither[Int](Some(Right(None))) + val sj = sjCodecOf[ComplexEither[Int]] + val js = sj.toJson(inst) + js should matchJson("""{}""") + sj.fromJson(js) shouldEqual (ComplexEither(None)) + } + it("Complex Either/Option must work (NoneAsNull)") { + val inst = ComplexEither[Int](Some(Right(None))) + val sj = sjCodecOf[ComplexEither[Int]](JsonConfig.withNoneAsNull()) + val js = sj.toJson(inst) + js should matchJson("""{"a":null}""") // same output result regardless of noneAsNull setting + sj.fromJson(js) shouldEqual (ComplexEither(None)) // None here because value existed, but was null with NoneAsNull + } } - it("Either with THROW_EXCEPTION left policy must work") { - val inst = EitherHolder[Int](Left(5), Right(3)) - val caught = - intercept[JsonEitherLeftError] { - sjCodecOf[EitherHolder[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.THROW_EXCEPTION)).toJson(inst) - } - assert(caught.getMessage == "Left Error: 5") + describe(colorString("""\\\ Left Tests \\\""")) { + it("Complex Either/Option must work (Left-default: AS_VALUE)") { + val inst = ComplexEither[Int](Some(Left("msg"))) + val sj = sjCodecOf[ComplexEither[Int]] + val js = sjCodecOf[ComplexEither[Int]].toJson(inst) + js should matchJson("""{"a":"msg"}""") + sj.fromJson(js) shouldEqual inst + } + it("Complex Either/Option must work (Left-AS_NULL)") { + val inst = ComplexEither[Int](Some(Left("err"))) + val sj = sjCodecOf[ComplexEither[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_NULL)) + val js = sj.toJson(inst) + js should matchJson("""{"a":null}""") + sj.fromJson(js) shouldEqual (ComplexEither(Some(null))) + } + it("Complex Either/Option must work (Left-AS_NULL, Option nullAsNull)") { + val inst = ComplexEither[Int](Some(Left("err"))) + val sj = sjCodecOf[ComplexEither[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_NULL).withNoneAsNull()) + val js = sj.toJson(inst) + js should matchJson("""{"a":null}""") + sj.fromJson(js) shouldEqual (ComplexEither(None)) + } + it("Either with AS_VALUE (default) left policy must work") { + val inst = EitherHolder[Int](Left(5), Right(3)) + val sj = sjCodecOf[EitherHolder[Int]] + val js = sj.toJson(inst) + js should matchJson("""{"a":5,"b":3}""") + sj.fromJson(js) shouldEqual (inst) + } + it("Either with AS_NULL left policy must work") { + val inst = EitherHolder[Int](Left(5), Right(3)) + val sj = sjCodecOf[EitherHolder[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_NULL)) + val js = sj.toJson(inst) + js should matchJson("""{"a":null,"b":3}""") + sj.fromJson(js) shouldEqual (EitherHolder(null, Right(3))) + } + it("Either with ERR_MSG_STRING left policy must work") { + val inst = EitherHolder[Int](Left(5), Right(3)) + val sj = sjCodecOf[EitherHolder[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.ERR_MSG_STRING)) + val js = sj.toJson(inst) + js should matchJson("""{"a":"Left Error: 5","b":3}""") + sj.fromJson(js) shouldEqual (EitherHolder(Right("Left Error: 5"), Right(3))) + // WARNING! Here a Left(err_msg) was "promoted" to a Right(String) upon read, because Right was of type + // String, and "Left Error: 5" is a valid string. Use with extreme caution. Best to consider this a 1-way + // trip for debugging purposes only. You have been warned. + } + it("Either with THROW_EXCEPTION left policy must work") { + val inst = EitherHolder[Int](Left(5), Right(3)) + val caught = + intercept[JsonEitherLeftError] { + sjCodecOf[EitherHolder[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.THROW_EXCEPTION)).toJson(inst) + } + assert(caught.getMessage == "Left Error: 5") + } } } describe(colorString("----------------------------------\n: Union Tests :\n----------------------------------", Console.YELLOW)) { it("LR (union) must work with Option (non-None)") { - val inst = LRUnionHolder[Option[Int], String](List(Some(5), "x"), ("y", Some(10))) - val sj = sjCodecOf[LRUnionHolder[Option[Int], String]] - val js = sj.toJson(inst) - js should matchJson("""{"a":[5,"x"],"b":["y",10]}""") - sj.fromJson(js) shouldEqual inst + val inst = LRUnionHolder[Option[Int], String](List(Some(5), "x"), ("y", Some(10))) + val sj = sjCodecOf[LRUnionHolder[Option[Int], String]] + val js = sj.toJson(inst) + js should matchJson("""{"a":[5,"x"],"b":["y",10]}""") + sj.fromJson(js) shouldEqual inst } it("LR (union) must work with Option (None)") { - val inst = LRUnionHolder[Option[Int], String](List(None, "x"), ("y", None)) - val sj = sjCodecOf[LRUnionHolder[Option[Int], String]] - val js = sj.toJson(inst) - js should matchJson("""{"a":["x"],"b":["y",null]}""") - sj.fromJson(js) shouldEqual LRUnionHolder[Option[Int], String](List("x"), ("y", None)) + val inst = LRUnionHolder[Option[Int], String](List(None, "x"), ("y", None)) + val sj = sjCodecOf[LRUnionHolder[Option[Int], String]] + val js = sj.toJson(inst) + js should matchJson("""{"a":["x"],"b":["y",null]}""") + sj.fromJson(js) shouldEqual LRUnionHolder[Option[Int], String](List("x"), ("y", None)) } // it("LR (union) must work with Try of Option (non-None)") { // val inst = LRHolder[Try[Option[Int]], String](List(Success(Some(5)), "x"), ("y", Success(Some(10)))) @@ -122,4 +125,4 @@ class LRSpec() extends AnyFunSpec with JsonMatchers: // val js = sj[LRHolder[Try[Option[Int]], String]].toJson(inst) // js should matchJson("""{"a":["x"],"b":["y",null]}""") // } - } \ No newline at end of file + } diff --git a/src/test/scala/co.blocke.scalajack/json/misc/Model.scala b/src/test/scala/co.blocke.scalajack/json/misc/Model.scala index 8d1c122e..f140bbaa 100644 --- a/src/test/scala/co.blocke.scalajack/json/misc/Model.scala +++ b/src/test/scala/co.blocke.scalajack/json/misc/Model.scala @@ -28,6 +28,7 @@ case class LRUnionHolder[T, U](a: Seq[T | U], b: (T | U, T | U)) case class EitherHolder[T](a: Either[T, String], b: Either[String, T]) case class ComplexEither[T](a: Option[Either[String, Option[T]]]) +case class EitherRecipe[T](a: Either[Boolean, Either[Option[T], String]]) case class AliasHolder[T](a: T, b: List[T], c: Map[T, String], d: Map[String, T]) case class AliasHolder2[T](a: T, b: List[T], c: Map[String, T]) diff --git a/src/test/scala/co.blocke.scalajack/json/misc/OptionSpec.scala b/src/test/scala/co.blocke.scalajack/json/misc/OptionSpec.scala index fabcda59..0213d0fe 100644 --- a/src/test/scala/co.blocke.scalajack/json/misc/OptionSpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/misc/OptionSpec.scala @@ -12,10 +12,9 @@ import TestUtil.* import java.util.UUID -class OptionTrySpec() extends AnyFunSpec with JsonMatchers: +class OptionSpec() extends AnyFunSpec with JsonMatchers: describe(colorString("-------------------------------\n: Option Tests :\n-------------------------------", Console.YELLOW)) { - /* it("Non-empty Options must work") { val inst = OptionHolder[Int]( Some(5), // straight Option @@ -29,8 +28,11 @@ class OptionTrySpec() extends AnyFunSpec with JsonMatchers: Right(Some(15)), // Either of Option (R) Left(Some(-3)) // Either of Option (L) ) - val js = sjCodecOf[OptionHolder[Int]].toJson(inst) - js should matchJson("""{"a":5,"b":[1,"ok"],"c":[1,3],"d":{"1":2,"3":4},"e":99,"f":100,"g":0,"h":{"name":"BoB","age":34},"i":15}""") + val sj = sjCodecOf[OptionHolder[Int]] + val js = sj.toJson(inst) + js should matchJson("""{"a":5,"b":[1,"ok"],"c":[1,3],"d":{"1":2,"3":4},"e":99,"f":100,"g":0,"h":{"name":"BoB","age":34},"i":15,"j":-3}""") + // Some fields get changed/"promoted" when read back in per policy, as Either reads "best-fit" starting with Right value first + sj.fromJson(js) shouldEqual (inst.copy(c = List(Some(1), Some(3))).copy(e = 99).copy(j = Right(-3))) } it("Empty Options must work (default)") { val inst = OptionHolder[Int]( @@ -45,8 +47,10 @@ class OptionTrySpec() extends AnyFunSpec with JsonMatchers: Right(None), // Either of Option (R) Left(None) // Either of Option (L) ) - val js = sjCodecOf[OptionHolder[Int]].toJson(inst) + val sj = sjCodecOf[OptionHolder[Int]] + val js = sj.toJson(inst) js should matchJson("""{"b":[null,"ok"],"c":[],"d":{}}""") + sj.fromJson(js) shouldEqual (inst.copy(c = List(), d = Map(), g = None)) } it("Empty Options must work (config noneAsNull = true)") { val inst = OptionHolder[Int]( @@ -61,13 +65,40 @@ class OptionTrySpec() extends AnyFunSpec with JsonMatchers: Right(None), // Either of Option (R) Left(None) // Either of Option (L) ) - val js = sj[OptionHolder[Int]]( - JsonConfig.withNoneAsNull().withEitherLeftHandling(EitherLeftPolicy.AS_VALUE) - ).toJson(inst) + val sj = sjCodecOf[OptionHolder[Int]]( + JsonConfig.withNoneAsNull() + ) + val js = sj.toJson(inst) js should matchJson("""{"a":null,"b":[null,"ok"],"c":[null,null,null],"d":{"1":null,"3":null},"e":null,"f":null,"g":null,"h":null,"i":null,"j":null}""") + sj.fromJson(js) shouldEqual (inst.copy(g = None, i = null, j = null)) + } + it("Either recipe should work (non-None)") { + val inst = EitherRecipe[Int](Right(Left(Some(5)))) + val sj = sjCodecOf[EitherRecipe[Int]] + val js = sj.toJson(inst) + js should matchJson("""{"a":5}""") + sj.fromJson(js) shouldEqual (inst) + } + it("Either recipe should work (None)") { + val inst = EitherRecipe[Int](Right(Left(None))) + val sj = sjCodecOf[EitherRecipe[Int]] + val js = sj.toJson(inst) + js should matchJson("""{}""") + sj.fromJson(js) shouldEqual (inst) } + it("Either recipe should work (None as null)") { + val inst = EitherRecipe[Int](Right(Left(None))) + val sj = sjCodecOf[EitherRecipe[Int]](JsonConfig.withNoneAsNull()) + val js = sj.toJson(inst) + js should matchJson("""{"a":null}""") + sj.fromJson(js) shouldEqual (EitherRecipe[Int](null)) + } + + /* + TODO: Java Optional flavor... + it("Java Optional must work") { ??? } - */ + */ } diff --git a/src/test/scala/co.blocke.scalajack/json/primitives/ScalaPrimSpec.scala b/src/test/scala/co.blocke.scalajack/json/primitives/ScalaPrimSpec.scala index 5968f70d..49ede9e6 100644 --- a/src/test/scala/co.blocke.scalajack/json/primitives/ScalaPrimSpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/primitives/ScalaPrimSpec.scala @@ -204,7 +204,7 @@ class ScalaPrimSpec() extends AnyFunSpec with JsonMatchers: val js = """{"d1":1.79769313486E23157E308,"d2":-1.7976931348623157E308,"d3":0.0,"d4":-123.4567}""" val msg = - """Expected ',' or '}' but found E at position [25] + """Expected ',' or '}' but found 'E' at position [25] |{"d1":1.79769313486E23157E308,"d2":-1.7976931348623157E308,"d3":0.0,"d4":-123... |-------------------------^""".stripMargin val ex = intercept[JsonParseError](sjCodecOf[SampleDouble].fromJson(js)) @@ -251,7 +251,7 @@ class ScalaPrimSpec() extends AnyFunSpec with JsonMatchers: val js2 = """{"l1":9223372036854775807,"l2":-9223372036854775808,"l3":0.3,"l4":123}""" val msg2 = - """Expected ',' or '}' but found . at position [58] + """Expected ',' or '}' but found '.' at position [58] |...3372036854775807,"l2":-9223372036854775808,"l3":0.3,"l4":123} |----------------------------------------------------^""".stripMargin val ex2 = intercept[JsonParseError](sj.fromJson(js2)) From 101807a3c8036e26cbde9247c7925e3eabb1ec49 Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Fri, 5 Apr 2024 22:09:44 -0500 Subject: [PATCH 53/65] checkpoint-lots of fixes and problems solved --- .../json/JsonCodecMaker.scala | 431 +++++++++++++++--- .../co.blocke.scalajack/json/package.scala | 14 +- .../json/reading/JsonSource.scala | 18 +- .../scala/co.blocke.scalajack/run/Play.scala | 15 +- .../co.blocke.scalajack/run/Record.scala | 4 +- ...JavaCollSpec.scalax => JavaCollSpec.scala} | 40 +- .../json/collections/MapSpec.scala | 58 ++- .../json/collections/Model.scala | 6 +- .../{AliasSpec.scalax => AliasSpec.scala} | 23 +- .../json/misc/LRSpec.scala | 37 +- .../co.blocke.scalajack/json/misc/Model.scala | 15 + .../json/misc/OptionSpec.scala | 242 ++++++---- .../misc/{TrySpec.scalax => TrySpec.scala} | 62 ++- 13 files changed, 717 insertions(+), 248 deletions(-) rename src/test/scala/co.blocke.scalajack/json/collections/{JavaCollSpec.scalax => JavaCollSpec.scala} (76%) rename src/test/scala/co.blocke.scalajack/json/misc/{AliasSpec.scalax => AliasSpec.scala} (61%) rename src/test/scala/co.blocke.scalajack/json/misc/{TrySpec.scalax => TrySpec.scala} (50%) diff --git a/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala b/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala index 75b56595..3507fd54 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala @@ -17,6 +17,7 @@ import dotty.tools.dotc.ast.Trees.EmptyTree import org.apache.commons.text.StringEscapeUtils import org.apache.commons.lang3.text.translate.CharSequenceTranslator import dotty.tools.dotc.core.TypeComparer.AnyConstantType +import scala.jdk.CollectionConverters.* object JsonCodecMaker: @@ -106,11 +107,12 @@ object JsonCodecMaker: def testValidMapKey(testRef: RTypeRef[?]): Boolean = val isValid = testRef match - case _: PrimitiveRef => true - case _: TimeRef => true - case _: NetRef => true - case a: AliasRef[?] => testValidMapKey(a.unwrappedType) - case _ => false + case _: PrimitiveRef => true + case _: TimeRef => true + case _: NetRef => true + case c: ScalaClassRef[?] if c.isValueClass => true + case a: AliasRef[?] => testValidMapKey(a.unwrappedType) + case _ => false if !isValid then throw new JsonTypeError(s"For JSON serialization, map keys must be a simple type. ${testRef.name} is too complex.") isValid @@ -260,7 +262,7 @@ object JsonCodecMaker: // --------------------------------------------------------------------------------------------- - def genEncFnBody[T](r: RTypeRef[?], aE: Expr[T], out: Expr[JsonOutput], emitDiscriminator: Boolean = false, inTuple: Boolean = false)(using Quotes): Expr[Unit] = + def genEncFnBody[T](r: RTypeRef[?], aE: Expr[T], out: Expr[JsonOutput], emitDiscriminator: Boolean = false, inTuple: Boolean = false, isMapKey: Boolean = false)(using Quotes): Expr[Unit] = r.refType match case '[b] => r match @@ -336,7 +338,7 @@ object JsonCodecMaker: theField.refType match case '[e] => val fieldValue = Select.unique(aE.asTerm, t.fields.head.name).asExprOf[e] - genWriteVal(fieldValue, theField.asInstanceOf[RTypeRef[e]], out) + genWriteVal(fieldValue, theField.asInstanceOf[RTypeRef[e]], out, isMapKey = isMapKey) case t: ScalaClassRef[?] => makeWriteFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => @@ -547,22 +549,37 @@ object JsonCodecMaker: } // No makeWriteFn here--Option is just a wrapper to the real thingy - case t: OptionRef[?] => + case t: ScalaOptionRef[?] => t.optionParamType.refType match case '[e] => val tin = aE.asExprOf[b] '{ - if $tin == null then $out.burpNull() - else - $tin match - case None => - ${ - if cfg.noneAsNull || inTuple then '{ $out.burpNull() } - else '{ () } - } - case Some(v) => - val vv = v.asInstanceOf[e] - ${ genWriteVal[e]('{ vv }, t.optionParamType.asInstanceOf[RTypeRef[e]], out) } + $tin match + case null => $out.burpNull() + case None => + ${ + if cfg.noneAsNull || inTuple then '{ $out.burpNull() } + else '{ () } + } + case Some(v) => + val vv = v.asInstanceOf[e] + ${ genWriteVal[e]('{ vv }, t.optionParamType.asInstanceOf[RTypeRef[e]], out) } + } + case t: JavaOptionalRef[?] => + t.optionParamType.refType match + case '[e] => + val tin = aE.asExprOf[b] + '{ + $tin.asInstanceOf[java.util.Optional[e]] match + case null => $out.burpNull() + case o if o.isEmpty => + ${ + if cfg.noneAsNull || inTuple then '{ $out.burpNull() } + else '{ () } + } + case o => + val vv = o.get().asInstanceOf[e] + ${ genWriteVal[e]('{ vv }, t.optionParamType.asInstanceOf[RTypeRef[e]], out) } } // No makeWriteFn here... SelfRef is referring to something we've already seen before. There absolutely should already be a geneated @@ -839,20 +856,35 @@ object JsonCodecMaker: // Everything else... // case _ if isStringified => throw new JsonIllegalKeyType("Non-primitive/non-simple types cannot be map keys") - case _ => genEncFnBody(ref, aE, out, inTuple = inTuple) + case _ => genEncFnBody(ref, aE, out, inTuple = inTuple, isMapKey = isMapKey) ) // --------------------------------------------------------------------------------------------- - def lrHasOptionChild(lr: LeftRightRef[?]): String = + def lrHasOptionChild(lr: LeftRightRef[?]): (String, Language) = lr.rightRef match - case t: OptionRef[?] => "r" - case t: LeftRightRef[?] => "r" + lrHasOptionChild(t) + case t: ScalaOptionRef[?] => ("r", Language.Scala) + case t: JavaOptionalRef[?] => ("r", Language.Java) + case t: LeftRightRef[?] => + val (recipe, lang) = lrHasOptionChild(t) + ("r" + recipe, lang) case _ => lr.leftRef match - case t: OptionRef[?] => "l" - case t: LeftRightRef[?] => "l" + lrHasOptionChild(t) - case _ => "" + case t: ScalaOptionRef[?] => ("l", Language.Scala) + case t: JavaOptionalRef[?] => ("l", Language.Java) + case t: LeftRightRef[?] => + val (recipe, lang) = lrHasOptionChild(t) + ("l" + recipe, lang) + case _ => ("", Language.Scala) + + def tryHasOptionChild(t: TryRef[?]): (Boolean, Language) = + t.tryRef match + case o: ScalaOptionRef[?] => (true, Language.Scala) + case o: JavaOptionalRef[?] => (true, Language.Java) + case lr: LeftRightRef[?] => + val (recipe, lang) = lrHasOptionChild(lr) + (recipe.length > 0, lang) + case _ => (false, Language.Scala) def genDecFnBody[T: Type](r: RTypeRef[?], in: Expr[JsonSource])(using Quotes): Expr[Unit] = import quotes.reflect.* @@ -864,6 +896,7 @@ object JsonCodecMaker: r.refType match // refType is Type[r.R] case '[b] => r match + case t: ScalaClassRef[?] => makeReadFn[T](MethodKey(t, false), in)(in => val fieldNames = Expr(t.fields.map(_.name).toArray) @@ -896,29 +929,41 @@ object JsonCodecMaker: else throw new JsonParseError("Duplicate field " + $fieldName, $in) }.asTerm ) - // if dvMembers.isEmpty then (ValDef(sym, Some(oneField.fieldRef.unitVal.asTerm)), caseDef, fieldSymRef) if dvMembers.isEmpty then // no default... required? Not if Option/Optional, or a collection val unitVal = oneField.fieldRef match { case _: OptionRef[?] => oneField.fieldRef.unitVal.asTerm // not required case r: LeftRightRef[?] if r.lrkind == LRKind.EITHER => // maybe required - val optionRecipe = lrHasOptionChild(r) + val (optionRecipe, lang) = lrHasOptionChild(r) if optionRecipe.length == 0 then required = required | math.pow(2, oneField.index).toInt // required oneField.fieldRef.unitVal.asTerm else val recipeE = Expr(optionRecipe) - '{ - $recipeE.foldRight(None: Any)((c, acc) => if c == 'r' then Right(acc) else Left(acc)).asInstanceOf[f] - }.asTerm + if lang == Language.Scala then + '{ + $recipeE.foldRight(None: Any)((c, acc) => if c == 'r' then Right(acc) else Left(acc)).asInstanceOf[f] + }.asTerm + else + '{ + $recipeE.foldRight(java.util.Optional.empty: Any)((c, acc) => if c == 'r' then Right(acc) else Left(acc)).asInstanceOf[f] + }.asTerm case r: LeftRightRef[?] => // maybe required - val optionRecipe = lrHasOptionChild(r) + val (optionRecipe, lang) = lrHasOptionChild(r) if optionRecipe.length == 0 then // no Option children -> required required = required | math.pow(2, oneField.index).toInt // required oneField.fieldRef.unitVal.asTerm else // at least one Option child -> optional - '{ None }.asTerm + if lang == Language.Scala then '{ None }.asTerm + else '{ java.util.Optional.empty.asInstanceOf[f] }.asTerm + case y: TryRef[?] => + tryHasOptionChild(y) match + case (true, Language.Scala) => '{ Success(None) }.asTerm + case (true, Language.Java) => '{ Success(java.util.Optional.empty).asInstanceOf[f] }.asTerm + case _ => + required = required | math.pow(2, oneField.index).toInt // required + oneField.fieldRef.unitVal.asTerm case _ => required = required | math.pow(2, oneField.index).toInt // required oneField.fieldRef.unitVal.asTerm @@ -1225,9 +1270,9 @@ object JsonCodecMaker: // Special check for RawJson pseudo-type if lastPart(t.definedType) == "RawJson" then '{ - $in.mark() + val mark = $in.pos $in.skipValue() - $in.captureMark() + $in.captureMark(mark) }.asExprOf[T] else t.unwrappedType.refType match @@ -1246,7 +1291,7 @@ object JsonCodecMaker: // -------------------- // Options... // -------------------- - case t: OptionRef[?] => + case t: ScalaOptionRef[?] => import quotes.reflect.* t.optionParamType.refType match case '[e] => @@ -1255,7 +1300,26 @@ object JsonCodecMaker: if $in.expectNull() then None else Some(${ genReadVal[e](t.optionParamType.asInstanceOf[RTypeRef[e]], in) }) }.asExprOf[T] - else ofOption[e](Some(genReadVal[e](t.optionParamType.asInstanceOf[RTypeRef[e]], in))).asExprOf[T] + else + '{ + if $in.expectNull() then null + else ${ ofOption[e](Some(genReadVal[e](t.optionParamType.asInstanceOf[RTypeRef[e]], in))).asExprOf[T] } + }.asExprOf[T] + case t: JavaOptionalRef[?] => + import quotes.reflect.* + t.optionParamType.refType match + case '[e] => + if cfg.noneAsNull || inTuple then + '{ + if $in.expectNull() then java.util.Optional.empty + else java.util.Optional.of(${ genReadVal[e](t.optionParamType.asInstanceOf[RTypeRef[e]], in) }) + }.asExprOf[T] + else + '{ + if $in.expectNull() then null + else ${ ofOptional[e](java.util.Optional.of(genReadVal[e](t.optionParamType.asInstanceOf[RTypeRef[e]], in))).asExprOf[T] } + }.asExprOf[T] + // else ofOption[e](Some(genReadVal[e](t.optionParamType.asInstanceOf[RTypeRef[e]], in))).asExprOf[T] case t: LeftRightRef[?] if t.lrkind == LRKind.EITHER => import quotes.reflect.* @@ -1264,14 +1328,14 @@ object JsonCodecMaker: t.rightRef.refType match case '[r] => '{ - $in.mark() + val mark = $in.pos if $in.expectNull() then null else scala.util.Try(${ genReadVal[r](t.rightRef.asInstanceOf[RTypeRef[r]], in, inTuple) }) match case Success(rval) => Right(rval) case Failure(f) => - $in.revertToMark() + $in.revertToPos(mark) scala.util.Try(${ genReadVal[l](t.leftRef.asInstanceOf[RTypeRef[l]], in, inTuple) }) match case Success(lval) => Left(lval) case Failure(_) => @@ -1286,11 +1350,11 @@ object JsonCodecMaker: t.rightRef.refType match case '[r] => '{ - $in.mark() + val mark = $in.pos scala.util.Try(${ genReadVal[l](t.leftRef.asInstanceOf[RTypeRef[l]], in, true) }) match case Success(lval) => lval case Failure(f) => - $in.revertToMark() + $in.revertToPos(mark) scala.util.Try(${ genReadVal[r](t.rightRef.asInstanceOf[RTypeRef[r]], in, true) }) match case Success(rval) => rval case Failure(_) => @@ -1302,22 +1366,6 @@ object JsonCodecMaker: Intersection: val syntheticTA = taCache.typeAdapterOf[L] syntheticTA.write(t.asInstanceOf[L], writer, out) - - Union: - def read(parser: Parser): L | R = { - val savedReader = parser.mark() - Try(leftTypeAdapter.read(parser)) match { - case Success(leftValue) => leftValue.asInstanceOf[L] - case Failure(_) => // Left parse failed... try Right - parser.revertToMark(savedReader) - Try(rightTypeAdapter.read(parser)) match { - case Success(rightValue) => rightValue.asInstanceOf[R] - case Failure(x) => - parser.backspace() - throw new ScalaJackError( parser.showError(s"Failed to read any values for union type") ) - } - } - } */ // -------------------- @@ -1407,7 +1455,10 @@ syntheticTA.write(t.asInstanceOf[L], writer, out) t.elementRef.refType match case '[e] => val rtypeRef = t.elementRef.asInstanceOf[RTypeRef[e]] + // println("REF: " + rtypeRef) + // LeftRightRef(scala.Union,List(L, R),TryRef(scala.util.Try,List(A),ScalaOptionRef(scala.Option,List(A),IntRef())),StringRef(),UnionRType) '{ + // null val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in, inTuple) }) if parsedArray != null then parsedArray.toSeq else null @@ -1454,23 +1505,227 @@ syntheticTA.write(t.asInstanceOf[L], writer, out) else null }.asExprOf[T] - case t: MapRef[?] => - t.elementRef.refType match - case '[k] => - t.elementRef2.refType match - case '[v] => - testValidMapKey(t.elementRef) + /* + - ArrayBlockingQueue, + + ArrayDeque, + + ArrayList, + + ConcurrentLinkedDeque, + + ConcurrentLinkedQueue, + + ConcurrentSkipListSet, + + CopyOnWriteArrayList, + + CopyOnWriteArraySet, + + DelayQueue, + + HashSet, + + LinkedBlockingDeque, + + LinkedBlockingQueue, + + LinkedHashSet, + + LinkedList, + + LinkedTransferQueue, + + PriorityBlockingQueue, + + PriorityQueue, + - Stack, + + TreeSet, + + Vector + So... 2 special cases. The rest follow a Collection constructor pattern + */ + // -------------------- + // Java Collections... + // -------------------- + case t: JavaCollectionRef[?] => + ref.refType match + case '[java.util.concurrent.ArrayBlockingQueue[?]] => + t.elementRef.refType match + case '[e] => + val rtypeRef = t.elementRef.asInstanceOf[RTypeRef[e]] '{ - if $in.expectNull() then null - else - $in.expectToken('{') - $in.parseMap[k, v]( - () => ${ genReadVal[k](t.elementRef.asInstanceOf[RTypeRef[k]], in, inTuple, true) }, - () => ${ genReadVal[v](t.elementRef2.asInstanceOf[RTypeRef[v]], in, inTuple) }, - Map.empty[k, v], - true - ) + val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in, inTuple) }) + if parsedArray != null then new java.util.concurrent.ArrayBlockingQueue(parsedArray.length, true, parsedArray.toList.asJava) + else null }.asExprOf[T] + case '[java.util.Stack[?]] => + t.elementRef.refType match + case '[e] => + val rtypeRef = t.elementRef.asInstanceOf[RTypeRef[e]] + '{ + val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in, inTuple) }) + if parsedArray != null then + val stack = new java.util.Stack[e]() + parsedArray.map(stack.push(_)) + stack + else null + }.asExprOf[T] + case _ => + t.elementRef.refType match + case '[e] => + val arg = '{ + val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](t.elementRef.asInstanceOf[RTypeRef[e]], in, inTuple) }) + if parsedArray == null then null + else parsedArray.toList.asJava + }.asTerm + Select.overloaded(New(Inferred(TypeRepr.of[T])), "", List(TypeRepr.of[e]), List(arg)).asExprOf[T] + + // -------------------- + // Maps... + // -------------------- + case t: MapRef[?] => + ref.refType match + case '[scala.collection.mutable.HashMap[?, ?]] => + t.elementRef.refType match + case '[k] => + t.elementRef2.refType match + case '[v] => + testValidMapKey(t.elementRef) + '{ + if $in.expectNull() then null + else + $in.expectToken('{') + scala.collection.mutable.HashMap.from( + $in.parseMap[k, v]( + () => ${ genReadVal[k](t.elementRef.asInstanceOf[RTypeRef[k]], in, inTuple, true) }, + () => ${ genReadVal[v](t.elementRef2.asInstanceOf[RTypeRef[v]], in, inTuple) }, + Map.empty[k, v], + true + ) + ) + }.asExprOf[T] + case '[scala.collection.mutable.Map[?, ?]] if ref.name == "scala.collection.mutable.Map" => + t.elementRef.refType match + case '[k] => + t.elementRef2.refType match + case '[v] => + testValidMapKey(t.elementRef) + '{ + if $in.expectNull() then null + else + $in.expectToken('{') + scala.collection.mutable.Map.from( + $in.parseMap[k, v]( + () => ${ genReadVal[k](t.elementRef.asInstanceOf[RTypeRef[k]], in, inTuple, true) }, + () => ${ genReadVal[v](t.elementRef2.asInstanceOf[RTypeRef[v]], in, inTuple) }, + Map.empty[k, v], + true + ) + ) + }.asExprOf[T] + case '[scala.collection.mutable.Map[?, ?]] => // all other mutable Maps + t.elementRef.refType match + case '[k] => + t.elementRef2.refType match + case '[v] => + testValidMapKey(t.elementRef) + '{ + if $in.expectNull() then null + else + $in.expectToken('{') + scala.collection.mutable.Map + .from( + $in.parseMap[k, v]( + () => ${ genReadVal[k](t.elementRef.asInstanceOf[RTypeRef[k]], in, inTuple, true) }, + () => ${ genReadVal[v](t.elementRef2.asInstanceOf[RTypeRef[v]], in, inTuple) }, + Map.empty[k, v], + true + ) + ) + .to(${ Expr.summon[Factory[(k, v), T]].get }) + }.asExprOf[T] + case '[scala.collection.immutable.HashMap[?, ?]] => // immutable + t.elementRef.refType match + case '[k] => + t.elementRef2.refType match + case '[v] => + testValidMapKey(t.elementRef) + '{ + if $in.expectNull() then null + else + $in.expectToken('{') + scala.collection.immutable.HashMap.from( + $in.parseMap[k, v]( + () => ${ genReadVal[k](t.elementRef.asInstanceOf[RTypeRef[k]], in, inTuple, true) }, + () => ${ genReadVal[v](t.elementRef2.asInstanceOf[RTypeRef[v]], in, inTuple) }, + Map.empty[k, v], + true + ) + ) + }.asExprOf[T] + case '[scala.collection.immutable.SeqMap[?, ?]] => // immutable + t.elementRef.refType match + case '[k] => + t.elementRef2.refType match + case '[v] => + testValidMapKey(t.elementRef) + '{ + if $in.expectNull() then null + else + $in.expectToken('{') + scala.collection.immutable.SeqMap.from( + $in.parseMap[k, v]( + () => ${ genReadVal[k](t.elementRef.asInstanceOf[RTypeRef[k]], in, inTuple, true) }, + () => ${ genReadVal[v](t.elementRef2.asInstanceOf[RTypeRef[v]], in, inTuple) }, + Map.empty[k, v], + true + ) + ) + }.asExprOf[T] + case '[Map[?, ?]] if ref.name == "scala.collection.immutable.Map" => // immutable + t.elementRef.refType match + case '[k] => + t.elementRef2.refType match + case '[v] => + testValidMapKey(t.elementRef) + '{ + if $in.expectNull() then null + else + $in.expectToken('{') + $in.parseMap[k, v]( + () => ${ genReadVal[k](t.elementRef.asInstanceOf[RTypeRef[k]], in, inTuple, true) }, + () => ${ genReadVal[v](t.elementRef2.asInstanceOf[RTypeRef[v]], in, inTuple) }, + Map.empty[k, v], + true + ) + }.asExprOf[T] + case '[Map[?, ?]] => // all other immutable Maps + println(ref) + t.elementRef.refType match + case '[k] => + t.elementRef2.refType match + case '[v] => + testValidMapKey(t.elementRef) + '{ + if $in.expectNull() then null + else + $in.expectToken('{') + $in + .parseMap[k, v]( + () => ${ genReadVal[k](t.elementRef.asInstanceOf[RTypeRef[k]], in, inTuple, true) }, + () => ${ genReadVal[v](t.elementRef2.asInstanceOf[RTypeRef[v]], in, inTuple) }, + Map.empty[k, v], + true + ) + .to(${ Expr.summon[Factory[(k, v), T]].get }) + }.asExprOf[T] + + // TODO: Find out why SeqMap fails with this summon here. Create a minimal project w/macro to test/show behavior. + // Note: All other Map subtypes seem to work ok. SortedMap, etc. No issues. Just SeqMap. + + /* + case _ => + t.elementRef.refType match + case '[k] => + t.elementRef2.refType match + case '[v] => + testValidMapKey(t.elementRef) + '{ + if $in.expectNull() then null + else + $in.expectToken('{') + $in.parseMap[k, v]( + () => ${ genReadVal[k](t.elementRef.asInstanceOf[RTypeRef[k]], in, inTuple, true) }, + () => ${ genReadVal[v](t.elementRef2.asInstanceOf[RTypeRef[v]], in, inTuple) }, + Map.empty[k, v], + true + ) + }.asExprOf[T] + */ // -------------------- // Tuples... @@ -1516,6 +1771,38 @@ syntheticTA.write(t.asInstanceOf[L], writer, out) tv }.asExprOf[T] + // -------------------- + // Try... + // -------------------- + case t: TryRef[?] => + t.tryRef.refType match + case '[e] => + '{ + val mark = $in.pos + try + if $in.expectNull() then null + else scala.util.Success(${ genReadVal[e](t.tryRef.asInstanceOf[RTypeRef[e]], in) }) + catch { + case t: Throwable => + $in.revertToPos(mark) + throw JsonParseError("Unsuccessful attempt to read Try type with failure: " + t.getMessage(), $in) + } + }.asExprOf[T] + + // -------------------- + // Value Class... + // -------------------- + case t: ScalaClassRef[?] if t.isValueClass => + val theField = t.fields.head.fieldRef + t.refType match + case '[e] => + theField.refType match + case '[f] => + val tpe = TypeRepr.of[e] + val constructor = Select(New(Inferred(tpe)), tpe.classSymbol.get.primaryConstructor) + val arg = genReadVal[f](theField.asInstanceOf[RTypeRef[f]], in, isMapKey = isMapKey).asTerm + Apply(constructor, List(arg)).asExprOf[T] + case _ => // Classes, traits, etc. genDecFnBody[T](ref, in) // Create a new decoder function (presumably for class, trait, etc) diff --git a/src/main/scala/co.blocke.scalajack/json/package.scala b/src/main/scala/co.blocke.scalajack/json/package.scala index 10143aba..ceccf26f 100644 --- a/src/main/scala/co.blocke.scalajack/json/package.scala +++ b/src/main/scala/co.blocke.scalajack/json/package.scala @@ -3,6 +3,7 @@ package json import scala.util.Failure import scala.quoted.{Expr, Quotes, Type} +import java.util.Optional opaque type RawJson = String @@ -27,4 +28,15 @@ def descrambleTest(in: String, hash: Int): Boolean = def ofOption[T](xs: Option[Expr[T]])(using Type[T])(using q: Quotes): Expr[Option[T]] = import q.reflect.* - if xs.isEmpty then Expr(None) else '{ Some(${ xs.get }) } + if xs.isEmpty then '{ None } + else '{ Some(${ xs.get }) } + +// Java variant of ofOption +def ofOptional[T](xs: Optional[Expr[T]])(using Type[T])(using q: Quotes): Expr[Optional[T]] = + import q.reflect.* + if xs.isEmpty then '{ Optional.empty } + else '{ Optional.of(${ xs.get }) } + +enum Language { + case Scala, Java +} diff --git a/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala b/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala index cbc8b0d9..488d06c4 100644 --- a/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala +++ b/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala @@ -17,7 +17,6 @@ object JsonSource: case class JsonSource(js: CharSequence): private var i = 0 - private var _mark = 0 val max = js.length // Navigation... @@ -29,9 +28,8 @@ case class JsonSource(js: CharSequence): inline def backspace() = i -= 1 - inline def mark() = _mark = i - inline def revertToMark() = i = _mark - inline def captureMark(): String = js.subSequence(_mark, i).toString + inline def revertToPos(p: Int) = i = p + inline def captureMark(mark: Int): String = js.subSequence(mark, i).toString @tailrec final def readToken(): Char = @@ -97,7 +95,9 @@ case class JsonSource(js: CharSequence): else if t == 'n' then readChars(JsonSource.ull, "null") null - else throw new JsonParseError(s"Expected object start '{' or null", this) + else + backspace() + throw new JsonParseError(s"Expected object start '{' or null", this) def expectObjectField(fieldNameMatrix: StringMatrix): Option[Int] = val t = readToken() @@ -175,7 +175,7 @@ case class JsonSource(js: CharSequence): // Value might be null! // expectString() will look for leading '"'. parseString() presumes the '"' has already been consumed. inline def expectString(): String = - mark() + val mark = i val t = readToken() if t == '"' then val endI = parseString(i) @@ -191,7 +191,7 @@ case class JsonSource(js: CharSequence): readChars(JsonSource.ull, "null") null else - i = _mark + i = mark throw new JsonParseError(s"Expected a String value but got '$t'", this) @tailrec @@ -333,9 +333,9 @@ case class JsonSource(js: CharSequence): def expectNumberOrNull(): String = skipWS() - mark() + val mark = i skipNumber() - if i != _mark then captureMark() + if i != mark then captureMark(mark) else if readChar() == 'n' then readChars(JsonSource.ull, "null") null diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index 0abdb81f..6c6c612d 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -47,6 +47,11 @@ object RunMe extends App: import co.blocke.scalajack.json.run.Record println("\n") +//case class JJ(a: java.util.Stack[Int]) + given blah: ScalaJack[JJ] = sjCodecOf[JJ] + val js = """{"a":[1,2,3]}""" + println(blah.fromJson(js)) + // println(RType.of[Person].pretty) // implicit val blah: ScalaJack[Record] = sj[Record] // (JsonConfig.withSuppressedEscapedStrings()) @@ -74,10 +79,12 @@ object RunMe extends App: // val c: Pizza = ScalaJack[Pizza].fromJson("\"READY\"") // println("Pizza: " + c) - implicit val blah: ScalaJack[Decide] = sjCodecOf[Decide] - // 012345678 - val c: Decide = ScalaJack[Decide].fromJson("""{"a":[1,2,3]}""") - println(c) + // case class LRUnionHolder2[T, U](a: Seq[Boolean | Int], b: (T | U, T | U)) + // implicit val blah: ScalaJack[LRUnionHolder2[scala.util.Try[Option[Int]], String]] = sjCodecOf[LRUnionHolder2[scala.util.Try[Option[Int]], String]] + // val b: LRUnionHolder2[scala.util.Try[Option[Int]], String] = LRUnionHolder2(List(true, 3, false, -1), ("y", scala.util.Success(Some(5)))) + // val js = ScalaJack[LRUnionHolder2[scala.util.Try[Option[Int]], String]].toJson(b) + // println(js) + // println(ScalaJack[LRUnionHolder2[scala.util.Try[Option[Int]], String]].fromJson(js)) println("done.") diff --git a/src/main/scala/co.blocke.scalajack/run/Record.scala b/src/main/scala/co.blocke.scalajack/run/Record.scala index 23eea00c..8cdeda22 100644 --- a/src/main/scala/co.blocke.scalajack/run/Record.scala +++ b/src/main/scala/co.blocke.scalajack/run/Record.scala @@ -100,8 +100,6 @@ case class Person2(age: XList) case class Foom(a: schema.Schema) -case class Decide(a: List[Int]) - sealed trait Candy: val isSweet: Boolean case class MMs(isSweet: Boolean) extends Candy @@ -110,6 +108,8 @@ case class Veggies(yuks: String) type Food = Candy | Veggies +case class JJ(a: java.util.ArrayList[Int]) + val jsData = """{ "person": { diff --git a/src/test/scala/co.blocke.scalajack/json/collections/JavaCollSpec.scalax b/src/test/scala/co.blocke.scalajack/json/collections/JavaCollSpec.scala similarity index 76% rename from src/test/scala/co.blocke.scalajack/json/collections/JavaCollSpec.scalax rename to src/test/scala/co.blocke.scalajack/json/collections/JavaCollSpec.scala index 5097f27a..2ffb4346 100644 --- a/src/test/scala/co.blocke.scalajack/json/collections/JavaCollSpec.scalax +++ b/src/test/scala/co.blocke.scalajack/json/collections/JavaCollSpec.scala @@ -17,103 +17,103 @@ class JavaCollSpec() extends AnyFunSpec with JsonMatchers: describe(colorString("+++ Positive Tests +++")) { it("Set is null must work") { val inst = JSetHolder[Int](null) - val js = sj[JSetHolder[Int]].toJson(inst) + val js = sjCodecOf[JSetHolder[Int]].toJson(inst) js should matchJson("""{"a":null}""") } it("Set of numeric must work") { val inst = JSetHolder[Int](HashSet(Arrays.asList(1, 2, 3))) - val js = sj[JSetHolder[Int]].toJson(inst) + val js = sjCodecOf[JSetHolder[Int]].toJson(inst) js should matchJson("""{"a":[1,2,3]}""") } it("Set of string must work") { val inst = JSetHolder[String](HashSet(Arrays.asList("a", "b", "c"))) - val js = sj[JSetHolder[String]].toJson(inst) + val js = sjCodecOf[JSetHolder[String]].toJson(inst) js should matchJson("""{"a":["a","b","c"]}""") } it("Set of boolean must work") { val inst = JSetHolder[Boolean](HashSet(Arrays.asList(true, false, true))) - val js = sj[JSetHolder[Boolean]].toJson(inst) + val js = sjCodecOf[JSetHolder[Boolean]].toJson(inst) js should matchJson("""{"a":[false,true]}""") } it("Set of Set (nested) must work") { val inst = JSetHolder[List[Int]](HashSet(Arrays.asList(List(1, 2), List(3, 4)))) - val js = sj[JSetHolder[List[Int]]].toJson(inst) + val js = sjCodecOf[JSetHolder[List[Int]]].toJson(inst) js should matchJson("""{"a":[[3,4],[1,2]]}""") } it("Set of either must work") { val inst = JSetHolder[Either[Int, Boolean]](HashSet(Arrays.asList(Right(true), Left(15), Right(false)))) - val js = sj[JSetHolder[Either[Int, Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)).toJson(inst) + val js = sjCodecOf[JSetHolder[Either[Int, Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)).toJson(inst) js should matchJson("""{"a":[true,15,false]}""") } it("Set of union must work") { val inst = JSetHolder[Int | Boolean](HashSet(Arrays.asList(true, 15, false))) - val js = sj[JSetHolder[Int | Boolean]].toJson(inst) + val js = sjCodecOf[JSetHolder[Int | Boolean]].toJson(inst) js should matchJson("""{"a":[false,true,15]}""") } it("Set of option must work") { val inst = JSetHolder[Option[Int]](HashSet(Arrays.asList(Some(1), None, Some(3)))) - val js = sj[JSetHolder[Option[Int]]].toJson(inst) + val js = sjCodecOf[JSetHolder[Option[Int]]].toJson(inst) js should matchJson("""{"a":[1,3]}""") } it("Set of map must work") { val inst = JSetHolder[Map[String, Int]](HashSet(Arrays.asList(Map("a" -> 1, "b" -> 2), Map("c" -> 3, "d" -> 4)))) - val js = sj[JSetHolder[Map[String, Int]]].toJson(inst) + val js = sjCodecOf[JSetHolder[Map[String, Int]]].toJson(inst) js should matchJson("""{"a":[{"a":1,"b":2},{"c":3,"d":4}]}""") } it("Set of class must work") { val inst = JSetHolder[Person](HashSet(Arrays.asList(Person("Bob", 35), Person("Sally", 54)))) - val js = sj[JSetHolder[Person]].toJson(inst) + val js = sjCodecOf[JSetHolder[Person]].toJson(inst) js should matchJson("""{"a":[{"name":"Bob","age",35},{"name":"Sally","age",54}]}""") } it("ArrayList is null must work") { val inst = ArrayListHolder[Int](null) - val js = sj[ArrayListHolder[Int]].toJson(inst) + val js = sjCodecOf[ArrayListHolder[Int]].toJson(inst) js should matchJson("""{"a":null}""") } it("ArrayList of numeric must work") { val inst = ArrayListHolder[Int](ArrayList[Int](Arrays.asList(1, 2, 3))) - val js = sj[ArrayListHolder[Int]].toJson(inst) + val js = sjCodecOf[ArrayListHolder[Int]].toJson(inst) js should matchJson("""{"a":[1,2,3]}""") } it("ArrayList of string must work") { val inst = ArrayListHolder[String](ArrayList[String](Arrays.asList("a", "b", "c"))) - val js = sj[ArrayListHolder[String]].toJson(inst) + val js = sjCodecOf[ArrayListHolder[String]].toJson(inst) js should matchJson("""{"a":["a","b","c"]}""") } it("ArrayList of boolean must work") { val inst = ArrayListHolder[Boolean](ArrayList[Boolean](Arrays.asList(true, false, true))) - val js = sj[ArrayListHolder[Boolean]].toJson(inst) + val js = sjCodecOf[ArrayListHolder[Boolean]].toJson(inst) js should matchJson("""{"a":[true,false,true]}""") } it("ArrayList of ArrayList (nested) must work") { val inst = ArrayListHolder[ArrayList[Int]](ArrayList[ArrayList[Int]](Arrays.asList(ArrayList(Arrays.asList(1, 2)), ArrayList(Arrays.asList(3, 4))))) - val js = sj[ArrayListHolder[ArrayList[Int]]].toJson(inst) + val js = sjCodecOf[ArrayListHolder[ArrayList[Int]]].toJson(inst) js should matchJson("""{"a":[[1,2],[3,4]]}""") } it("ArrayList of either must work") { val inst = ArrayListHolder[Either[Int, Boolean]](ArrayList[Either[Int, Boolean]](Arrays.asList(Right(true), Left(15), Right(false)))) - val js = sj[ArrayListHolder[Either[Int, Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)).toJson(inst) + val js = sjCodecOf[ArrayListHolder[Either[Int, Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)).toJson(inst) js should matchJson("""{"a":[true,15,false]}""") } it("ArrayList of union must work") { val inst = ArrayListHolder[Int | Boolean](ArrayList[Int | Boolean](Arrays.asList(true, 15, false))) - val js = sj[ArrayListHolder[Int | Boolean]].toJson(inst) + val js = sjCodecOf[ArrayListHolder[Int | Boolean]].toJson(inst) js should matchJson("""{"a":[true,15,false]}""") } it("ArrayList of option must work") { val inst = ArrayListHolder[Option[Int]](ArrayList[Option[Int]](Arrays.asList(Some(1), None, Some(3)))) - val js = sj[ArrayListHolder[Option[Int]]].toJson(inst) + val js = sjCodecOf[ArrayListHolder[Option[Int]]].toJson(inst) js should matchJson("""{"a":[1,3]}""") } it("ArrayList of map must work") { val inst = ArrayListHolder[Map[String, Int]](ArrayList[Map[String, Int]](Arrays.asList(Map("a" -> 1, "b" -> 2), Map("c" -> 3, "d" -> 4)))) - val js = sj[ArrayListHolder[Map[String, Int]]].toJson(inst) + val js = sjCodecOf[ArrayListHolder[Map[String, Int]]].toJson(inst) js should matchJson("""{"a":[{"a":1,"b":2},{"c":3,"d":4}]}""") } it("ArrayList of class must work") { val inst = ArrayListHolder[Person](ArrayList[Person](Arrays.asList(Person("Bob", 35), Person("Sally", 54)))) - val js = sj[ArrayListHolder[Person]].toJson(inst) + val js = sjCodecOf[ArrayListHolder[Person]].toJson(inst) js should matchJson("""{"a":[{"name":"Bob","age",35},{"name":"Sally","age",54}]}""") } } diff --git a/src/test/scala/co.blocke.scalajack/json/collections/MapSpec.scala b/src/test/scala/co.blocke.scalajack/json/collections/MapSpec.scala index 6b35e11f..a9e89727 100644 --- a/src/test/scala/co.blocke.scalajack/json/collections/MapSpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/collections/MapSpec.scala @@ -106,29 +106,61 @@ class MapSpec() extends AnyFunSpec with JsonMatchers: js should matchJson("""{"a":{"w":3,"y":["wow","blah"]}}""") sj.fromJson(js) shouldEqual (inst) } - /* - * Keys of value class (Distance) must work - it("Map value of class must work") { val inst = MapHolder[String, Person](Map("w" -> Person("Bob", 34), "y" -> Person("Sally", 25))) - val js = sj[MapHolder[String, Person]].toJson(inst) + val sj = sjCodecOf[MapHolder[String, Person]] + val js = sj.toJson(inst) js should matchJson("""{"a":{"w":{"name":"Bob","age":34},"y":{"name":"Sally","age":25}}}""") + sj.fromJson(js) shouldEqual (inst) + } + it("Map key of value class must work") { + val inst = MapHolder[Distance, String](Map(new Distance(1.23) -> "x", Distance(4.56) -> "y")) + val sj = sjCodecOf[MapHolder[Distance, String]] + val js = sj.toJson(inst) + js should matchJson("""{"a":{"1.23":"x","4.56":"y"}}""") + sj.fromJson(js) shouldEqual (inst) } it("Map value of value class must work") { val inst = MapHolder[String, Distance](Map("w" -> new Distance(1.23), "y" -> Distance(4.56))) - val js = sj[MapHolder[String, Distance]].toJson(inst) + val sj = sjCodecOf[MapHolder[String, Distance]] + val js = sj.toJson(inst) js should matchJson("""{"a":{"w":1.23,"y":4.56}}""") + sj.fromJson(js) shouldEqual (inst) } - it("Mutable Map value must work") { - val inst = MMapHolder[String, Distance](scala.collection.mutable.HashMap("w" -> new Distance(1.23), "y" -> Distance(4.56))) - val sj = sjCodecOf[MapHolder[String, Distance]] + it("Mutable Map value must work - Map") { + val inst = MMapHolder[String, Distance](scala.collection.mutable.Map("w" -> new Distance(1.23), "y" -> Distance(4.56))) + val sj = sjCodecOf[MMapHolder[String, Distance]] + val js = sj.toJson(inst) + js should matchJson("""{"a":{"w":1.23,"y":4.56}}""") + sj.fromJson(js) shouldEqual (inst) + } + it("Mutable Map value must work - HashMap") { + val inst = MMapHolder2[String, Distance](scala.collection.mutable.HashMap("w" -> new Distance(1.23), "y" -> Distance(4.56))) + val sj = sjCodecOf[MMapHolder2[String, Distance]] val js = sj.toJson(inst) js should matchJson("""{"a":{"w":1.23,"y":4.56}}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) + } + it("Mutable Map value must work - SeqMap (examplar for all other mutable Maps)") { + val inst = MMapHolder3[String, Distance](scala.collection.mutable.SeqMap("w" -> new Distance(1.23), "y" -> Distance(4.56))) + val sj = sjCodecOf[MMapHolder3[String, Distance]] + val js = sj.toJson(inst) + js should matchJson("""{"a":{"w":1.23,"y":4.56}}""") + sj.fromJson(js) shouldEqual (inst) + } + it("Map value must work - HashMap") { + val inst = MapHolder2[String, Distance](scala.collection.immutable.HashMap("w" -> new Distance(1.23), "y" -> Distance(4.56))) + val sj = sjCodecOf[MapHolder2[String, Distance]] + val js = sj.toJson(inst) + js should matchJson("""{"a":{"w":1.23,"y":4.56}}""") + sj.fromJson(js) shouldEqual (inst) + } + it("Map value must work - SeqMap (examplar for all other immutable Maps)") { + val inst = MapHolder3[String, Distance](scala.collection.immutable.SeqMap("w" -> new Distance(1.23), "y" -> Distance(4.56))) + val sj = sjCodecOf[MapHolder3[String, Distance]] + val js = sj.toJson(inst) + js should matchJson("""{"a":{"w":1.23,"y":4.56}}""") + sj.fromJson(js) shouldEqual (inst) } - */ } - - // describe(colorString("--- Negative Tests ---")) { - // } } diff --git a/src/test/scala/co.blocke.scalajack/json/collections/Model.scala b/src/test/scala/co.blocke.scalajack/json/collections/Model.scala index 79ea22b0..012b1007 100644 --- a/src/test/scala/co.blocke.scalajack/json/collections/Model.scala +++ b/src/test/scala/co.blocke.scalajack/json/collections/Model.scala @@ -14,7 +14,11 @@ case class SetHolder[T](a: Set[T]) case class ArrayHolder[T](a: Array[T]) case class MapHolder[T, V](a: Map[T, V]) -case class MMapHolder[T, V](a: scala.collection.mutable.Map[T, V]) +case class MapHolder2[T, V](a: scala.collection.immutable.HashMap[T, V]) // specific +case class MapHolder3[T, V](a: scala.collection.immutable.SeqMap[T, V]) // open coersion +case class MMapHolder[T, V](a: scala.collection.mutable.Map[T, V]) // specific +case class MMapHolder2[T, V](a: scala.collection.mutable.HashMap[T, V]) // specific +case class MMapHolder3[T, V](a: scala.collection.mutable.SeqMap[T, V]) // open coersion case class JMapHolder[T, V](a: JMap[T, V]) class Distance(val meters: Double) extends AnyVal diff --git a/src/test/scala/co.blocke.scalajack/json/misc/AliasSpec.scalax b/src/test/scala/co.blocke.scalajack/json/misc/AliasSpec.scala similarity index 61% rename from src/test/scala/co.blocke.scalajack/json/misc/AliasSpec.scalax rename to src/test/scala/co.blocke.scalajack/json/misc/AliasSpec.scala index e26cf8ae..398ebb17 100644 --- a/src/test/scala/co.blocke.scalajack/json/misc/AliasSpec.scalax +++ b/src/test/scala/co.blocke.scalajack/json/misc/AliasSpec.scala @@ -22,23 +22,38 @@ class AliasSpec() extends AnyFunSpec with JsonMatchers: describe(colorString("+++ Positive Tests +++")) { it("Type aliases (opaque types) must be dereferenced") { val inst = AliasHolder[Count](5, List(1, 2, 3), Map(1 -> "wow"), Map("wow" -> 2)) - val js = sj[AliasHolder[Count]].toJson(inst) + val sj = sjCodecOf[AliasHolder[Count]] + val js = sj.toJson(inst) js should matchJson("""{"a":5,"b":[1,2,3],"c":{"1":"wow"},"d":{"wow":2}}""") + sj.fromJson(js) shouldEqual(inst) } it("Type aliases (opaque types) must be dereferenced (with Option)") { val inst = AliasHolder2[CountX](Some(5), List(Some(1), None, Some(3)), Map("wow" -> None)) - val js = sj[AliasHolder2[CountX]].toJson(inst) + val sj = sjCodecOf[AliasHolder2[CountX]] + val js = sj.toJson(inst) js should matchJson("""{"a":5,"b":[1,3],"c":{}}""") + sj.fromJson(js) shouldEqual(AliasHolder2[CountX](Some(5), List(Some(1),Some(3)), Map.empty[String,CountX])) + } + it("Type aliases (opaque types) must be dereferenced (with Option, noneAsNull)") { + val inst = AliasHolder2[CountX](Some(5), List(Some(1), None, Some(3)), Map("wow" -> None)) + val sj = sjCodecOf[AliasHolder2[CountX]](JsonConfig.withNoneAsNull()) + val js = sj.toJson(inst) + js should matchJson("""{"a":5,"b":[1,null,3],"c":{"wow":null}}""") + sj.fromJson(js) shouldEqual(inst) } it("Type aliases (non-opaque types) must be dereferenced") { val inst = AliasHolder[CountY]("q", List("r", "s", "t"), Map("u" -> "wow"), Map("wow" -> "v")) - val js = sj[AliasHolder[CountY]].toJson(inst) + val sj = sjCodecOf[AliasHolder[CountY]] + val js = sj.toJson(inst) js should matchJson("""{"a":"q","b":["r","s","t"],"c":{"u":"wow"},"d":{"wow":"v"}}""") + sj.fromJson(js) shouldEqual(inst) } it("Type aliases (non-opaque types) must be dereferenced (with Option)") { val inst = AliasHolder2[CountZ](Some("q"), List(Some("r"), None, Some("t")), Map("wow" -> None)) - val js = sj[AliasHolder2[CountZ]].toJson(inst) + val sj = sjCodecOf[AliasHolder2[CountZ]] + val js = sj.toJson(inst) js should matchJson("""{"a":"q","b":["r","t"],"c":{}}""") + sj.fromJson(js) shouldEqual(AliasHolder2[CountZ](Some("q"), List(Some("r"),Some("t")), Map.empty[String,CountZ])) } } } diff --git a/src/test/scala/co.blocke.scalajack/json/misc/LRSpec.scala b/src/test/scala/co.blocke.scalajack/json/misc/LRSpec.scala index 8d9016c5..cfb25498 100644 --- a/src/test/scala/co.blocke.scalajack/json/misc/LRSpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/misc/LRSpec.scala @@ -51,7 +51,7 @@ class LRSpec() extends AnyFunSpec with JsonMatchers: val sj = sjCodecOf[ComplexEither[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_NULL)) val js = sj.toJson(inst) js should matchJson("""{"a":null}""") - sj.fromJson(js) shouldEqual (ComplexEither(Some(null))) + sj.fromJson(js) shouldEqual (ComplexEither(null)) } it("Complex Either/Option must work (Left-AS_NULL, Option nullAsNull)") { val inst = ComplexEither[Int](Some(Left("err"))) @@ -110,19 +110,24 @@ class LRSpec() extends AnyFunSpec with JsonMatchers: js should matchJson("""{"a":["x"],"b":["y",null]}""") sj.fromJson(js) shouldEqual LRUnionHolder[Option[Int], String](List("x"), ("y", None)) } - // it("LR (union) must work with Try of Option (non-None)") { - // val inst = LRHolder[Try[Option[Int]], String](List(Success(Some(5)), "x"), ("y", Success(Some(10)))) - // val js = sj[LRHolder[Try[Option[Int]], String]].toJson(inst) - // js should matchJson("""{"a":[5,"x"],"b":["y",10]}""") - // } - // it("LR (union) must work with Try of Option (Success(None))") { - // val inst = LRHolder[Try[Option[Int]], String](List(Success(None), "x"), ("y", Success(None))) - // val js = sj[LRHolder[Try[Option[Int]], String]].toJson(inst) - // js should matchJson("""{"a":["x"],"b":["y",null]}""") - // } - // it("LR (union) must work with Try of Option (Failure)") { - // val inst = LRHolder[Try[Option[Int]], String](List(Failure(new Exception("boom")), "x"), ("y", Failure(new Exception("boom2")))) - // val js = sj[LRHolder[Try[Option[Int]], String]].toJson(inst) - // js should matchJson("""{"a":["x"],"b":["y",null]}""") - // } + it("LR (union) must work with Try of Option (non-None)") { + val inst = LRUnionHolder[Try[Option[Int]], String](List(Success(Some(5)), "x"), ("y", Success(Some(10)))) + val sj = sjCodecOf[LRUnionHolder[Try[Option[Int]], String]] + val js = sj.toJson(inst) + js should matchJson("""{"a":[5,"x"],"b":["y",10]}""") + sj.fromJson(js) shouldEqual (inst) + } + it("LR (union) must work with Try of Option (Success(None))") { + val inst = LRUnionHolder[Try[Option[Int]], String](List(Success(None), "x"), ("y", Success(None))) + val sj = sjCodecOf[LRUnionHolder[Try[Option[Int]], String]] + val js = sj.toJson(inst) + sj.fromJson(js) shouldEqual (LRUnionHolder(List("x"), ("y", null))) // None's get swallowed + } + it("LR (union) must work with Try of Option (Failure)") { + val inst = LRUnionHolder[Try[Option[Int]], String](List(Failure(new Exception("boom")), "x"), ("y", Failure(new Exception("boom2")))) + val sj = sjCodecOf[LRUnionHolder[Try[Option[Int]], String]] + val js = sj.toJson(inst) + js should matchJson("""{"a":[null,"x"],"b":["y",null]}""") + sj.fromJson(js) shouldEqual (LRUnionHolder(List(null, "x"), ("y", null))) // Left's come back as null + } } diff --git a/src/test/scala/co.blocke.scalajack/json/misc/Model.scala b/src/test/scala/co.blocke.scalajack/json/misc/Model.scala index f140bbaa..506834c1 100644 --- a/src/test/scala/co.blocke.scalajack/json/misc/Model.scala +++ b/src/test/scala/co.blocke.scalajack/json/misc/Model.scala @@ -21,14 +21,29 @@ case class OptionHolder[T]( j: Either[Option[T], T] // Either of Option (L) ) +case class OptionalHolder[T]( + a: Optional[T], // straight Optional + b: (Optional[T], String), // tuple w/Optional + c: List[Optional[T]], // Seq of Optional + d: Map[Int, Optional[T]], // Map of Optional + e: T | Optional[T], // Union of Optional (R) + f: Optional[T] | T, // Union of Optional (L) + g: Optional[Optional[T]], // Nested Optional + h: Optional[Person], // Optional of Class + i: Either[T, Optional[T]], // Either of Optional (R) + j: Either[Optional[T], T] // Either of Optional (L) +) + case class TryHolder[T](a: Try[T]) case class TryHolder2[T](a: Seq[Try[T]], b: (Try[T], Try[T])) case class LRUnionHolder[T, U](a: Seq[T | U], b: (T | U, T | U)) +case class LRUnionHolder2[T, U](a: Seq[Boolean | Int], b: (T | U, T | U)) case class EitherHolder[T](a: Either[T, String], b: Either[String, T]) case class ComplexEither[T](a: Option[Either[String, Option[T]]]) case class EitherRecipe[T](a: Either[Boolean, Either[Option[T], String]]) +case class EitherRecipeJ[T](a: Either[Boolean, Either[Optional[T], String]]) case class AliasHolder[T](a: T, b: List[T], c: Map[T, String], d: Map[String, T]) case class AliasHolder2[T](a: T, b: List[T], c: Map[String, T]) diff --git a/src/test/scala/co.blocke.scalajack/json/misc/OptionSpec.scala b/src/test/scala/co.blocke.scalajack/json/misc/OptionSpec.scala index 0213d0fe..03375979 100644 --- a/src/test/scala/co.blocke.scalajack/json/misc/OptionSpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/misc/OptionSpec.scala @@ -8,6 +8,7 @@ import org.scalatest.funspec.AnyFunSpec import org.scalatest.matchers.should.Matchers.* import org.scalatest.* import scala.util.* +import java.util.Optional import TestUtil.* import java.util.UUID @@ -15,90 +16,165 @@ import java.util.UUID class OptionSpec() extends AnyFunSpec with JsonMatchers: describe(colorString("-------------------------------\n: Option Tests :\n-------------------------------", Console.YELLOW)) { - it("Non-empty Options must work") { - val inst = OptionHolder[Int]( - Some(5), // straight Option - (Some(1), "ok"), // tuple w/Option - List(Some(1), None, Some(3)), // Seq of Option - Map(1 -> Some(2), 3 -> Some(4)), // Map of Option - Some(99), // Union of Option (R) - Some(100), // Union of Option (L) - Some(Some(0)), // Nested Option - Some(Person("BoB", 34)), // Option of class - Right(Some(15)), // Either of Option (R) - Left(Some(-3)) // Either of Option (L) - ) - val sj = sjCodecOf[OptionHolder[Int]] - val js = sj.toJson(inst) - js should matchJson("""{"a":5,"b":[1,"ok"],"c":[1,3],"d":{"1":2,"3":4},"e":99,"f":100,"g":0,"h":{"name":"BoB","age":34},"i":15,"j":-3}""") - // Some fields get changed/"promoted" when read back in per policy, as Either reads "best-fit" starting with Right value first - sj.fromJson(js) shouldEqual (inst.copy(c = List(Some(1), Some(3))).copy(e = 99).copy(j = Right(-3))) + describe(colorString("+++ Scala Option +++")) { + it("Non-empty Options must work") { + val inst = OptionHolder[Int]( + Some(5), // straight Option + (Some(1), "ok"), // tuple w/Option + List(Some(1), None, Some(3)), // Seq of Option + Map(1 -> Some(2), 3 -> Some(4)), // Map of Option + Some(99), // Union of Option (R) + Some(100), // Union of Option (L) + Some(Some(0)), // Nested Option + Some(Person("BoB", 34)), // Option of class + Right(Some(15)), // Either of Option (R) + Left(Some(-3)) // Either of Option (L) + ) + val sj = sjCodecOf[OptionHolder[Int]] + val js = sj.toJson(inst) + js should matchJson("""{"a":5,"b":[1,"ok"],"c":[1,3],"d":{"1":2,"3":4},"e":99,"f":100,"g":0,"h":{"name":"BoB","age":34},"i":15,"j":-3}""") + // Some fields get changed/"promoted" when read back in per policy, as Either reads "best-fit" starting with Right value first + sj.fromJson(js) shouldEqual (inst.copy(c = List(Some(1), Some(3))).copy(e = 99).copy(j = Right(-3))) + } + it("Empty Options must work (default)") { + val inst = OptionHolder[Int]( + None, // straight Option + (None, "ok"), // tuple w/Option + List(None, None, None), // Seq of Option + Map(1 -> None, 3 -> None), // Map of Option + None, // Union of Option (R) + None, // Union of Option (L) + Some(None), // Nested Option + None, // Option of class + Right(None), // Either of Option (R) + Left(None) // Either of Option (L) + ) + val sj = sjCodecOf[OptionHolder[Int]] + val js = sj.toJson(inst) + js should matchJson("""{"b":[null,"ok"],"c":[],"d":{}}""") + sj.fromJson(js) shouldEqual (inst.copy(c = List(), d = Map(), g = None)) + } + it("Empty Options must work (config noneAsNull = true)") { + val inst = OptionHolder[Int]( + None, // straight Option + (None, "ok"), // tuple w/Option + List(None, None, None), // Seq of Option + Map(1 -> None, 3 -> None), // Map of Option + None, // Union of Option (R) + None, // Union of Option (L) + Some(None), // Nested Option + None, // Option of class + Right(None), // Either of Option (R) + Left(None) // Either of Option (L) + ) + val sj = sjCodecOf[OptionHolder[Int]]( + JsonConfig.withNoneAsNull() + ) + val js = sj.toJson(inst) + js should matchJson("""{"a":null,"b":[null,"ok"],"c":[null,null,null],"d":{"1":null,"3":null},"e":null,"f":null,"g":null,"h":null,"i":null,"j":null}""") + sj.fromJson(js) shouldEqual (inst.copy(g = None, i = null, j = null)) + } + it("Either recipe should work (non-None)") { + val inst = EitherRecipe[Int](Right(Left(Some(5)))) + val sj = sjCodecOf[EitherRecipe[Int]] + val js = sj.toJson(inst) + js should matchJson("""{"a":5}""") + sj.fromJson(js) shouldEqual (inst) + } + it("Either recipe should work (None)") { + val inst = EitherRecipe[Int](Right(Left(None))) + val sj = sjCodecOf[EitherRecipe[Int]] + val js = sj.toJson(inst) + js should matchJson("""{}""") + sj.fromJson(js) shouldEqual (inst) + } + it("Either recipe should work (None as null)") { + val inst = EitherRecipe[Int](Right(Left(None))) + val sj = sjCodecOf[EitherRecipe[Int]](JsonConfig.withNoneAsNull()) + val js = sj.toJson(inst) + js should matchJson("""{"a":null}""") + sj.fromJson(js) shouldEqual (EitherRecipe[Int](null)) + } } - it("Empty Options must work (default)") { - val inst = OptionHolder[Int]( - None, // straight Option - (None, "ok"), // tuple w/Option - List(None, None, None), // Seq of Option - Map(1 -> None, 3 -> None), // Map of Option - None, // Union of Option (R) - None, // Union of Option (L) - Some(None), // Nested Option - None, // Option of class - Right(None), // Either of Option (R) - Left(None) // Either of Option (L) - ) - val sj = sjCodecOf[OptionHolder[Int]] - val js = sj.toJson(inst) - js should matchJson("""{"b":[null,"ok"],"c":[],"d":{}}""") - sj.fromJson(js) shouldEqual (inst.copy(c = List(), d = Map(), g = None)) - } - it("Empty Options must work (config noneAsNull = true)") { - val inst = OptionHolder[Int]( - None, // straight Option - (None, "ok"), // tuple w/Option - List(None, None, None), // Seq of Option - Map(1 -> None, 3 -> None), // Map of Option - None, // Union of Option (R) - None, // Union of Option (L) - Some(None), // Nested Option - None, // Option of class - Right(None), // Either of Option (R) - Left(None) // Either of Option (L) - ) - val sj = sjCodecOf[OptionHolder[Int]]( - JsonConfig.withNoneAsNull() - ) - val js = sj.toJson(inst) - js should matchJson("""{"a":null,"b":[null,"ok"],"c":[null,null,null],"d":{"1":null,"3":null},"e":null,"f":null,"g":null,"h":null,"i":null,"j":null}""") - sj.fromJson(js) shouldEqual (inst.copy(g = None, i = null, j = null)) - } - it("Either recipe should work (non-None)") { - val inst = EitherRecipe[Int](Right(Left(Some(5)))) - val sj = sjCodecOf[EitherRecipe[Int]] - val js = sj.toJson(inst) - js should matchJson("""{"a":5}""") - sj.fromJson(js) shouldEqual (inst) - } - it("Either recipe should work (None)") { - val inst = EitherRecipe[Int](Right(Left(None))) - val sj = sjCodecOf[EitherRecipe[Int]] - val js = sj.toJson(inst) - js should matchJson("""{}""") - sj.fromJson(js) shouldEqual (inst) - } - it("Either recipe should work (None as null)") { - val inst = EitherRecipe[Int](Right(Left(None))) - val sj = sjCodecOf[EitherRecipe[Int]](JsonConfig.withNoneAsNull()) - val js = sj.toJson(inst) - js should matchJson("""{"a":null}""") - sj.fromJson(js) shouldEqual (EitherRecipe[Int](null)) - } - - /* - TODO: Java Optional flavor... - it("Java Optional must work") { - ??? + describe(colorString("+++ Java Optional +++")) { + it("Non-empty Options must work") { + val inst = OptionalHolder[Int]( + Optional.of(5), // straight Optional + (Optional.of(1), "ok"), // tuple w/Optional + List(Optional.of(1), Optional.empty, Optional.of(3)), // Seq of Optional + Map(1 -> Optional.of(2), 3 -> Optional.of(4)), // Map of Optional + Optional.of(99), // Union of Optional (R) + Optional.of(100), // Union of Optional (L) + Optional.of(Optional.of(0)), // Nested Optional + Optional.of(Person("BoB", 34)), // Optional of class + Right(Optional.of(15)), // Either of Optional (R) + Left(Optional.of(-3)) // Either of Optional (L) + ) + val sj = sjCodecOf[OptionalHolder[Int]] + val js = sj.toJson(inst) + js should matchJson("""{"a":5,"b":[1,"ok"],"c":[1,3],"d":{"1":2,"3":4},"e":99,"f":100,"g":0,"h":{"name":"BoB","age":34},"i":15,"j":-3}""") + // Some fields get changed/"promoted" when read back in per policy, as Either reads "best-fit" starting with Right value first + sj.fromJson(js) shouldEqual (inst.copy(c = List(Optional.of(1), Optional.of(3))).copy(e = 99).copy(j = Right(-3))) + } + it("Empty Options must work (default)") { + val inst = OptionalHolder[Int]( + Optional.empty, // straight Option + (Optional.empty, "ok"), // tuple w/Option + List(Optional.empty, Optional.empty, Optional.empty), // Seq of Option + Map(1 -> Optional.empty, 3 -> Optional.empty), // Map of Option + Optional.empty, // Union of Option (R) + Optional.empty, // Union of Option (L) + Optional.of(Optional.empty), // Nested Option + Optional.empty, // Option of class + Right(Optional.empty), // Either of Option (R) + Left(Optional.empty) // Either of Option (L) + ) + val sj = sjCodecOf[OptionalHolder[Int]] + val js = sj.toJson(inst) + js should matchJson("""{"b":[null,"ok"],"c":[],"d":{}}""") + sj.fromJson(js) shouldEqual (inst.copy(c = List(), d = Map(), g = Optional.empty)) + } + it("Empty Options must work (config noneAsNull = true)") { + val inst = OptionalHolder[Int]( + Optional.empty, // straight Option + (Optional.empty, "ok"), // tuple w/Option + List(Optional.empty, Optional.empty, Optional.empty), // Seq of Option + Map(1 -> Optional.empty, 3 -> Optional.empty), // Map of Option + Optional.empty, // Union of Option (R) + Optional.empty, // Union of Option (L) + Optional.of(Optional.empty), // Nested Option + Optional.empty, // Option of class + Right(Optional.empty), // Either of Option (R) + Left(Optional.empty) // Either of Option (L) + ) + val sj = sjCodecOf[OptionalHolder[Int]]( + JsonConfig.withNoneAsNull() + ) + val js = sj.toJson(inst) + js should matchJson("""{"a":null,"b":[null,"ok"],"c":[null,null,null],"d":{"1":null,"3":null},"e":null,"f":null,"g":null,"h":null,"i":null,"j":null}""") + sj.fromJson(js) shouldEqual (inst.copy(g = Optional.empty, i = null, j = null)) + } + it("Either recipe should work (non-None)") { + val inst = EitherRecipeJ[Int](Right(Left(Optional.of(5)))) + val sj = sjCodecOf[EitherRecipeJ[Int]] + val js = sj.toJson(inst) + js should matchJson("""{"a":5}""") + sj.fromJson(js) shouldEqual (inst) + } + it("Either recipe should work (None)") { + val inst = EitherRecipeJ[Int](Right(Left(Optional.empty))) + val sj = sjCodecOf[EitherRecipeJ[Int]] + val js = sj.toJson(inst) + js should matchJson("""{}""") + sj.fromJson(js) shouldEqual (inst) + } + it("Either recipe should work (None as null)") { + val inst = EitherRecipeJ[Int](Right(Left(Optional.empty))) + val sj = sjCodecOf[EitherRecipeJ[Int]](JsonConfig.withNoneAsNull()) + val js = sj.toJson(inst) + js should matchJson("""{"a":null}""") + sj.fromJson(js) shouldEqual (EitherRecipeJ[Int](null)) + } } - */ } diff --git a/src/test/scala/co.blocke.scalajack/json/misc/TrySpec.scalax b/src/test/scala/co.blocke.scalajack/json/misc/TrySpec.scala similarity index 50% rename from src/test/scala/co.blocke.scalajack/json/misc/TrySpec.scalax rename to src/test/scala/co.blocke.scalajack/json/misc/TrySpec.scala index efcdb1b9..7b77bb15 100644 --- a/src/test/scala/co.blocke.scalajack/json/misc/TrySpec.scalax +++ b/src/test/scala/co.blocke.scalajack/json/misc/TrySpec.scala @@ -8,6 +8,7 @@ import org.scalatest.funspec.AnyFunSpec import org.scalatest.matchers.should.Matchers.* import org.scalatest.* import scala.util.* +import java.util.Optional import TestUtil.* import java.util.UUID @@ -18,56 +19,71 @@ class TrySpec() extends AnyFunSpec with JsonMatchers: describe(colorString("+++ Positive Tests +++")) { it("Try must work (Success)") { val inst = TryHolder(Success(15)) - val js = sj[TryHolder[Int]].toJson(inst) + val sj = sjCodecOf[TryHolder[Int]] + val js = sj.toJson(inst) js should matchJson("""{"a":15}""") + sj.fromJson(js) shouldEqual(inst) } it("Try of Option (non-None) must work (Success)") { val inst = TryHolder[Option[Int]](Success(Some(15))) - val js = sj[TryHolder[Option[Int]]].toJson(inst) + val sj = sjCodecOf[TryHolder[Option[Int]]] + val js = sj.toJson(inst) js should matchJson("""{"a":15}""") + sj.fromJson(js) shouldEqual(inst) } it("Try of Option (None) must work (Success)") { val inst = TryHolder[Option[Int]](Success(None)) - val js = sj[TryHolder[Option[Int]]].toJson(inst) + val sj = sjCodecOf[TryHolder[Option[Int]]] + val js = sj.toJson(inst) js should matchJson("""{}""") + sj.fromJson(js) shouldEqual(inst) + } + it("Try of Optional (non-None) must work (Success)") { + val inst = TryHolder[Optional[Int]](Success(Optional.of(15))) + val sj = sjCodecOf[TryHolder[Optional[Int]]] + val js = sj.toJson(inst) + js should matchJson("""{"a":15}""") + sj.fromJson(js) shouldEqual(inst) + } + it("Try of Optional (None) must work (Success)") { + val inst = TryHolder[Optional[Int]](Success(Optional.empty)) + val sj = sjCodecOf[TryHolder[Optional[Int]]] + val js = sj.toJson(inst) + js should matchJson("""{}""") + sj.fromJson(js) shouldEqual(inst) } it("Try w/policy AS_NULL must work (Failure)") { val inst = TryHolder[Int](Failure(new Exception("boom"))) - val js = sj[TryHolder[Int]](JsonConfig.withTryFailureHandling(TryPolicy.AS_NULL)).toJson(inst) + val sj = sjCodecOf[TryHolder[Int]](JsonConfig.withTryFailureHandling(TryPolicy.AS_NULL)) + val js = sj.toJson(inst) js should matchJson("""{"a":null}""") - } - it("Try w/policy NO_WRITE must work (Failure)") { - val inst = TryHolder[Int](Failure(new Exception("boom"))) - val js = sj[TryHolder[Int]](JsonConfig.withTryFailureHandling(TryPolicy.NO_WRITE)).toJson(inst) - js should matchJson("""{}""") + sj.fromJson(js) shouldEqual(TryHolder[Int](null)) } it("Try w/policy ERR_MSG_STRING must work (Failure)") { val inst = TryHolder[Int](Failure(new Exception("boom"))) - val js = sj[TryHolder[Int]](JsonConfig.withTryFailureHandling(TryPolicy.ERR_MSG_STRING)).toJson(inst) + val sj = sjCodecOf[TryHolder[Int]](JsonConfig.withTryFailureHandling(TryPolicy.ERR_MSG_STRING)) + val js = sj.toJson(inst) js should matchJson("""{"a":"Try Failure with msg: boom"}""") + val msg = """Unsuccessful attempt to read Try type with failure: Non-numeric character found when integer value expected at position 5 at position [5] + |{"a":"Try Failure with msg: boom"} + |-----^""".stripMargin + val err = intercept[JsonParseError] { sj.fromJson(js) } + err.show shouldEqual msg } it("Try w/policy ATHROW_EXCEPTIONS_NULL must work (Failure)") { val inst = TryHolder[Int](Failure(new Exception("boom"))) val caught = intercept[java.lang.Exception] { - sj[TryHolder[Int]](JsonConfig.withTryFailureHandling(TryPolicy.THROW_EXCEPTION)).toJson(inst) + sjCodecOf[TryHolder[Int]](JsonConfig.withTryFailureHandling(TryPolicy.THROW_EXCEPTION)).toJson(inst) } assert(caught.getMessage == "boom") } it("Seq and Tuple of Try must work for AS_NULL (Failure)") { val inst = TryHolder2[Int](List(Success(1), Failure(new Exception("boom")), Success(3)), (Failure(new Exception("boom")), Success(0))) - val js = sj[TryHolder2[Int]](JsonConfig.withTryFailureHandling(TryPolicy.AS_NULL)).toJson(inst) + val sj = sjCodecOf[TryHolder2[Int]](JsonConfig.withTryFailureHandling(TryPolicy.AS_NULL)) + val js = sj.toJson(inst) js should matchJson("""{"a":[1,null,3],"b":[null,0]}""") - } - it("Seq and Tuple of Try must work for NO_WRITE (Failure)") { - val inst = TryHolder2[Int](List(Success(1), Failure(new Exception("boom")), Success(3)), (Failure(new Exception("boom")), Success(0))) - val js = sj[TryHolder2[Int]](JsonConfig.withTryFailureHandling(TryPolicy.NO_WRITE)).toJson(inst) - js should matchJson("""{"a":[1,3],"b":[null,0]}""") - } - it("Seq and Tuple of Try of an Option must work for NO_WRITE (Failure)") { - val inst = TryHolder2[Option[Int]](List(Success(None), Failure(new Exception("boom")), Success(Some(3))), (Failure(new Exception("boom")), Success(None))) - val js = sj[TryHolder2[Option[Int]]](JsonConfig.withTryFailureHandling(TryPolicy.NO_WRITE)).toJson(inst) - js should matchJson("""{"a":[3],"b":[null,null]}""") + sj.fromJson(js) shouldEqual(TryHolder2[Int](List(Success(1), null, Success(3)), (null, Success(0)))) } } - } + } \ No newline at end of file From 1de8717dca451b79d34901849711e4ac610f2bdb Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Mon, 8 Apr 2024 00:57:21 -0500 Subject: [PATCH 54/65] Java collections and maps --- Z | 17685 ++++++++++++++++ build.sbt | 2 +- .../json/JsonCodecMaker.scala | 236 +- .../scala/co.blocke.scalajack/run/Play.scala | 10 +- .../json/collections/JavaCollSpec.scala | 201 +- .../{JavaMapSpec.scalax => JavaMapSpec.scala} | 132 +- .../json/collections/Model.scala | 1 + .../json/collections/TupleSpec.scala | 12 +- .../json/misc/AliasSpec.scala | 10 +- .../json/misc/TrySpec.scala | 18 +- 10 files changed, 18182 insertions(+), 125 deletions(-) create mode 100644 Z rename src/test/scala/co.blocke.scalajack/json/collections/{JavaMapSpec.scalax => JavaMapSpec.scala} (50%) diff --git a/Z b/Z new file mode 100644 index 00000000..128b233f --- /dev/null +++ b/Z @@ -0,0 +1,17685 @@ +[info] welcome to sbt 1.9.6 (Homebrew Java 19.0.2) +[info] loading global plugins from /Users/gzoller/.sbt/1.0/plugins +[info] loading settings for project scalajack-build-build-build from metals.sbt ... +[info] loading project definition from /Users/gzoller/me/git/ScalaJack/project/project/project +[info] loading settings for project scalajack-build-build from metals.sbt ... +[info] loading project definition from /Users/gzoller/me/git/ScalaJack/project/project +[success] Generated .bloop/scalajack-build-build.json +[success] Total time: 1 s, completed Apr 6, 2024, 11:44:09 PM +[info] loading settings for project scalajack-build from metals.sbt,plugins.sbt ... +[info] loading project definition from /Users/gzoller/me/git/ScalaJack/project +[success] Generated .bloop/scalajack-build.json +[success] Total time: 0 s, completed Apr 6, 2024, 11:44:09 PM +[info] loading settings for project root from build.sbt ... +>>> Unknown gitflow branch: newworld +>>> Unknown gitflow branch: newworld +Packaged artifact name: scalajack_unknown-tests.jar +Packaged artifact name: scalajack_unknown.jar +>>> Unknown gitflow branch: newworld +Packaged artifact name: scalajack_unknown.jar +>>> Unknown gitflow branch: newworld +Packaged artifact name: scalajack_unknown-sources.jar +>>> Unknown gitflow branch: newworld +Packaged artifact name: scalajack_unknown-tests-sources.jar +>>> Unknown gitflow branch: newworld +Packaged artifact name: scalajack_unknown.jar +>>> Unknown gitflow branch: newworld +>>> Unknown gitflow branch: newworld +Packaged artifact name: scalajack_unknown.pom +Packaged artifact name: scalajack_unknown.pom +>>> Unknown gitflow branch: newworld +Packaged artifact name: scalajack_unknown-javadoc.jar +>>> Unknown gitflow branch: newworld +Packaged artifact name: scalajack_unknown-tests-javadoc.jar +>>> Unknown gitflow branch: newworld +Packaged artifact name: scalajack_unknown-cached-compile.jar +>>> Unknown gitflow branch: newworld +Packaged artifact name: scalajack_unknown-cached-test.jar +[info] set current project to scalajack (in build file:/Users/gzoller/me/git/ScalaJack/) +[info] scalafmt: Formatting 17 Scala sources (/Users/gzoller/me/git/ScalaJack)... +[info] scalafmt: Formatting 17 Scala sources (/Users/gzoller/me/git/ScalaJack)... +[info] compiling 17 Scala sources and 20 Java sources to /Users/gzoller/me/git/ScalaJack/target/scala-3.3.1/classes ... +Codec: { + val __co_blocke_scalajack_json_run_JJ_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: java.util.ArrayList[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startArray() + scala.Predef.refArrayOps[java.lang.Object](in.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => out.value(elem.asInstanceOf[scala.Int]))) + out.endArray() + } + def w0(`in₂`: co.blocke.scalajack.json.run.JJ, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `out₂`.label("a") + w1(`in₂`.a, `out₂`) + `out₂`.endObject() + } + def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.run.JJ = { + var _a: java.util.ArrayList[scala.Int] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_run_JJ_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.run.JJ] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₃`.expectNull()) null else new java.util.ArrayList[scala.Int][scala.Int]({ + val parsedArray: scala.collection.mutable.ListBuffer[scala.Int] = `in₃`.expectArray[scala.Int]((() => `in₃`.expectInt())) + scala.jdk.CollectionConverters.SeqHasAsJava[scala.Int](parsedArray.toList).asJava.asInstanceOf[java.util.Collection[scala.Int]] + }) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) + case _ => + `in₃`.skipValue() + } + maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_run_JJ_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.run.JJ(_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.run.JJ] { + def encodeValue(`in₄`: co.blocke.scalajack.json.run.JJ, `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) + def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.run.JJ = r0(`in₅`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.run.JJ]) +} +[info] done compiling +[info] compiling 17 Scala sources and 2 Java sources to /Users/gzoller/me/git/ScalaJack/target/scala-3.3.1/test-classes ... +Codec: { + val __co_blocke_scalajack_json_collections_JSetHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: java.util.Set[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startArray() + scala.Predef.refArrayOps[java.lang.Object](in.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => out.value(elem.asInstanceOf[scala.Int]))) + out.endArray() + } + def w0(`in₂`: co.blocke.scalajack.json.collections.JSetHolder[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `out₂`.label("a") + w1(`in₂`.a, `out₂`) + `out₂`.endObject() + } + def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.JSetHolder[scala.Int] = { + var _a: java.util.Set[scala.Int] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_JSetHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.JSetHolder[scala.Int]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = { + val parsedArray: scala.collection.mutable.ListBuffer[scala.Int] = `in₃`.expectArray[scala.Int]((() => `in₃`.expectInt())) + if (parsedArray.==(null)) null else new java.util.TreeSet[scala.Int](scala.jdk.CollectionConverters.SeqHasAsJava[scala.Int](parsedArray.toList).asJava) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) + case _ => + `in₃`.skipValue() + } + maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_JSetHolder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.JSetHolder[scala.Int][scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.JSetHolder[scala.Int]] { + def encodeValue(`in₄`: co.blocke.scalajack.json.collections.JSetHolder[scala.Int], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) + def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.JSetHolder[scala.Int] = r0(`in₅`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.JSetHolder[scala.Int]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_JSetHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: java.util.Set[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startArray() + scala.Predef.refArrayOps[java.lang.Object](in.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => out.value(elem.asInstanceOf[scala.Int]))) + out.endArray() + } + def w0(`in₂`: co.blocke.scalajack.json.collections.JSetHolder[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `out₂`.label("a") + w1(`in₂`.a, `out₂`) + `out₂`.endObject() + } + def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.JSetHolder[scala.Int] = { + var _a: java.util.Set[scala.Int] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_JSetHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.JSetHolder[scala.Int]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = { + val parsedArray: scala.collection.mutable.ListBuffer[scala.Int] = `in₃`.expectArray[scala.Int]((() => `in₃`.expectInt())) + if (parsedArray.==(null)) null else new java.util.TreeSet[scala.Int](scala.jdk.CollectionConverters.SeqHasAsJava[scala.Int](parsedArray.toList).asJava) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) + case _ => + `in₃`.skipValue() + } + maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_JSetHolder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.JSetHolder[scala.Int][scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.JSetHolder[scala.Int]] { + def encodeValue(`in₄`: co.blocke.scalajack.json.collections.JSetHolder[scala.Int], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) + def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.JSetHolder[scala.Int] = r0(`in₅`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.JSetHolder[scala.Int]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_JSetHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: java.util.Set[scala.Predef.String], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startArray() + scala.Predef.refArrayOps[java.lang.Object](in.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => out.valueEscaped(elem.asInstanceOf[java.lang.String]))) + out.endArray() + } + def w0(`in₂`: co.blocke.scalajack.json.collections.JSetHolder[scala.Predef.String], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `out₂`.label("a") + w1(`in₂`.a, `out₂`) + `out₂`.endObject() + } + def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.JSetHolder[scala.Predef.String] = { + var _a: java.util.Set[scala.Predef.String] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_JSetHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.JSetHolder[scala.Predef.String]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = { + val parsedArray: scala.collection.mutable.ListBuffer[java.lang.String] = `in₃`.expectArray[java.lang.String]((() => `in₃`.expectString())) + if (parsedArray.==(null)) null else new java.util.TreeSet[java.lang.String](scala.jdk.CollectionConverters.SeqHasAsJava[java.lang.String](parsedArray.toList).asJava) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) + case _ => + `in₃`.skipValue() + } + maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_JSetHolder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.JSetHolder[scala.Predef.String][java.lang.String](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.JSetHolder[scala.Predef.String]] { + def encodeValue(`in₄`: co.blocke.scalajack.json.collections.JSetHolder[scala.Predef.String], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) + def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.JSetHolder[scala.Predef.String] = r0(`in₅`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.JSetHolder[scala.Predef.String]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_JSetHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: java.util.Set[scala.Boolean], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startArray() + scala.Predef.refArrayOps[java.lang.Object](in.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => out.value(elem.asInstanceOf[scala.Boolean]))) + out.endArray() + } + def w0(`in₂`: co.blocke.scalajack.json.collections.JSetHolder[scala.Boolean], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `out₂`.label("a") + w1(`in₂`.a, `out₂`) + `out₂`.endObject() + } + def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.JSetHolder[scala.Boolean] = { + var _a: java.util.Set[scala.Boolean] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_JSetHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.JSetHolder[scala.Boolean]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = { + val parsedArray: scala.collection.mutable.ListBuffer[scala.Boolean] = `in₃`.expectArray[scala.Boolean]((() => `in₃`.expectBoolean())) + if (parsedArray.==(null)) null else new java.util.TreeSet[scala.Boolean](scala.jdk.CollectionConverters.SeqHasAsJava[scala.Boolean](parsedArray.toList).asJava) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) + case _ => + `in₃`.skipValue() + } + maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_JSetHolder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.JSetHolder[scala.Boolean][scala.Boolean](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.JSetHolder[scala.Boolean]] { + def encodeValue(`in₄`: co.blocke.scalajack.json.collections.JSetHolder[scala.Boolean], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) + def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.JSetHolder[scala.Boolean] = r0(`in₅`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.JSetHolder[scala.Boolean]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_JSetHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w2(in: java.util.Set[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startArray() + scala.Predef.refArrayOps[java.lang.Object](in.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => out.value(elem.asInstanceOf[scala.Int]))) + out.endArray() + } + def w1(`in₂`: java.util.Set[java.util.Set[scala.Int]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startArray() + scala.Predef.refArrayOps[java.lang.Object](`in₂`.toArray()).foreach[scala.Unit](((`elem₂`: java.lang.Object) => w2(`elem₂`.asInstanceOf[java.util.Set[scala.Int]], `out₂`))) + `out₂`.endArray() + } + def w0(`in₃`: co.blocke.scalajack.json.collections.JSetHolder[java.util.Set[scala.Int]], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { + `out₃`.startObject() + `out₃`.label("a") + w1(`in₃`.a, `out₃`) + `out₃`.endObject() + } + def r0(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.JSetHolder[java.util.Set[scala.Int]] = { + var _a: java.util.Set[java.util.Set[scala.Int]] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₄`.expectFirstObjectField(__co_blocke_scalajack_json_collections_JSetHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.JSetHolder[java.util.Set[scala.Int]]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = { + val parsedArray: scala.collection.mutable.ListBuffer[java.util.Set[scala.Int]] = `in₄`.expectArray[java.util.Set[scala.Int]]((() => { + val `parsedArray₂`: scala.collection.mutable.ListBuffer[scala.Int] = `in₄`.expectArray[scala.Int]((() => `in₄`.expectInt())) + if (`parsedArray₂`.==(null)) null else new java.util.TreeSet[scala.Int](scala.jdk.CollectionConverters.SeqHasAsJava[scala.Int](`parsedArray₂`.toList).asJava) + })) + if (parsedArray.==(null)) null else new java.util.TreeSet[java.util.Set[scala.Int]](scala.jdk.CollectionConverters.SeqHasAsJava[java.util.Set[scala.Int]](parsedArray.toList).asJava) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₄`) + case _ => + `in₄`.skipValue() + } + maybeFieldNum = `in₄`.expectObjectField(__co_blocke_scalajack_json_collections_JSetHolder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.JSetHolder[java.util.Set[scala.Int]][java.util.Set[scala.Int]](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₄`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.JSetHolder[java.util.Set[scala.Int]]] { + def encodeValue(`in₅`: co.blocke.scalajack.json.collections.JSetHolder[java.util.Set[scala.Int]], `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₅`, `out₄`) + def decodeValue(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.JSetHolder[java.util.Set[scala.Int]] = r0(`in₆`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.JSetHolder[java.util.Set[scala.Int]]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_JSetHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: java.util.Set[scala.util.Either[scala.Int, scala.Boolean]], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startArray() + scala.Predef.refArrayOps[java.lang.Object](in.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => if (elem.asInstanceOf[scala.util.Either[scala.Int, scala.Boolean]].==(null)) out.burpNull() else elem.asInstanceOf[scala.util.Either[scala.Int, scala.Boolean]] match { + case scala.Left(v) => + out.value(v.asInstanceOf[scala.Int]) + case scala.Right(v) => + out.value(`v₂`.asInstanceOf[scala.Boolean]) + })) + out.endArray() + } + def w0(`in₂`: co.blocke.scalajack.json.collections.JSetHolder[scala.util.Either[scala.Int, scala.Boolean]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `out₂`.label("a") + w1(`in₂`.a, `out₂`) + `out₂`.endObject() + } + def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.JSetHolder[scala.util.Either[scala.Int, scala.Boolean]] = { + var _a: java.util.Set[scala.util.Either[scala.Int, scala.Boolean]] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_JSetHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.JSetHolder[scala.util.Either[scala.Int, scala.Boolean]]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = { + val parsedArray: scala.collection.mutable.ListBuffer[scala.util.Either[scala.Int, scala.Boolean]] = `in₃`.expectArray[scala.util.Either[scala.Int, scala.Boolean]]((() => { + val mark: scala.Int = `in₃`.pos + if (`in₃`.expectNull()) null else scala.util.Try.apply[scala.Boolean](`in₃`.expectBoolean()) match { + case scala.util.Success(rval) => + scala.Right.apply[scala.Nothing, scala.Boolean](rval) + case scala.util.Failure(f) => + `in₃`.revertToPos(mark) + scala.util.Try.apply[scala.Int](`in₃`.expectInt()) match { + case scala.util.Success(lval) => + scala.Left.apply[scala.Int, scala.Nothing](lval) + case scala.util.Failure(_) => + `in₃`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₃`) + } + } + })) + if (parsedArray.==(null)) null else new java.util.TreeSet[scala.util.Either[scala.Int, scala.Boolean]](scala.jdk.CollectionConverters.SeqHasAsJava[scala.util.Either[scala.Int, scala.Boolean]](parsedArray.toList).asJava) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) + case _ => + `in₃`.skipValue() + } + maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_JSetHolder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.JSetHolder[scala.util.Either[scala.Int, scala.Boolean]][scala.util.Either[scala.Int, scala.Boolean]](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.JSetHolder[scala.util.Either[scala.Int, scala.Boolean]]] { + def encodeValue(`in₄`: co.blocke.scalajack.json.collections.JSetHolder[scala.util.Either[scala.Int, scala.Boolean]], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) + def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.JSetHolder[scala.util.Either[scala.Int, scala.Boolean]] = r0(`in₅`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.JSetHolder[scala.util.Either[scala.Int, scala.Boolean]]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_JSetHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: java.util.Set[scala.Int | scala.Boolean], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startArray() + scala.Predef.refArrayOps[java.lang.Object](in.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => { + out.mark() + scala.util.Try.apply[scala.Unit](out.value(elem.asInstanceOf[scala.Int | scala.Boolean].asInstanceOf[scala.Boolean])) match { + case scala.util.Success(_) => + () + case scala.util.Failure(_) => + out.revert() + out.value(elem.asInstanceOf[scala.Int | scala.Boolean].asInstanceOf[scala.Int]) + } + })) + out.endArray() + } + def w0(`in₂`: co.blocke.scalajack.json.collections.JSetHolder[scala.Int | scala.Boolean], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `out₂`.label("a") + w1(`in₂`.a, `out₂`) + `out₂`.endObject() + } + def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.JSetHolder[scala.Int | scala.Boolean] = { + var _a: java.util.Set[scala.Int | scala.Boolean] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_JSetHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.JSetHolder[scala.Int | scala.Boolean]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = { + val parsedArray: scala.collection.mutable.ListBuffer[scala.Int | scala.Boolean] = `in₃`.expectArray[scala.Int | scala.Boolean]((() => { + val mark: scala.Int = `in₃`.pos + scala.util.Try.apply[scala.Int](`in₃`.expectInt()) match { + case scala.util.Success(lval) => + (lval: scala.Int) + case scala.util.Failure(f) => + `in₃`.revertToPos(mark) + scala.util.Try.apply[scala.Boolean](`in₃`.expectBoolean()) match { + case scala.util.Success(rval) => + (rval: scala.Boolean) + case scala.util.Failure(_) => + `in₃`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₃`) + } + } + })) + if (parsedArray.==(null)) null else new java.util.TreeSet[scala.Int | scala.Boolean](scala.jdk.CollectionConverters.SeqHasAsJava[scala.Int | scala.Boolean](parsedArray.toList).asJava) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) + case _ => + `in₃`.skipValue() + } + maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_JSetHolder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.JSetHolder[scala.Int | scala.Boolean][scala.Int | scala.Boolean](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.JSetHolder[scala.Int | scala.Boolean]] { + def encodeValue(`in₄`: co.blocke.scalajack.json.collections.JSetHolder[scala.Int | scala.Boolean], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) + def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.JSetHolder[scala.Int | scala.Boolean] = r0(`in₅`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.JSetHolder[scala.Int | scala.Boolean]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_JSetHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: java.util.Set[scala.Option[scala.Int]], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startArray() + scala.Predef.refArrayOps[java.lang.Object](in.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => elem.asInstanceOf[scala.Option[scala.Int]] match { + case null => + out.burpNull() + case scala.None => + () + case scala.Some(v) => + val vv: scala.Int = v.asInstanceOf[scala.Int] + out.value(vv) + })) + out.endArray() + } + def w0(`in₂`: co.blocke.scalajack.json.collections.JSetHolder[scala.Option[scala.Int]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `out₂`.label("a") + w1(`in₂`.a, `out₂`) + `out₂`.endObject() + } + def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.JSetHolder[scala.Option[scala.Int]] = { + var _a: java.util.Set[scala.Option[scala.Int]] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_JSetHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.JSetHolder[scala.Option[scala.Int]]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = { + val parsedArray: scala.collection.mutable.ListBuffer[scala.Option[scala.Int]] = `in₃`.expectArray[scala.Option[scala.Int]]((() => if (`in₃`.expectNull()) null else scala.Some.apply[scala.Int](`in₃`.expectInt()))) + if (parsedArray.==(null)) null else new java.util.TreeSet[scala.Option[scala.Int]](scala.jdk.CollectionConverters.SeqHasAsJava[scala.Option[scala.Int]](parsedArray.toList).asJava) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) + case _ => + `in₃`.skipValue() + } + maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_JSetHolder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.JSetHolder[scala.Option[scala.Int]][scala.Option[scala.Int]](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.JSetHolder[scala.Option[scala.Int]]] { + def encodeValue(`in₄`: co.blocke.scalajack.json.collections.JSetHolder[scala.Option[scala.Int]], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) + def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.JSetHolder[scala.Option[scala.Int]] = r0(`in₅`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.JSetHolder[scala.Option[scala.Int]]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_JSetHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w2(in: scala.collection.immutable.Map[scala.Predef.String, scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + in.foreach[scala.Unit](((x$1: scala.Tuple2[java.lang.String, scala.Int]) => x$1 match { + case scala.Tuple2(key, value) => + { + out.maybeComma() + out.valueEscaped(key) + out.colon() + out.value(value) + } + })) + out.endObject() + } + def w1(`in₂`: java.util.Set[scala.collection.immutable.Map[scala.Predef.String, scala.Int]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startArray() + scala.Predef.refArrayOps[java.lang.Object](`in₂`.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => w2(elem.asInstanceOf[scala.collection.immutable.Map[scala.Predef.String, scala.Int]], `out₂`))) + `out₂`.endArray() + } + def w0(`in₃`: co.blocke.scalajack.json.collections.JSetHolder[scala.collection.immutable.Map[scala.Predef.String, scala.Int]], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { + `out₃`.startObject() + `out₃`.label("a") + w1(`in₃`.a, `out₃`) + `out₃`.endObject() + } + def r0(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.JSetHolder[scala.collection.immutable.Map[scala.Predef.String, scala.Int]] = { + var _a: java.util.Set[scala.collection.immutable.Map[scala.Predef.String, scala.Int]] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₄`.expectFirstObjectField(__co_blocke_scalajack_json_collections_JSetHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.JSetHolder[scala.collection.immutable.Map[scala.Predef.String, scala.Int]]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = { + val parsedArray: scala.collection.mutable.ListBuffer[scala.collection.immutable.Map[scala.Predef.String, scala.Int]] = `in₄`.expectArray[scala.collection.immutable.Map[scala.Predef.String, scala.Int]]((() => if (`in₄`.expectNull()) null else { + `in₄`.expectToken('{') + `in₄`.parseMap[java.lang.String, scala.Int]((() => `in₄`.expectString()), (() => `in₄`.expectInt()), scala.Predef.Map.empty[java.lang.String, scala.Int], true) + })) + if (parsedArray.==(null)) null else new java.util.TreeSet[scala.collection.immutable.Map[scala.Predef.String, scala.Int]](scala.jdk.CollectionConverters.SeqHasAsJava[scala.collection.immutable.Map[scala.Predef.String, scala.Int]](parsedArray.toList).asJava) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₄`) + case _ => + `in₄`.skipValue() + } + maybeFieldNum = `in₄`.expectObjectField(__co_blocke_scalajack_json_collections_JSetHolder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.JSetHolder[scala.collection.immutable.Map[scala.Predef.String, scala.Int]][scala.collection.immutable.Map[scala.Predef.String, scala.Int]](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₄`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.JSetHolder[scala.collection.immutable.Map[scala.Predef.String, scala.Int]]] { + def encodeValue(`in₅`: co.blocke.scalajack.json.collections.JSetHolder[scala.collection.immutable.Map[scala.Predef.String, scala.Int]], `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₅`, `out₄`) + def decodeValue(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.JSetHolder[scala.collection.immutable.Map[scala.Predef.String, scala.Int]] = r0(`in₆`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.JSetHolder[scala.collection.immutable.Map[scala.Predef.String, scala.Int]]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_Person_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("name", "age")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + val __co_blocke_scalajack_json_collections_JSetHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w2(in: co.blocke.scalajack.json.collections.Person, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("name") + out.valueEscaped(in.name) + out.label("age") + out.value(in.age) + out.endObject() + } + def w1(`in₂`: java.util.Set[co.blocke.scalajack.json.collections.Person], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startArray() + scala.Predef.refArrayOps[java.lang.Object](`in₂`.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => w2(elem.asInstanceOf[co.blocke.scalajack.json.collections.Person], `out₂`))) + `out₂`.endArray() + } + def w0(`in₃`: co.blocke.scalajack.json.collections.JSetHolder[co.blocke.scalajack.json.collections.Person], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { + `out₃`.startObject() + `out₃`.label("a") + w1(`in₃`.a, `out₃`) + `out₃`.endObject() + } + def r1(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.Person = { + var _name: java.lang.String = "" + var _age: scala.Int = 0 + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₄`.expectFirstObjectField(__co_blocke_scalajack_json_collections_Person_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.Person] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _name = `in₄`.expectString() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("name"), `in₄`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _age = `in₄`.expectInt() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("age"), `in₄`) + case _ => + `in₄`.skipValue() + } + maybeFieldNum = `in₄`.expectObjectField(__co_blocke_scalajack_json_collections_Person_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.collections.Person(_name, _age) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("name", "age")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₄`) + } + } + def r0(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.JSetHolder[co.blocke.scalajack.json.collections.Person] = { + var _a: java.util.Set[co.blocke.scalajack.json.collections.Person] = null + var `required₂`: scala.Int = 1 + var `maybeFieldNum₂`: scala.Option[scala.Int] = `in₅`.expectFirstObjectField(__co_blocke_scalajack_json_collections_JSetHolder_fields) + if (`maybeFieldNum₂`.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.JSetHolder[co.blocke.scalajack.json.collections.Person]] else { + while (`maybeFieldNum₂`.isDefined) { + `maybeFieldNum₂`.get match { + case 0 => + if (`required₂`.&(1).!=(0)) { + `required₂` = `required₂`.^(1) + _a = { + val parsedArray: scala.collection.mutable.ListBuffer[co.blocke.scalajack.json.collections.Person] = `in₅`.expectArray[co.blocke.scalajack.json.collections.Person]((() => r1(`in₅`))) + if (parsedArray.==(null)) null else new java.util.TreeSet[co.blocke.scalajack.json.collections.Person](scala.jdk.CollectionConverters.SeqHasAsJava[co.blocke.scalajack.json.collections.Person](parsedArray.toList).asJava) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₅`) + case _ => + `in₅`.skipValue() + } + `maybeFieldNum₂` = `in₅`.expectObjectField(__co_blocke_scalajack_json_collections_JSetHolder_fields) + } + if (`required₂`.&(1).==(0)) new co.blocke.scalajack.json.collections.JSetHolder[co.blocke.scalajack.json.collections.Person][co.blocke.scalajack.json.collections.Person](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(`required₂`.&(1)))), `in₅`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.JSetHolder[co.blocke.scalajack.json.collections.Person]] { + def encodeValue(`in₆`: co.blocke.scalajack.json.collections.JSetHolder[co.blocke.scalajack.json.collections.Person], `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₆`, `out₄`) + def decodeValue(`in₇`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.JSetHolder[co.blocke.scalajack.json.collections.Person] = r0(`in₇`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.JSetHolder[co.blocke.scalajack.json.collections.Person]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_ArrayListHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: java.util.ArrayList[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startArray() + scala.Predef.refArrayOps[java.lang.Object](in.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => out.value(elem.asInstanceOf[scala.Int]))) + out.endArray() + } + def w0(`in₂`: co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `out₂`.label("a") + w1(`in₂`.a, `out₂`) + `out₂`.endObject() + } + def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int] = { + var _a: java.util.ArrayList[scala.Int] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_ArrayListHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₃`.expectNull()) null else new java.util.ArrayList[scala.Int][scala.Int]({ + val parsedArray: scala.collection.mutable.ListBuffer[scala.Int] = `in₃`.expectArray[scala.Int]((() => `in₃`.expectInt())) + scala.jdk.CollectionConverters.SeqHasAsJava[scala.Int](parsedArray.toList).asJava.asInstanceOf[java.util.Collection[scala.Int]] + }) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) + case _ => + `in₃`.skipValue() + } + maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_ArrayListHolder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int][scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int]] { + def encodeValue(`in₄`: co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) + def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int] = r0(`in₅`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_ArrayListHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: java.util.ArrayList[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startArray() + scala.Predef.refArrayOps[java.lang.Object](in.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => out.value(elem.asInstanceOf[scala.Int]))) + out.endArray() + } + def w0(`in₂`: co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `out₂`.label("a") + w1(`in₂`.a, `out₂`) + `out₂`.endObject() + } + def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int] = { + var _a: java.util.ArrayList[scala.Int] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_ArrayListHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₃`.expectNull()) null else new java.util.ArrayList[scala.Int][scala.Int]({ + val parsedArray: scala.collection.mutable.ListBuffer[scala.Int] = `in₃`.expectArray[scala.Int]((() => `in₃`.expectInt())) + scala.jdk.CollectionConverters.SeqHasAsJava[scala.Int](parsedArray.toList).asJava.asInstanceOf[java.util.Collection[scala.Int]] + }) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) + case _ => + `in₃`.skipValue() + } + maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_ArrayListHolder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int][scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int]] { + def encodeValue(`in₄`: co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) + def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int] = r0(`in₅`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_ArrayListHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: java.util.ArrayList[scala.Predef.String], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startArray() + scala.Predef.refArrayOps[java.lang.Object](in.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => out.valueEscaped(elem.asInstanceOf[java.lang.String]))) + out.endArray() + } + def w0(`in₂`: co.blocke.scalajack.json.collections.ArrayListHolder[scala.Predef.String], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `out₂`.label("a") + w1(`in₂`.a, `out₂`) + `out₂`.endObject() + } + def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.ArrayListHolder[scala.Predef.String] = { + var _a: java.util.ArrayList[scala.Predef.String] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_ArrayListHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.ArrayListHolder[scala.Predef.String]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₃`.expectNull()) null else new java.util.ArrayList[scala.Predef.String][java.lang.String]({ + val parsedArray: scala.collection.mutable.ListBuffer[java.lang.String] = `in₃`.expectArray[java.lang.String]((() => `in₃`.expectString())) + scala.jdk.CollectionConverters.SeqHasAsJava[java.lang.String](parsedArray.toList).asJava.asInstanceOf[java.util.Collection[java.lang.String]] + }) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) + case _ => + `in₃`.skipValue() + } + maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_ArrayListHolder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.ArrayListHolder[scala.Predef.String][java.lang.String](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.ArrayListHolder[scala.Predef.String]] { + def encodeValue(`in₄`: co.blocke.scalajack.json.collections.ArrayListHolder[scala.Predef.String], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) + def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.ArrayListHolder[scala.Predef.String] = r0(`in₅`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.ArrayListHolder[scala.Predef.String]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_ArrayListHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: java.util.ArrayList[scala.Boolean], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startArray() + scala.Predef.refArrayOps[java.lang.Object](in.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => out.value(elem.asInstanceOf[scala.Boolean]))) + out.endArray() + } + def w0(`in₂`: co.blocke.scalajack.json.collections.ArrayListHolder[scala.Boolean], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `out₂`.label("a") + w1(`in₂`.a, `out₂`) + `out₂`.endObject() + } + def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.ArrayListHolder[scala.Boolean] = { + var _a: java.util.ArrayList[scala.Boolean] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_ArrayListHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.ArrayListHolder[scala.Boolean]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₃`.expectNull()) null else new java.util.ArrayList[scala.Boolean][scala.Boolean]({ + val parsedArray: scala.collection.mutable.ListBuffer[scala.Boolean] = `in₃`.expectArray[scala.Boolean]((() => `in₃`.expectBoolean())) + scala.jdk.CollectionConverters.SeqHasAsJava[scala.Boolean](parsedArray.toList).asJava.asInstanceOf[java.util.Collection[scala.Boolean]] + }) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) + case _ => + `in₃`.skipValue() + } + maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_ArrayListHolder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.ArrayListHolder[scala.Boolean][scala.Boolean](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.ArrayListHolder[scala.Boolean]] { + def encodeValue(`in₄`: co.blocke.scalajack.json.collections.ArrayListHolder[scala.Boolean], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) + def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.ArrayListHolder[scala.Boolean] = r0(`in₅`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.ArrayListHolder[scala.Boolean]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_ArrayListHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w2(in: java.util.ArrayList[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startArray() + scala.Predef.refArrayOps[java.lang.Object](in.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => out.value(elem.asInstanceOf[scala.Int]))) + out.endArray() + } + def w1(`in₂`: java.util.ArrayList[java.util.ArrayList[scala.Int]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startArray() + scala.Predef.refArrayOps[java.lang.Object](`in₂`.toArray()).foreach[scala.Unit](((`elem₂`: java.lang.Object) => w2(`elem₂`.asInstanceOf[java.util.ArrayList[scala.Int]], `out₂`))) + `out₂`.endArray() + } + def w0(`in₃`: co.blocke.scalajack.json.collections.ArrayListHolder[java.util.ArrayList[scala.Int]], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { + `out₃`.startObject() + `out₃`.label("a") + w1(`in₃`.a, `out₃`) + `out₃`.endObject() + } + def r0(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.ArrayListHolder[java.util.ArrayList[scala.Int]] = { + var _a: java.util.ArrayList[java.util.ArrayList[scala.Int]] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₄`.expectFirstObjectField(__co_blocke_scalajack_json_collections_ArrayListHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.ArrayListHolder[java.util.ArrayList[scala.Int]]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₄`.expectNull()) null else new java.util.ArrayList[java.util.ArrayList[scala.Int]][java.util.ArrayList[scala.Int]]({ + val parsedArray: scala.collection.mutable.ListBuffer[java.util.ArrayList[scala.Int]] = `in₄`.expectArray[java.util.ArrayList[scala.Int]]((() => if (`in₄`.expectNull()) null else new java.util.ArrayList[scala.Int][scala.Int]({ + val `parsedArray₂`: scala.collection.mutable.ListBuffer[scala.Int] = `in₄`.expectArray[scala.Int]((() => `in₄`.expectInt())) + scala.jdk.CollectionConverters.SeqHasAsJava[scala.Int](`parsedArray₂`.toList).asJava.asInstanceOf[java.util.Collection[scala.Int]] + }))) + scala.jdk.CollectionConverters.SeqHasAsJava[java.util.ArrayList[scala.Int]](parsedArray.toList).asJava.asInstanceOf[java.util.Collection[java.util.ArrayList[scala.Int]]] + }) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₄`) + case _ => + `in₄`.skipValue() + } + maybeFieldNum = `in₄`.expectObjectField(__co_blocke_scalajack_json_collections_ArrayListHolder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.ArrayListHolder[java.util.ArrayList[scala.Int]][java.util.ArrayList[scala.Int]](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₄`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.ArrayListHolder[java.util.ArrayList[scala.Int]]] { + def encodeValue(`in₅`: co.blocke.scalajack.json.collections.ArrayListHolder[java.util.ArrayList[scala.Int]], `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₅`, `out₄`) + def decodeValue(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.ArrayListHolder[java.util.ArrayList[scala.Int]] = r0(`in₆`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.ArrayListHolder[java.util.ArrayList[scala.Int]]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_ArrayListHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: java.util.ArrayList[scala.util.Either[scala.Int, scala.Boolean]], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startArray() + scala.Predef.refArrayOps[java.lang.Object](in.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => if (elem.asInstanceOf[scala.util.Either[scala.Int, scala.Boolean]].==(null)) out.burpNull() else elem.asInstanceOf[scala.util.Either[scala.Int, scala.Boolean]] match { + case scala.Left(v) => + out.value(v.asInstanceOf[scala.Int]) + case scala.Right(v) => + out.value(`v₂`.asInstanceOf[scala.Boolean]) + })) + out.endArray() + } + def w0(`in₂`: co.blocke.scalajack.json.collections.ArrayListHolder[scala.util.Either[scala.Int, scala.Boolean]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `out₂`.label("a") + w1(`in₂`.a, `out₂`) + `out₂`.endObject() + } + def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.ArrayListHolder[scala.util.Either[scala.Int, scala.Boolean]] = { + var _a: java.util.ArrayList[scala.util.Either[scala.Int, scala.Boolean]] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_ArrayListHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.ArrayListHolder[scala.util.Either[scala.Int, scala.Boolean]]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₃`.expectNull()) null else new java.util.ArrayList[scala.util.Either[scala.Int, scala.Boolean]][scala.util.Either[scala.Int, scala.Boolean]]({ + val parsedArray: scala.collection.mutable.ListBuffer[scala.util.Either[scala.Int, scala.Boolean]] = `in₃`.expectArray[scala.util.Either[scala.Int, scala.Boolean]]((() => { + val mark: scala.Int = `in₃`.pos + if (`in₃`.expectNull()) null else scala.util.Try.apply[scala.Boolean](`in₃`.expectBoolean()) match { + case scala.util.Success(rval) => + scala.Right.apply[scala.Nothing, scala.Boolean](rval) + case scala.util.Failure(f) => + `in₃`.revertToPos(mark) + scala.util.Try.apply[scala.Int](`in₃`.expectInt()) match { + case scala.util.Success(lval) => + scala.Left.apply[scala.Int, scala.Nothing](lval) + case scala.util.Failure(_) => + `in₃`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₃`) + } + } + })) + scala.jdk.CollectionConverters.SeqHasAsJava[scala.util.Either[scala.Int, scala.Boolean]](parsedArray.toList).asJava.asInstanceOf[java.util.Collection[scala.util.Either[scala.Int, scala.Boolean]]] + }) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) + case _ => + `in₃`.skipValue() + } + maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_ArrayListHolder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.ArrayListHolder[scala.util.Either[scala.Int, scala.Boolean]][scala.util.Either[scala.Int, scala.Boolean]](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.ArrayListHolder[scala.util.Either[scala.Int, scala.Boolean]]] { + def encodeValue(`in₄`: co.blocke.scalajack.json.collections.ArrayListHolder[scala.util.Either[scala.Int, scala.Boolean]], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) + def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.ArrayListHolder[scala.util.Either[scala.Int, scala.Boolean]] = r0(`in₅`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.ArrayListHolder[scala.util.Either[scala.Int, scala.Boolean]]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_ArrayListHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: java.util.ArrayList[scala.Int | scala.Boolean], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startArray() + scala.Predef.refArrayOps[java.lang.Object](in.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => { + out.mark() + scala.util.Try.apply[scala.Unit](out.value(elem.asInstanceOf[scala.Int | scala.Boolean].asInstanceOf[scala.Boolean])) match { + case scala.util.Success(_) => + () + case scala.util.Failure(_) => + out.revert() + out.value(elem.asInstanceOf[scala.Int | scala.Boolean].asInstanceOf[scala.Int]) + } + })) + out.endArray() + } + def w0(`in₂`: co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int | scala.Boolean], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `out₂`.label("a") + w1(`in₂`.a, `out₂`) + `out₂`.endObject() + } + def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int | scala.Boolean] = { + var _a: java.util.ArrayList[scala.Int | scala.Boolean] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_ArrayListHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int | scala.Boolean]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₃`.expectNull()) null else new java.util.ArrayList[scala.Int | scala.Boolean][scala.Int | scala.Boolean]({ + val parsedArray: scala.collection.mutable.ListBuffer[scala.Int | scala.Boolean] = `in₃`.expectArray[scala.Int | scala.Boolean]((() => { + val mark: scala.Int = `in₃`.pos + scala.util.Try.apply[scala.Int](`in₃`.expectInt()) match { + case scala.util.Success(lval) => + (lval: scala.Int) + case scala.util.Failure(f) => + `in₃`.revertToPos(mark) + scala.util.Try.apply[scala.Boolean](`in₃`.expectBoolean()) match { + case scala.util.Success(rval) => + (rval: scala.Boolean) + case scala.util.Failure(_) => + `in₃`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₃`) + } + } + })) + scala.jdk.CollectionConverters.SeqHasAsJava[scala.Int | scala.Boolean](parsedArray.toList).asJava.asInstanceOf[java.util.Collection[scala.Int | scala.Boolean]] + }) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) + case _ => + `in₃`.skipValue() + } + maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_ArrayListHolder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int | scala.Boolean][scala.Int | scala.Boolean](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int | scala.Boolean]] { + def encodeValue(`in₄`: co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int | scala.Boolean], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) + def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int | scala.Boolean] = r0(`in₅`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int | scala.Boolean]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_ArrayListHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: java.util.ArrayList[scala.Option[scala.Int]], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startArray() + scala.Predef.refArrayOps[java.lang.Object](in.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => elem.asInstanceOf[scala.Option[scala.Int]] match { + case null => + out.burpNull() + case scala.None => + () + case scala.Some(v) => + val vv: scala.Int = v.asInstanceOf[scala.Int] + out.value(vv) + })) + out.endArray() + } + def w0(`in₂`: co.blocke.scalajack.json.collections.ArrayListHolder[scala.Option[scala.Int]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `out₂`.label("a") + w1(`in₂`.a, `out₂`) + `out₂`.endObject() + } + def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.ArrayListHolder[scala.Option[scala.Int]] = { + var _a: java.util.ArrayList[scala.Option[scala.Int]] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_ArrayListHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.ArrayListHolder[scala.Option[scala.Int]]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₃`.expectNull()) null else new java.util.ArrayList[scala.Option[scala.Int]][scala.Option[scala.Int]]({ + val parsedArray: scala.collection.mutable.ListBuffer[scala.Option[scala.Int]] = `in₃`.expectArray[scala.Option[scala.Int]]((() => if (`in₃`.expectNull()) null else scala.Some.apply[scala.Int](`in₃`.expectInt()))) + scala.jdk.CollectionConverters.SeqHasAsJava[scala.Option[scala.Int]](parsedArray.toList).asJava.asInstanceOf[java.util.Collection[scala.Option[scala.Int]]] + }) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) + case _ => + `in₃`.skipValue() + } + maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_ArrayListHolder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.ArrayListHolder[scala.Option[scala.Int]][scala.Option[scala.Int]](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.ArrayListHolder[scala.Option[scala.Int]]] { + def encodeValue(`in₄`: co.blocke.scalajack.json.collections.ArrayListHolder[scala.Option[scala.Int]], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) + def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.ArrayListHolder[scala.Option[scala.Int]] = r0(`in₅`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.ArrayListHolder[scala.Option[scala.Int]]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_ArrayListHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w2(in: scala.collection.immutable.Map[scala.Predef.String, scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + in.foreach[scala.Unit](((x$1: scala.Tuple2[java.lang.String, scala.Int]) => x$1 match { + case scala.Tuple2(key, value) => + { + out.maybeComma() + out.valueEscaped(key) + out.colon() + out.value(value) + } + })) + out.endObject() + } + def w1(`in₂`: java.util.ArrayList[scala.collection.immutable.Map[scala.Predef.String, scala.Int]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startArray() + scala.Predef.refArrayOps[java.lang.Object](`in₂`.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => w2(elem.asInstanceOf[scala.collection.immutable.Map[scala.Predef.String, scala.Int]], `out₂`))) + `out₂`.endArray() + } + def w0(`in₃`: co.blocke.scalajack.json.collections.ArrayListHolder[scala.collection.immutable.Map[scala.Predef.String, scala.Int]], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { + `out₃`.startObject() + `out₃`.label("a") + w1(`in₃`.a, `out₃`) + `out₃`.endObject() + } + def r0(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.ArrayListHolder[scala.collection.immutable.Map[scala.Predef.String, scala.Int]] = { + var _a: java.util.ArrayList[scala.collection.immutable.Map[scala.Predef.String, scala.Int]] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₄`.expectFirstObjectField(__co_blocke_scalajack_json_collections_ArrayListHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.ArrayListHolder[scala.collection.immutable.Map[scala.Predef.String, scala.Int]]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₄`.expectNull()) null else new java.util.ArrayList[scala.collection.immutable.Map[scala.Predef.String, scala.Int]][scala.collection.immutable.Map[scala.Predef.String, scala.Int]]({ + val parsedArray: scala.collection.mutable.ListBuffer[scala.collection.immutable.Map[scala.Predef.String, scala.Int]] = `in₄`.expectArray[scala.collection.immutable.Map[scala.Predef.String, scala.Int]]((() => if (`in₄`.expectNull()) null else { + `in₄`.expectToken('{') + `in₄`.parseMap[java.lang.String, scala.Int]((() => `in₄`.expectString()), (() => `in₄`.expectInt()), scala.Predef.Map.empty[java.lang.String, scala.Int], true) + })) + scala.jdk.CollectionConverters.SeqHasAsJava[scala.collection.immutable.Map[scala.Predef.String, scala.Int]](parsedArray.toList).asJava.asInstanceOf[java.util.Collection[scala.collection.immutable.Map[scala.Predef.String, scala.Int]]] + }) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₄`) + case _ => + `in₄`.skipValue() + } + maybeFieldNum = `in₄`.expectObjectField(__co_blocke_scalajack_json_collections_ArrayListHolder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.ArrayListHolder[scala.collection.immutable.Map[scala.Predef.String, scala.Int]][scala.collection.immutable.Map[scala.Predef.String, scala.Int]](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₄`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.ArrayListHolder[scala.collection.immutable.Map[scala.Predef.String, scala.Int]]] { + def encodeValue(`in₅`: co.blocke.scalajack.json.collections.ArrayListHolder[scala.collection.immutable.Map[scala.Predef.String, scala.Int]], `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₅`, `out₄`) + def decodeValue(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.ArrayListHolder[scala.collection.immutable.Map[scala.Predef.String, scala.Int]] = r0(`in₆`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.ArrayListHolder[scala.collection.immutable.Map[scala.Predef.String, scala.Int]]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_Person_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("name", "age")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + val __co_blocke_scalajack_json_collections_ArrayListHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w2(in: co.blocke.scalajack.json.collections.Person, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("name") + out.valueEscaped(in.name) + out.label("age") + out.value(in.age) + out.endObject() + } + def w1(`in₂`: java.util.ArrayList[co.blocke.scalajack.json.collections.Person], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startArray() + scala.Predef.refArrayOps[java.lang.Object](`in₂`.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => w2(elem.asInstanceOf[co.blocke.scalajack.json.collections.Person], `out₂`))) + `out₂`.endArray() + } + def w0(`in₃`: co.blocke.scalajack.json.collections.ArrayListHolder[co.blocke.scalajack.json.collections.Person], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { + `out₃`.startObject() + `out₃`.label("a") + w1(`in₃`.a, `out₃`) + `out₃`.endObject() + } + def r1(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.Person = { + var _name: java.lang.String = "" + var _age: scala.Int = 0 + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₄`.expectFirstObjectField(__co_blocke_scalajack_json_collections_Person_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.Person] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _name = `in₄`.expectString() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("name"), `in₄`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _age = `in₄`.expectInt() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("age"), `in₄`) + case _ => + `in₄`.skipValue() + } + maybeFieldNum = `in₄`.expectObjectField(__co_blocke_scalajack_json_collections_Person_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.collections.Person(_name, _age) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("name", "age")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₄`) + } + } + def r0(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.ArrayListHolder[co.blocke.scalajack.json.collections.Person] = { + var _a: java.util.ArrayList[co.blocke.scalajack.json.collections.Person] = null + var `required₂`: scala.Int = 1 + var `maybeFieldNum₂`: scala.Option[scala.Int] = `in₅`.expectFirstObjectField(__co_blocke_scalajack_json_collections_ArrayListHolder_fields) + if (`maybeFieldNum₂`.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.ArrayListHolder[co.blocke.scalajack.json.collections.Person]] else { + while (`maybeFieldNum₂`.isDefined) { + `maybeFieldNum₂`.get match { + case 0 => + if (`required₂`.&(1).!=(0)) { + `required₂` = `required₂`.^(1) + _a = if (`in₅`.expectNull()) null else new java.util.ArrayList[co.blocke.scalajack.json.collections.Person][co.blocke.scalajack.json.collections.Person]({ + val parsedArray: scala.collection.mutable.ListBuffer[co.blocke.scalajack.json.collections.Person] = `in₅`.expectArray[co.blocke.scalajack.json.collections.Person]((() => r1(`in₅`))) + scala.jdk.CollectionConverters.SeqHasAsJava[co.blocke.scalajack.json.collections.Person](parsedArray.toList).asJava.asInstanceOf[java.util.Collection[co.blocke.scalajack.json.collections.Person]] + }) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₅`) + case _ => + `in₅`.skipValue() + } + `maybeFieldNum₂` = `in₅`.expectObjectField(__co_blocke_scalajack_json_collections_ArrayListHolder_fields) + } + if (`required₂`.&(1).==(0)) new co.blocke.scalajack.json.collections.ArrayListHolder[co.blocke.scalajack.json.collections.Person][co.blocke.scalajack.json.collections.Person](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(`required₂`.&(1)))), `in₅`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.ArrayListHolder[co.blocke.scalajack.json.collections.Person]] { + def encodeValue(`in₆`: co.blocke.scalajack.json.collections.ArrayListHolder[co.blocke.scalajack.json.collections.Person], `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₆`, `out₄`) + def decodeValue(`in₇`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.ArrayListHolder[co.blocke.scalajack.json.collections.Person] = r0(`in₇`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.ArrayListHolder[co.blocke.scalajack.json.collections.Person]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_Holder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: java.util.concurrent.ArrayBlockingQueue[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startArray() + scala.Predef.refArrayOps[java.lang.Object](in.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => out.value(elem.asInstanceOf[scala.Int]))) + out.endArray() + } + def w0(`in₂`: co.blocke.scalajack.json.collections.Holder[java.util.concurrent.ArrayBlockingQueue[scala.Int]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `out₂`.label("a") + w1(`in₂`.a, `out₂`) + `out₂`.endObject() + } + def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.Holder[java.util.concurrent.ArrayBlockingQueue[scala.Int]] = { + var _a: java.util.concurrent.ArrayBlockingQueue[scala.Int] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_Holder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.Holder[java.util.concurrent.ArrayBlockingQueue[scala.Int]]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = { + val parsedArray: scala.collection.mutable.ListBuffer[scala.Int] = `in₃`.expectArray[scala.Int]((() => `in₃`.expectInt())) + if (parsedArray.==(null)) null else new java.util.concurrent.ArrayBlockingQueue[scala.Int](parsedArray.length, true, scala.jdk.CollectionConverters.SeqHasAsJava[scala.Int](parsedArray.toList).asJava) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) + case _ => + `in₃`.skipValue() + } + maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_Holder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.Holder[java.util.concurrent.ArrayBlockingQueue[scala.Int]][java.util.concurrent.ArrayBlockingQueue[scala.Int]](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.Holder[java.util.concurrent.ArrayBlockingQueue[scala.Int]]] { + def encodeValue(`in₄`: co.blocke.scalajack.json.collections.Holder[java.util.concurrent.ArrayBlockingQueue[scala.Int]], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) + def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.Holder[java.util.concurrent.ArrayBlockingQueue[scala.Int]] = r0(`in₅`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.Holder[java.util.concurrent.ArrayBlockingQueue[scala.Int]]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_Holder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: java.util.TreeSet[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startArray() + scala.Predef.refArrayOps[java.lang.Object](in.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => out.value(elem.asInstanceOf[scala.Int]))) + out.endArray() + } + def w0(`in₂`: co.blocke.scalajack.json.collections.Holder[java.util.TreeSet[scala.Int]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `out₂`.label("a") + w1(`in₂`.a, `out₂`) + `out₂`.endObject() + } + def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.Holder[java.util.TreeSet[scala.Int]] = { + var _a: java.util.TreeSet[scala.Int] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_Holder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.Holder[java.util.TreeSet[scala.Int]]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = { + val parsedArray: scala.collection.mutable.ListBuffer[scala.Int] = `in₃`.expectArray[scala.Int]((() => `in₃`.expectInt())) + if (parsedArray.==(null)) null else new java.util.TreeSet[scala.Int](scala.jdk.CollectionConverters.SeqHasAsJava[scala.Int](parsedArray.toList).asJava) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) + case _ => + `in₃`.skipValue() + } + maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_Holder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.Holder[java.util.TreeSet[scala.Int]][java.util.TreeSet[scala.Int]](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.Holder[java.util.TreeSet[scala.Int]]] { + def encodeValue(`in₄`: co.blocke.scalajack.json.collections.Holder[java.util.TreeSet[scala.Int]], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) + def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.Holder[java.util.TreeSet[scala.Int]] = r0(`in₅`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.Holder[java.util.TreeSet[scala.Int]]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_MapHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: scala.collection.immutable.Map[scala.Int, scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + in.foreach[scala.Unit](((x$1: scala.Tuple2[scala.Int, scala.Int]) => x$1 match { + case scala.Tuple2(key, value) => + { + out.maybeComma() + out.valueStringified(scala.Int.int2long(key)) + out.colon() + out.value(value) + } + })) + out.endObject() + } + def w0(`in₂`: co.blocke.scalajack.json.collections.MapHolder[scala.Int, scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `out₂`.label("a") + w1(`in₂`.a, `out₂`) + `out₂`.endObject() + } + def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Int, scala.Int] = { + var _a: scala.collection.immutable.Map[scala.Int, scala.Int] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.MapHolder[scala.Int, scala.Int]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₃`.expectNull()) null else { + `in₃`.expectToken('{') + `in₃`.parseMap[scala.Int, scala.Int]((() => scala.Predef.augmentString(`in₃`.expectString()).toInt), (() => `in₃`.expectInt()), scala.Predef.Map.empty[scala.Int, scala.Int], true) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) + case _ => + `in₃`.skipValue() + } + maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.MapHolder[scala.Int, scala.Int][scala.Int, scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Int, scala.Int]] { + def encodeValue(`in₄`: co.blocke.scalajack.json.collections.MapHolder[scala.Int, scala.Int], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) + def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Int, scala.Int] = r0(`in₅`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Int, scala.Int]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_MapHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: scala.collection.immutable.Map[scala.Predef.String, scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + in.foreach[scala.Unit](((x$1: scala.Tuple2[java.lang.String, scala.Int]) => x$1 match { + case scala.Tuple2(key, value) => + { + out.maybeComma() + out.valueEscaped(key) + out.colon() + out.value(value) + } + })) + out.endObject() + } + def w0(`in₂`: co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `out₂`.label("a") + w1(`in₂`.a, `out₂`) + `out₂`.endObject() + } + def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Int] = { + var _a: scala.collection.immutable.Map[scala.Predef.String, scala.Int] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Int]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₃`.expectNull()) null else { + `in₃`.expectToken('{') + `in₃`.parseMap[java.lang.String, scala.Int]((() => `in₃`.expectString()), (() => `in₃`.expectInt()), scala.Predef.Map.empty[java.lang.String, scala.Int], true) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) + case _ => + `in₃`.skipValue() + } + maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Int][java.lang.String, scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Int]] { + def encodeValue(`in₄`: co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Int], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) + def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Int] = r0(`in₅`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Int]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_MapHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: scala.collection.immutable.Map[scala.Long, scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + in.foreach[scala.Unit](((x$1: scala.Tuple2[scala.Long, scala.Int]) => x$1 match { + case scala.Tuple2(key, value) => + { + out.maybeComma() + out.valueStringified(key) + out.colon() + out.value(value) + } + })) + out.endObject() + } + def w0(`in₂`: co.blocke.scalajack.json.collections.MapHolder[scala.Long, scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `out₂`.label("a") + w1(`in₂`.a, `out₂`) + `out₂`.endObject() + } + def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Long, scala.Int] = { + var _a: scala.collection.immutable.Map[scala.Long, scala.Int] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.MapHolder[scala.Long, scala.Int]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₃`.expectNull()) null else { + `in₃`.expectToken('{') + `in₃`.parseMap[scala.Long, scala.Int]((() => scala.Predef.augmentString(`in₃`.expectString()).toLong), (() => `in₃`.expectInt()), scala.Predef.Map.empty[scala.Long, scala.Int], true) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) + case _ => + `in₃`.skipValue() + } + maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.MapHolder[scala.Long, scala.Int][scala.Long, scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Long, scala.Int]] { + def encodeValue(`in₄`: co.blocke.scalajack.json.collections.MapHolder[scala.Long, scala.Int], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) + def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Long, scala.Int] = r0(`in₅`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Long, scala.Int]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_MapHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: scala.collection.immutable.Map[scala.Boolean, scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + in.foreach[scala.Unit](((x$1: scala.Tuple2[scala.Boolean, scala.Int]) => x$1 match { + case scala.Tuple2(key, value) => + { + out.maybeComma() + out.valueStringified(key) + out.colon() + out.value(value) + } + })) + out.endObject() + } + def w0(`in₂`: co.blocke.scalajack.json.collections.MapHolder[scala.Boolean, scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `out₂`.label("a") + w1(`in₂`.a, `out₂`) + `out₂`.endObject() + } + def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Boolean, scala.Int] = { + var _a: scala.collection.immutable.Map[scala.Boolean, scala.Int] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.MapHolder[scala.Boolean, scala.Int]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₃`.expectNull()) null else { + `in₃`.expectToken('{') + `in₃`.parseMap[scala.Boolean, scala.Int]((() => scala.Predef.augmentString(`in₃`.expectString()).toBoolean), (() => `in₃`.expectInt()), scala.Predef.Map.empty[scala.Boolean, scala.Int], true) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) + case _ => + `in₃`.skipValue() + } + maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.MapHolder[scala.Boolean, scala.Int][scala.Boolean, scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Boolean, scala.Int]] { + def encodeValue(`in₄`: co.blocke.scalajack.json.collections.MapHolder[scala.Boolean, scala.Int], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) + def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Boolean, scala.Int] = r0(`in₅`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Boolean, scala.Int]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_MapHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: scala.collection.immutable.Map[java.util.UUID, scala.Predef.String], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + in.foreach[scala.Unit](((x$1: scala.Tuple2[java.util.UUID, java.lang.String]) => x$1 match { + case scala.Tuple2(key, value) => + { + out.maybeComma() + out.value(key) + out.colon() + out.valueEscaped(value) + } + })) + out.endObject() + } + def w0(`in₂`: co.blocke.scalajack.json.collections.MapHolder[java.util.UUID, scala.Predef.String], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `out₂`.label("a") + w1(`in₂`.a, `out₂`) + `out₂`.endObject() + } + def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[java.util.UUID, scala.Predef.String] = { + var _a: scala.collection.immutable.Map[java.util.UUID, scala.Predef.String] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.MapHolder[java.util.UUID, scala.Predef.String]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₃`.expectNull()) null else { + `in₃`.expectToken('{') + `in₃`.parseMap[java.util.UUID, java.lang.String]((() => `in₃`.expectString[java.util.UUID](((x$0: java.lang.String) => java.util.UUID.fromString(x$0)))), (() => `in₃`.expectString()), scala.Predef.Map.empty[java.util.UUID, java.lang.String], true) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) + case _ => + `in₃`.skipValue() + } + maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.MapHolder[java.util.UUID, scala.Predef.String][java.util.UUID, java.lang.String](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[java.util.UUID, scala.Predef.String]] { + def encodeValue(`in₄`: co.blocke.scalajack.json.collections.MapHolder[java.util.UUID, scala.Predef.String], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) + def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[java.util.UUID, scala.Predef.String] = r0(`in₅`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[java.util.UUID, scala.Predef.String]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_MapHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: scala.collection.immutable.Map[scala.Predef.String, scala.Predef.String], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + in.foreach[scala.Unit](((x$1: scala.Tuple2[java.lang.String, java.lang.String]) => x$1 match { + case scala.Tuple2(key, value) => + { + out.maybeComma() + out.valueEscaped(key) + out.colon() + out.valueEscaped(value) + } + })) + out.endObject() + } + def w0(`in₂`: co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Predef.String], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `out₂`.label("a") + w1(`in₂`.a, `out₂`) + `out₂`.endObject() + } + def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Predef.String] = { + var _a: scala.collection.immutable.Map[scala.Predef.String, scala.Predef.String] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Predef.String]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₃`.expectNull()) null else { + `in₃`.expectToken('{') + `in₃`.parseMap[java.lang.String, java.lang.String]((() => `in₃`.expectString()), (() => `in₃`.expectString()), scala.Predef.Map.empty[java.lang.String, java.lang.String], true) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) + case _ => + `in₃`.skipValue() + } + maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Predef.String][java.lang.String, java.lang.String](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Predef.String]] { + def encodeValue(`in₄`: co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Predef.String], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) + def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Predef.String] = r0(`in₅`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Predef.String]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_MapHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: scala.collection.immutable.Map[scala.Predef.String, scala.Long], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + in.foreach[scala.Unit](((x$1: scala.Tuple2[java.lang.String, scala.Long]) => x$1 match { + case scala.Tuple2(key, value) => + { + out.maybeComma() + out.valueEscaped(key) + out.colon() + out.value(value) + } + })) + out.endObject() + } + def w0(`in₂`: co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Long], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `out₂`.label("a") + w1(`in₂`.a, `out₂`) + `out₂`.endObject() + } + def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Long] = { + var _a: scala.collection.immutable.Map[scala.Predef.String, scala.Long] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Long]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₃`.expectNull()) null else { + `in₃`.expectToken('{') + `in₃`.parseMap[java.lang.String, scala.Long]((() => `in₃`.expectString()), (() => `in₃`.expectLong()), scala.Predef.Map.empty[java.lang.String, scala.Long], true) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) + case _ => + `in₃`.skipValue() + } + maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Long][java.lang.String, scala.Long](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Long]] { + def encodeValue(`in₄`: co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Long], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) + def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Long] = r0(`in₅`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Long]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_MapHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: scala.collection.immutable.Map[scala.Predef.String, scala.Boolean], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + in.foreach[scala.Unit](((x$1: scala.Tuple2[java.lang.String, scala.Boolean]) => x$1 match { + case scala.Tuple2(key, value) => + { + out.maybeComma() + out.valueEscaped(key) + out.colon() + out.value(value) + } + })) + out.endObject() + } + def w0(`in₂`: co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Boolean], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `out₂`.label("a") + w1(`in₂`.a, `out₂`) + `out₂`.endObject() + } + def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Boolean] = { + var _a: scala.collection.immutable.Map[scala.Predef.String, scala.Boolean] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Boolean]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₃`.expectNull()) null else { + `in₃`.expectToken('{') + `in₃`.parseMap[java.lang.String, scala.Boolean]((() => `in₃`.expectString()), (() => `in₃`.expectBoolean()), scala.Predef.Map.empty[java.lang.String, scala.Boolean], true) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) + case _ => + `in₃`.skipValue() + } + maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Boolean][java.lang.String, scala.Boolean](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Boolean]] { + def encodeValue(`in₄`: co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Boolean], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) + def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Boolean] = r0(`in₅`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Boolean]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_MapHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: scala.collection.immutable.Map[co.blocke.scalajack.json.collections.Model$package.OnOff, co.blocke.scalajack.json.collections.Model$package.Counter], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + in.foreach[scala.Unit](((x$1: scala.Tuple2[co.blocke.scalajack.json.collections.Model$package.OnOff, co.blocke.scalajack.json.collections.Model$package.Counter]) => x$1 match { + case scala.Tuple2(key, value) => + { + out.maybeComma() + out.valueStringified(key.asInstanceOf[scala.Boolean]) + out.colon() + out.value(value.asInstanceOf[scala.Short]) + } + })) + out.endObject() + } + def w0(`in₂`: co.blocke.scalajack.json.collections.MapHolder[co.blocke.scalajack.json.collections.Model$package.OnOff, co.blocke.scalajack.json.collections.Model$package.Counter], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `out₂`.label("a") + w1(`in₂`.a, `out₂`) + `out₂`.endObject() + } + def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[co.blocke.scalajack.json.collections.Model$package.OnOff, co.blocke.scalajack.json.collections.Model$package.Counter] = { + var _a: scala.collection.immutable.Map[co.blocke.scalajack.json.collections.Model$package.OnOff, co.blocke.scalajack.json.collections.Model$package.Counter] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.MapHolder[co.blocke.scalajack.json.collections.Model$package.OnOff, co.blocke.scalajack.json.collections.Model$package.Counter]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₃`.expectNull()) null else { + `in₃`.expectToken('{') + `in₃`.parseMap[co.blocke.scalajack.json.collections.Model$package.OnOff, co.blocke.scalajack.json.collections.Model$package.Counter]((() => scala.Predef.augmentString(`in₃`.expectString()).toBoolean.asInstanceOf[co.blocke.scalajack.json.collections.Model$package.OnOff]), (() => `in₃`.expectInt().toShort.asInstanceOf[co.blocke.scalajack.json.collections.Model$package.Counter]), scala.Predef.Map.empty[co.blocke.scalajack.json.collections.Model$package.OnOff, co.blocke.scalajack.json.collections.Model$package.Counter], true) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) + case _ => + `in₃`.skipValue() + } + maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.MapHolder[co.blocke.scalajack.json.collections.Model$package.OnOff, co.blocke.scalajack.json.collections.Model$package.Counter][co.blocke.scalajack.json.collections.Model$package.OnOff, co.blocke.scalajack.json.collections.Model$package.Counter](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[co.blocke.scalajack.json.collections.Model$package.OnOff, co.blocke.scalajack.json.collections.Model$package.Counter]] { + def encodeValue(`in₄`: co.blocke.scalajack.json.collections.MapHolder[co.blocke.scalajack.json.collections.Model$package.OnOff, co.blocke.scalajack.json.collections.Model$package.Counter], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) + def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[co.blocke.scalajack.json.collections.Model$package.OnOff, co.blocke.scalajack.json.collections.Model$package.Counter] = r0(`in₅`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[co.blocke.scalajack.json.collections.Model$package.OnOff, co.blocke.scalajack.json.collections.Model$package.Counter]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_MapHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: scala.collection.immutable.Map[scala.Predef.String, java.util.UUID], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + in.foreach[scala.Unit](((x$1: scala.Tuple2[java.lang.String, java.util.UUID]) => x$1 match { + case scala.Tuple2(key, value) => + { + out.maybeComma() + out.valueEscaped(key) + out.colon() + out.value(value) + } + })) + out.endObject() + } + def w0(`in₂`: co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, java.util.UUID], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `out₂`.label("a") + w1(`in₂`.a, `out₂`) + `out₂`.endObject() + } + def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, java.util.UUID] = { + var _a: scala.collection.immutable.Map[scala.Predef.String, java.util.UUID] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, java.util.UUID]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₃`.expectNull()) null else { + `in₃`.expectToken('{') + `in₃`.parseMap[java.lang.String, java.util.UUID]((() => `in₃`.expectString()), (() => `in₃`.expectString[java.util.UUID](((x$0: java.lang.String) => java.util.UUID.fromString(x$0)))), scala.Predef.Map.empty[java.lang.String, java.util.UUID], true) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) + case _ => + `in₃`.skipValue() + } + maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, java.util.UUID][java.lang.String, java.util.UUID](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, java.util.UUID]] { + def encodeValue(`in₄`: co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, java.util.UUID], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) + def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, java.util.UUID] = r0(`in₅`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, java.util.UUID]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_MapHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w2(in: scala.collection.immutable.List[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startArray() + in.foreach[scala.Unit](((i: scala.Int) => out.value(i))) + out.endArray() + } + def w1(`in₂`: scala.collection.immutable.Map[scala.Predef.String, scala.collection.immutable.List[scala.Int]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `in₂`.foreach[scala.Unit](((x$1: scala.Tuple2[java.lang.String, scala.collection.immutable.List[scala.Int]]) => x$1 match { + case scala.Tuple2(key, value) => + { + `out₂`.maybeComma() + `out₂`.valueEscaped(key) + `out₂`.colon() + w2(value, `out₂`) + } + })) + `out₂`.endObject() + } + def w0(`in₃`: co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.collection.immutable.List[scala.Int]], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { + `out₃`.startObject() + `out₃`.label("a") + w1(`in₃`.a, `out₃`) + `out₃`.endObject() + } + def r0(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.collection.immutable.List[scala.Int]] = { + var _a: scala.collection.immutable.Map[scala.Predef.String, scala.collection.immutable.List[scala.Int]] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₄`.expectFirstObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.collection.immutable.List[scala.Int]]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₄`.expectNull()) null else { + `in₄`.expectToken('{') + `in₄`.parseMap[java.lang.String, scala.collection.immutable.List[scala.Int]]((() => `in₄`.expectString()), (() => { + val parsedArray: scala.collection.mutable.ListBuffer[scala.Int] = `in₄`.expectArray[scala.Int]((() => `in₄`.expectInt())) + + (if (parsedArray.!=(null)) parsedArray.toList else null: scala.collection.immutable.List[scala.Int]) + }), scala.Predef.Map.empty[java.lang.String, scala.collection.immutable.List[scala.Int]], true) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₄`) + case _ => + `in₄`.skipValue() + } + maybeFieldNum = `in₄`.expectObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.collection.immutable.List[scala.Int]][java.lang.String, scala.collection.immutable.List[scala.Int]](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₄`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.collection.immutable.List[scala.Int]]] { + def encodeValue(`in₅`: co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.collection.immutable.List[scala.Int]], `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₅`, `out₄`) + def decodeValue(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.collection.immutable.List[scala.Int]] = r0(`in₆`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.collection.immutable.List[scala.Int]]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_MapHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w2(in: scala.collection.immutable.Map[scala.Predef.String, scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + in.foreach[scala.Unit](((x$1: scala.Tuple2[java.lang.String, scala.Int]) => x$1 match { + case scala.Tuple2(key, value) => + { + out.maybeComma() + out.valueEscaped(key) + out.colon() + out.value(value) + } + })) + out.endObject() + } + def w1(`in₂`: scala.collection.immutable.Map[scala.Predef.String, scala.collection.immutable.Map[scala.Predef.String, scala.Int]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `in₂`.foreach[scala.Unit](((`x$1₂`: scala.Tuple2[java.lang.String, scala.collection.immutable.Map[scala.Predef.String, scala.Int]]) => `x$1₂` match { + case scala.Tuple2(key, value) => + { + `out₂`.maybeComma() + `out₂`.valueEscaped(`key₂`) + `out₂`.colon() + w2(`value₂`, `out₂`) + } + })) + `out₂`.endObject() + } + def w0(`in₃`: co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.collection.immutable.Map[scala.Predef.String, scala.Int]], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { + `out₃`.startObject() + `out₃`.label("a") + w1(`in₃`.a, `out₃`) + `out₃`.endObject() + } + def r0(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.collection.immutable.Map[scala.Predef.String, scala.Int]] = { + var _a: scala.collection.immutable.Map[scala.Predef.String, scala.collection.immutable.Map[scala.Predef.String, scala.Int]] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₄`.expectFirstObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.collection.immutable.Map[scala.Predef.String, scala.Int]]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₄`.expectNull()) null else { + `in₄`.expectToken('{') + `in₄`.parseMap[java.lang.String, scala.collection.immutable.Map[scala.Predef.String, scala.Int]]((() => `in₄`.expectString()), (() => if (`in₄`.expectNull()) null else { + `in₄`.expectToken('{') + `in₄`.parseMap[java.lang.String, scala.Int]((() => `in₄`.expectString()), (() => `in₄`.expectInt()), scala.Predef.Map.empty[java.lang.String, scala.Int], true) + }), scala.Predef.Map.empty[java.lang.String, scala.collection.immutable.Map[scala.Predef.String, scala.Int]], true) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₄`) + case _ => + `in₄`.skipValue() + } + maybeFieldNum = `in₄`.expectObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.collection.immutable.Map[scala.Predef.String, scala.Int]][java.lang.String, scala.collection.immutable.Map[scala.Predef.String, scala.Int]](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₄`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.collection.immutable.Map[scala.Predef.String, scala.Int]]] { + def encodeValue(`in₅`: co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.collection.immutable.Map[scala.Predef.String, scala.Int]], `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₅`, `out₄`) + def decodeValue(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.collection.immutable.Map[scala.Predef.String, scala.Int]] = r0(`in₆`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.collection.immutable.Map[scala.Predef.String, scala.Int]]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_MapHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w2(in: scala.collection.immutable.List[scala.Predef.String], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startArray() + in.foreach[scala.Unit](((i: java.lang.String) => out.valueEscaped(i))) + out.endArray() + } + def w1(`in₂`: scala.collection.immutable.Map[scala.Predef.String, scala.Int | scala.collection.immutable.List[scala.Predef.String]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `in₂`.foreach[scala.Unit](((x$1: scala.Tuple2[java.lang.String, scala.Int | scala.collection.immutable.List[scala.Predef.String]]) => x$1 match { + case scala.Tuple2(key, value) => + if (value.==(null)) `out₂`.burpNull() else { + `out₂`.mark() + scala.util.Try.apply[scala.Unit]({ + `out₂`.maybeComma() + `out₂`.valueEscaped(key) + `out₂`.colon() + w2(value.asInstanceOf[scala.collection.immutable.List[scala.Predef.String]], `out₂`) + }) match { + case scala.util.Success(_) => + () + case scala.util.Failure(f) => + `out₂`.revert() + { + `out₂`.maybeComma() + `out₂`.valueEscaped(key) + `out₂`.colon() + `out₂`.value(value.asInstanceOf[scala.Int]) + } + } + } + })) + `out₂`.endObject() + } + def w0(`in₃`: co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Int | scala.collection.immutable.List[scala.Predef.String]], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { + `out₃`.startObject() + `out₃`.label("a") + w1(`in₃`.a, `out₃`) + `out₃`.endObject() + } + def r0(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Int | scala.collection.immutable.List[scala.Predef.String]] = { + var _a: scala.collection.immutable.Map[scala.Predef.String, scala.Int | scala.collection.immutable.List[scala.Predef.String]] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₄`.expectFirstObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Int | scala.collection.immutable.List[scala.Predef.String]]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₄`.expectNull()) null else { + `in₄`.expectToken('{') + `in₄`.parseMap[java.lang.String, scala.Int | scala.collection.immutable.List[scala.Predef.String]]((() => `in₄`.expectString()), (() => { + val mark: scala.Int = `in₄`.pos + scala.util.Try.apply[scala.Int](`in₄`.expectInt()) match { + case scala.util.Success(lval) => + (lval: scala.Int) + case scala.util.Failure(f) => + `in₄`.revertToPos(mark) + scala.util.Try.apply[scala.collection.immutable.List[scala.Predef.String]]({ + val parsedArray: scala.collection.mutable.ListBuffer[java.lang.String] = `in₄`.expectArray[java.lang.String]((() => `in₄`.expectString())) + + (if (parsedArray.!=(null)) parsedArray.toList else null: scala.collection.immutable.List[java.lang.String]) + }) match { + case scala.util.Success(rval) => + (rval: scala.collection.immutable.List[scala.Predef.String]) + case scala.util.Failure(_) => + `in₄`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₄`) + } + } + }), scala.Predef.Map.empty[java.lang.String, scala.Int | scala.collection.immutable.List[scala.Predef.String]], true) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₄`) + case _ => + `in₄`.skipValue() + } + maybeFieldNum = `in₄`.expectObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Int | scala.collection.immutable.List[scala.Predef.String]][java.lang.String, scala.Int | scala.collection.immutable.List[scala.Predef.String]](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₄`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Int | scala.collection.immutable.List[scala.Predef.String]]] { + def encodeValue(`in₅`: co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Int | scala.collection.immutable.List[scala.Predef.String]], `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₅`, `out₄`) + def decodeValue(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Int | scala.collection.immutable.List[scala.Predef.String]] = r0(`in₆`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Int | scala.collection.immutable.List[scala.Predef.String]]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_Person_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("name", "age")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + val __co_blocke_scalajack_json_collections_MapHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w2(in: co.blocke.scalajack.json.collections.Person, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("name") + out.valueEscaped(in.name) + out.label("age") + out.value(in.age) + out.endObject() + } + def w1(`in₂`: scala.collection.immutable.Map[scala.Predef.String, co.blocke.scalajack.json.collections.Person], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `in₂`.foreach[scala.Unit](((x$1: scala.Tuple2[java.lang.String, co.blocke.scalajack.json.collections.Person]) => x$1 match { + case scala.Tuple2(key, value) => + { + `out₂`.maybeComma() + `out₂`.valueEscaped(key) + `out₂`.colon() + w2(value, `out₂`) + } + })) + `out₂`.endObject() + } + def w0(`in₃`: co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Person], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { + `out₃`.startObject() + `out₃`.label("a") + w1(`in₃`.a, `out₃`) + `out₃`.endObject() + } + def r1(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.Person = { + var _name: java.lang.String = "" + var _age: scala.Int = 0 + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₄`.expectFirstObjectField(__co_blocke_scalajack_json_collections_Person_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.Person] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _name = `in₄`.expectString() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("name"), `in₄`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _age = `in₄`.expectInt() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("age"), `in₄`) + case _ => + `in₄`.skipValue() + } + maybeFieldNum = `in₄`.expectObjectField(__co_blocke_scalajack_json_collections_Person_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.collections.Person(_name, _age) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("name", "age")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₄`) + } + } + def r0(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Person] = { + var _a: scala.collection.immutable.Map[scala.Predef.String, co.blocke.scalajack.json.collections.Person] = null + var `required₂`: scala.Int = 1 + var `maybeFieldNum₂`: scala.Option[scala.Int] = `in₅`.expectFirstObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) + if (`maybeFieldNum₂`.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Person]] else { + while (`maybeFieldNum₂`.isDefined) { + `maybeFieldNum₂`.get match { + case 0 => + if (`required₂`.&(1).!=(0)) { + `required₂` = `required₂`.^(1) + _a = if (`in₅`.expectNull()) null else { + `in₅`.expectToken('{') + `in₅`.parseMap[java.lang.String, co.blocke.scalajack.json.collections.Person]((() => `in₅`.expectString()), (() => r1(`in₅`)), scala.Predef.Map.empty[java.lang.String, co.blocke.scalajack.json.collections.Person], true) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₅`) + case _ => + `in₅`.skipValue() + } + `maybeFieldNum₂` = `in₅`.expectObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) + } + if (`required₂`.&(1).==(0)) new co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Person][java.lang.String, co.blocke.scalajack.json.collections.Person](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(`required₂`.&(1)))), `in₅`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Person]] { + def encodeValue(`in₆`: co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Person], `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₆`, `out₄`) + def decodeValue(`in₇`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Person] = r0(`in₇`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Person]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_MapHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: scala.collection.immutable.Map[co.blocke.scalajack.json.collections.Distance, scala.Predef.String], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + in.foreach[scala.Unit](((x$1: scala.Tuple2[co.blocke.scalajack.json.collections.Distance, java.lang.String]) => x$1 match { + case scala.Tuple2(key, value) => + { + out.maybeComma() + out.valueStringified(key.meters) + out.colon() + out.valueEscaped(value) + } + })) + out.endObject() + } + def w0(`in₂`: co.blocke.scalajack.json.collections.MapHolder[co.blocke.scalajack.json.collections.Distance, scala.Predef.String], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `out₂`.label("a") + w1(`in₂`.a, `out₂`) + `out₂`.endObject() + } + def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[co.blocke.scalajack.json.collections.Distance, scala.Predef.String] = { + var _a: scala.collection.immutable.Map[co.blocke.scalajack.json.collections.Distance, scala.Predef.String] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.MapHolder[co.blocke.scalajack.json.collections.Distance, scala.Predef.String]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₃`.expectNull()) null else { + `in₃`.expectToken('{') + `in₃`.parseMap[co.blocke.scalajack.json.collections.Distance, java.lang.String]((() => new co.blocke.scalajack.json.collections.Distance(scala.Predef.augmentString(`in₃`.expectString()).toDouble)), (() => `in₃`.expectString()), scala.Predef.Map.empty[co.blocke.scalajack.json.collections.Distance, java.lang.String], true) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) + case _ => + `in₃`.skipValue() + } + maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.MapHolder[co.blocke.scalajack.json.collections.Distance, scala.Predef.String][co.blocke.scalajack.json.collections.Distance, java.lang.String](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[co.blocke.scalajack.json.collections.Distance, scala.Predef.String]] { + def encodeValue(`in₄`: co.blocke.scalajack.json.collections.MapHolder[co.blocke.scalajack.json.collections.Distance, scala.Predef.String], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) + def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[co.blocke.scalajack.json.collections.Distance, scala.Predef.String] = r0(`in₅`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[co.blocke.scalajack.json.collections.Distance, scala.Predef.String]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_MapHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: scala.collection.immutable.Map[scala.Predef.String, co.blocke.scalajack.json.collections.Distance], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + in.foreach[scala.Unit](((x$1: scala.Tuple2[java.lang.String, co.blocke.scalajack.json.collections.Distance]) => x$1 match { + case scala.Tuple2(key, value) => + { + out.maybeComma() + out.valueEscaped(key) + out.colon() + out.value(value.meters) + } + })) + out.endObject() + } + def w0(`in₂`: co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Distance], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `out₂`.label("a") + w1(`in₂`.a, `out₂`) + `out₂`.endObject() + } + def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Distance] = { + var _a: scala.collection.immutable.Map[scala.Predef.String, co.blocke.scalajack.json.collections.Distance] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Distance]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₃`.expectNull()) null else { + `in₃`.expectToken('{') + `in₃`.parseMap[java.lang.String, co.blocke.scalajack.json.collections.Distance]((() => `in₃`.expectString()), (() => new co.blocke.scalajack.json.collections.Distance(`in₃`.expectDouble())), scala.Predef.Map.empty[java.lang.String, co.blocke.scalajack.json.collections.Distance], true) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) + case _ => + `in₃`.skipValue() + } + maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Distance][java.lang.String, co.blocke.scalajack.json.collections.Distance](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Distance]] { + def encodeValue(`in₄`: co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Distance], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) + def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Distance] = r0(`in₅`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Distance]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_MMapHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: scala.collection.mutable.Map[scala.Predef.String, co.blocke.scalajack.json.collections.Distance], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + in.foreach[scala.Unit](((x$1: scala.Tuple2[java.lang.String, co.blocke.scalajack.json.collections.Distance]) => x$1 match { + case scala.Tuple2(key, value) => + { + out.maybeComma() + out.valueEscaped(key) + out.colon() + out.value(value.meters) + } + })) + out.endObject() + } + def w0(`in₂`: co.blocke.scalajack.json.collections.MMapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Distance], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `out₂`.label("a") + w1(`in₂`.a, `out₂`) + `out₂`.endObject() + } + def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MMapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Distance] = { + var _a: scala.collection.mutable.Map[scala.Predef.String, co.blocke.scalajack.json.collections.Distance] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_MMapHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.MMapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Distance]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₃`.expectNull()) null else { + `in₃`.expectToken('{') + scala.collection.mutable.Map.from[java.lang.String, co.blocke.scalajack.json.collections.Distance](`in₃`.parseMap[java.lang.String, co.blocke.scalajack.json.collections.Distance]((() => `in₃`.expectString()), (() => new co.blocke.scalajack.json.collections.Distance(`in₃`.expectDouble())), scala.Predef.Map.empty[java.lang.String, co.blocke.scalajack.json.collections.Distance], true)) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) + case _ => + `in₃`.skipValue() + } + maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_MMapHolder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.MMapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Distance][java.lang.String, co.blocke.scalajack.json.collections.Distance](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MMapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Distance]] { + def encodeValue(`in₄`: co.blocke.scalajack.json.collections.MMapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Distance], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) + def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MMapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Distance] = r0(`in₅`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MMapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Distance]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_MMapHolder2_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: scala.collection.mutable.HashMap[scala.Predef.String, co.blocke.scalajack.json.collections.Distance], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + in.foreach[scala.Unit](((x$1: scala.Tuple2[java.lang.String, co.blocke.scalajack.json.collections.Distance]) => x$1 match { + case scala.Tuple2(key, value) => + { + out.maybeComma() + out.valueEscaped(key) + out.colon() + out.value(value.meters) + } + })) + out.endObject() + } + def w0(`in₂`: co.blocke.scalajack.json.collections.MMapHolder2[scala.Predef.String, co.blocke.scalajack.json.collections.Distance], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `out₂`.label("a") + w1(`in₂`.a, `out₂`) + `out₂`.endObject() + } + def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MMapHolder2[scala.Predef.String, co.blocke.scalajack.json.collections.Distance] = { + var _a: scala.collection.mutable.HashMap[scala.Predef.String, co.blocke.scalajack.json.collections.Distance] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_MMapHolder2_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.MMapHolder2[scala.Predef.String, co.blocke.scalajack.json.collections.Distance]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₃`.expectNull()) null else { + `in₃`.expectToken('{') + scala.collection.mutable.HashMap.from[java.lang.String, co.blocke.scalajack.json.collections.Distance](`in₃`.parseMap[java.lang.String, co.blocke.scalajack.json.collections.Distance]((() => `in₃`.expectString()), (() => new co.blocke.scalajack.json.collections.Distance(`in₃`.expectDouble())), scala.Predef.Map.empty[java.lang.String, co.blocke.scalajack.json.collections.Distance], true)) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) + case _ => + `in₃`.skipValue() + } + maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_MMapHolder2_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.MMapHolder2[scala.Predef.String, co.blocke.scalajack.json.collections.Distance][java.lang.String, co.blocke.scalajack.json.collections.Distance](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MMapHolder2[scala.Predef.String, co.blocke.scalajack.json.collections.Distance]] { + def encodeValue(`in₄`: co.blocke.scalajack.json.collections.MMapHolder2[scala.Predef.String, co.blocke.scalajack.json.collections.Distance], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) + def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MMapHolder2[scala.Predef.String, co.blocke.scalajack.json.collections.Distance] = r0(`in₅`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MMapHolder2[scala.Predef.String, co.blocke.scalajack.json.collections.Distance]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_MMapHolder3_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: scala.collection.mutable.SeqMap[scala.Predef.String, co.blocke.scalajack.json.collections.Distance], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + in.foreach[scala.Unit](((x$1: scala.Tuple2[java.lang.String, co.blocke.scalajack.json.collections.Distance]) => x$1 match { + case scala.Tuple2(key, value) => + { + out.maybeComma() + out.valueEscaped(key) + out.colon() + out.value(value.meters) + } + })) + out.endObject() + } + def w0(`in₂`: co.blocke.scalajack.json.collections.MMapHolder3[scala.Predef.String, co.blocke.scalajack.json.collections.Distance], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `out₂`.label("a") + w1(`in₂`.a, `out₂`) + `out₂`.endObject() + } + def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MMapHolder3[scala.Predef.String, co.blocke.scalajack.json.collections.Distance] = { + var _a: scala.collection.mutable.SeqMap[scala.Predef.String, co.blocke.scalajack.json.collections.Distance] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_MMapHolder3_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.MMapHolder3[scala.Predef.String, co.blocke.scalajack.json.collections.Distance]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₃`.expectNull()) null else { + `in₃`.expectToken('{') + scala.collection.mutable.Map.from[java.lang.String, co.blocke.scalajack.json.collections.Distance](`in₃`.parseMap[java.lang.String, co.blocke.scalajack.json.collections.Distance]((() => `in₃`.expectString()), (() => new co.blocke.scalajack.json.collections.Distance(`in₃`.expectDouble())), scala.Predef.Map.empty[java.lang.String, co.blocke.scalajack.json.collections.Distance], true)).to[scala.collection.mutable.SeqMap[scala.Predef.String, co.blocke.scalajack.json.collections.Distance]](scala.collection.mutable.SeqMap.mapFactory[java.lang.String, co.blocke.scalajack.json.collections.Distance]) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) + case _ => + `in₃`.skipValue() + } + maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_MMapHolder3_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.MMapHolder3[scala.Predef.String, co.blocke.scalajack.json.collections.Distance][java.lang.String, co.blocke.scalajack.json.collections.Distance](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MMapHolder3[scala.Predef.String, co.blocke.scalajack.json.collections.Distance]] { + def encodeValue(`in₄`: co.blocke.scalajack.json.collections.MMapHolder3[scala.Predef.String, co.blocke.scalajack.json.collections.Distance], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) + def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MMapHolder3[scala.Predef.String, co.blocke.scalajack.json.collections.Distance] = r0(`in₅`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MMapHolder3[scala.Predef.String, co.blocke.scalajack.json.collections.Distance]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_MapHolder2_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: scala.collection.immutable.HashMap[scala.Predef.String, co.blocke.scalajack.json.collections.Distance], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + in.foreach[scala.Unit](((x$1: scala.Tuple2[java.lang.String, co.blocke.scalajack.json.collections.Distance]) => x$1 match { + case scala.Tuple2(key, value) => + { + out.maybeComma() + out.valueEscaped(key) + out.colon() + out.value(value.meters) + } + })) + out.endObject() + } + def w0(`in₂`: co.blocke.scalajack.json.collections.MapHolder2[scala.Predef.String, co.blocke.scalajack.json.collections.Distance], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `out₂`.label("a") + w1(`in₂`.a, `out₂`) + `out₂`.endObject() + } + def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder2[scala.Predef.String, co.blocke.scalajack.json.collections.Distance] = { + var _a: scala.collection.immutable.HashMap[scala.Predef.String, co.blocke.scalajack.json.collections.Distance] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_MapHolder2_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.MapHolder2[scala.Predef.String, co.blocke.scalajack.json.collections.Distance]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₃`.expectNull()) null else { + `in₃`.expectToken('{') + scala.collection.immutable.HashMap.from[java.lang.String, co.blocke.scalajack.json.collections.Distance](`in₃`.parseMap[java.lang.String, co.blocke.scalajack.json.collections.Distance]((() => `in₃`.expectString()), (() => new co.blocke.scalajack.json.collections.Distance(`in₃`.expectDouble())), scala.Predef.Map.empty[java.lang.String, co.blocke.scalajack.json.collections.Distance], true)) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) + case _ => + `in₃`.skipValue() + } + maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_MapHolder2_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.MapHolder2[scala.Predef.String, co.blocke.scalajack.json.collections.Distance][java.lang.String, co.blocke.scalajack.json.collections.Distance](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder2[scala.Predef.String, co.blocke.scalajack.json.collections.Distance]] { + def encodeValue(`in₄`: co.blocke.scalajack.json.collections.MapHolder2[scala.Predef.String, co.blocke.scalajack.json.collections.Distance], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) + def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder2[scala.Predef.String, co.blocke.scalajack.json.collections.Distance] = r0(`in₅`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder2[scala.Predef.String, co.blocke.scalajack.json.collections.Distance]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_MapHolder3_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: scala.collection.immutable.SeqMap[scala.Predef.String, co.blocke.scalajack.json.collections.Distance], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + in.foreach[scala.Unit](((x$1: scala.Tuple2[java.lang.String, co.blocke.scalajack.json.collections.Distance]) => x$1 match { + case scala.Tuple2(key, value) => + { + out.maybeComma() + out.valueEscaped(key) + out.colon() + out.value(value.meters) + } + })) + out.endObject() + } + def w0(`in₂`: co.blocke.scalajack.json.collections.MapHolder3[scala.Predef.String, co.blocke.scalajack.json.collections.Distance], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `out₂`.label("a") + w1(`in₂`.a, `out₂`) + `out₂`.endObject() + } + def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder3[scala.Predef.String, co.blocke.scalajack.json.collections.Distance] = { + var _a: scala.collection.immutable.SeqMap[scala.Predef.String, co.blocke.scalajack.json.collections.Distance] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_MapHolder3_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.MapHolder3[scala.Predef.String, co.blocke.scalajack.json.collections.Distance]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₃`.expectNull()) null else { + `in₃`.expectToken('{') + scala.collection.immutable.SeqMap.from[java.lang.String, co.blocke.scalajack.json.collections.Distance](`in₃`.parseMap[java.lang.String, co.blocke.scalajack.json.collections.Distance]((() => `in₃`.expectString()), (() => new co.blocke.scalajack.json.collections.Distance(`in₃`.expectDouble())), scala.Predef.Map.empty[java.lang.String, co.blocke.scalajack.json.collections.Distance], true)) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) + case _ => + `in₃`.skipValue() + } + maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_MapHolder3_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.MapHolder3[scala.Predef.String, co.blocke.scalajack.json.collections.Distance][java.lang.String, co.blocke.scalajack.json.collections.Distance](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder3[scala.Predef.String, co.blocke.scalajack.json.collections.Distance]] { + def encodeValue(`in₄`: co.blocke.scalajack.json.collections.MapHolder3[scala.Predef.String, co.blocke.scalajack.json.collections.Distance], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) + def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder3[scala.Predef.String, co.blocke.scalajack.json.collections.Distance] = r0(`in₅`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder3[scala.Predef.String, co.blocke.scalajack.json.collections.Distance]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_TupleHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: scala.Tuple3[scala.Int, scala.Predef.String, scala.Boolean], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startArray() + out.value(in._1) + out.valueEscaped(in._2) + out.value(in._3) + out.endArray() + } + def w0(`in₂`: co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `out₂`.label("a") + w1(`in₂`.a, `out₂`) + `out₂`.endObject() + } + def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean] = { + var _a: scala.Tuple3[scala.Int, scala.Predef.String, scala.Boolean] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_TupleHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₃`.expectNull()) null else { + `in₃`.expectToken('[') + val tv: scala.Tuple3[scala.Int, scala.Predef.String, scala.Boolean] = new scala.Tuple3[scala.Int, scala.Predef.String, scala.Boolean][scala.Int, java.lang.String, scala.Boolean](`in₃`.expectInt(), { + `in₃`.expectToken(',') + `in₃`.expectString() + }, { + `in₃`.expectToken(',') + `in₃`.expectBoolean() + }) + `in₃`.expectToken(']') + + (tv: scala.Tuple3[scala.Int, scala.Predef.String, scala.Boolean]) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) + case _ => + `in₃`.skipValue() + } + maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_TupleHolder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean][scala.Int, java.lang.String, scala.Boolean](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean]] { + def encodeValue(`in₄`: co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) + def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean] = r0(`in₅`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_TupleHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: scala.Tuple3[scala.Int, scala.Predef.String, scala.Boolean], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startArray() + out.value(in._1) + out.valueEscaped(in._2) + out.value(in._3) + out.endArray() + } + def w0(`in₂`: co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `out₂`.label("a") + w1(`in₂`.a, `out₂`) + `out₂`.endObject() + } + def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean] = { + var _a: scala.Tuple3[scala.Int, scala.Predef.String, scala.Boolean] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_TupleHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₃`.expectNull()) null else { + `in₃`.expectToken('[') + val tv: scala.Tuple3[scala.Int, scala.Predef.String, scala.Boolean] = new scala.Tuple3[scala.Int, scala.Predef.String, scala.Boolean][scala.Int, java.lang.String, scala.Boolean](`in₃`.expectInt(), { + `in₃`.expectToken(',') + `in₃`.expectString() + }, { + `in₃`.expectToken(',') + `in₃`.expectBoolean() + }) + `in₃`.expectToken(']') + + (tv: scala.Tuple3[scala.Int, scala.Predef.String, scala.Boolean]) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) + case _ => + `in₃`.skipValue() + } + maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_TupleHolder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean][scala.Int, java.lang.String, scala.Boolean](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean]] { + def encodeValue(`in₄`: co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) + def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean] = r0(`in₅`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_TupleHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: scala.Tuple3[scala.Int, scala.Predef.String, scala.Boolean], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startArray() + out.value(in._1) + out.valueEscaped(in._2) + out.value(in._3) + out.endArray() + } + def w0(`in₂`: co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `out₂`.label("a") + w1(`in₂`.a, `out₂`) + `out₂`.endObject() + } + def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean] = { + var _a: scala.Tuple3[scala.Int, scala.Predef.String, scala.Boolean] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_TupleHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₃`.expectNull()) null else { + `in₃`.expectToken('[') + val tv: scala.Tuple3[scala.Int, scala.Predef.String, scala.Boolean] = new scala.Tuple3[scala.Int, scala.Predef.String, scala.Boolean][scala.Int, java.lang.String, scala.Boolean](`in₃`.expectInt(), { + `in₃`.expectToken(',') + `in₃`.expectString() + }, { + `in₃`.expectToken(',') + `in₃`.expectBoolean() + }) + `in₃`.expectToken(']') + + (tv: scala.Tuple3[scala.Int, scala.Predef.String, scala.Boolean]) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) + case _ => + `in₃`.skipValue() + } + maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_TupleHolder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean][scala.Int, java.lang.String, scala.Boolean](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean]] { + def encodeValue(`in₄`: co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) + def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean] = r0(`in₅`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean]]) +} +Codec: { + val __co_blocke_scalajack_json_collections_TupleHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: scala.Tuple3[scala.Int, scala.Predef.String, scala.Boolean], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startArray() + out.value(in._1) + out.valueEscaped(in._2) + out.value(in._3) + out.endArray() + } + def w0(`in₂`: co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `out₂`.label("a") + w1(`in₂`.a, `out₂`) + `out₂`.endObject() + } + def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean] = { + var _a: scala.Tuple3[scala.Int, scala.Predef.String, scala.Boolean] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_TupleHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₃`.expectNull()) null else { + `in₃`.expectToken('[') + val tv: scala.Tuple3[scala.Int, scala.Predef.String, scala.Boolean] = new scala.Tuple3[scala.Int, scala.Predef.String, scala.Boolean][scala.Int, java.lang.String, scala.Boolean](`in₃`.expectInt(), { + `in₃`.expectToken(',') + `in₃`.expectString() + }, { + `in₃`.expectToken(',') + `in₃`.expectBoolean() + }) + `in₃`.expectToken(']') + + (tv: scala.Tuple3[scala.Int, scala.Predef.String, scala.Boolean]) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) + case _ => + `in₃`.skipValue() + } + maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_TupleHolder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean][scala.Int, java.lang.String, scala.Boolean](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean]] { + def encodeValue(`in₄`: co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) + def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean] = r0(`in₅`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean]]) +} +Codec: { + val __co_blocke_scalajack_json_misc_AliasHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a", "b", "c", "d")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: scala.collection.immutable.List[AliasSpec.this.Count], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startArray() + in.foreach[scala.Unit](((i: scala.Int) => out.value(i))) + out.endArray() + } + def w2(`in₂`: scala.collection.immutable.Map[AliasSpec.this.Count, scala.Predef.String], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `in₂`.foreach[scala.Unit](((x$1: scala.Tuple2[scala.Int, java.lang.String]) => x$1 match { + case scala.Tuple2(key, value) => + { + `out₂`.maybeComma() + `out₂`.valueStringified(scala.Int.int2long(key)) + `out₂`.colon() + `out₂`.valueEscaped(value) + } + })) + `out₂`.endObject() + } + def w3(`in₃`: scala.collection.immutable.Map[scala.Predef.String, AliasSpec.this.Count], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { + `out₃`.startObject() + `in₃`.foreach[scala.Unit](((`x$1₂`: scala.Tuple2[java.lang.String, scala.Int]) => `x$1₂` match { + case scala.Tuple2(key, value) => + { + `out₃`.maybeComma() + `out₃`.valueEscaped(`key₂`) + `out₃`.colon() + `out₃`.value(`value₂`) + } + })) + `out₃`.endObject() + } + def w0(`in₄`: co.blocke.scalajack.json.misc.AliasHolder[AliasSpec.this.Count], `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₄`.==(null)) `out₄`.burpNull() else { + `out₄`.startObject() + `out₄`.label("a") + `out₄`.value(`in₄`.a) + `out₄`.label("b") + w1(`in₄`.b, `out₄`) + `out₄`.label("c") + w2(`in₄`.c, `out₄`) + `out₄`.label("d") + w3(`in₄`.d, `out₄`) + `out₄`.endObject() + } + def r0(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.AliasHolder[AliasSpec.this.Count] = { + var _a: scala.Int = 0 + var _b: scala.collection.immutable.List[AliasSpec.this.Count] = null + var _c: scala.collection.immutable.Map[AliasSpec.this.Count, scala.Predef.String] = null + var _d: scala.collection.immutable.Map[scala.Predef.String, AliasSpec.this.Count] = null + var required: scala.Int = 15 + var maybeFieldNum: scala.Option[scala.Int] = `in₅`.expectFirstObjectField(__co_blocke_scalajack_json_misc_AliasHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.AliasHolder[AliasSpec.this.Count]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = `in₅`.expectInt() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₅`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _b = { + val parsedArray: scala.collection.mutable.ListBuffer[scala.Int] = `in₅`.expectArray[scala.Int]((() => `in₅`.expectInt())) + + (if (parsedArray.!=(null)) parsedArray.toList else null: scala.collection.immutable.List[scala.Int]) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b"), `in₅`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _c = if (`in₅`.expectNull()) null else { + `in₅`.expectToken('{') + `in₅`.parseMap[scala.Int, java.lang.String]((() => scala.Predef.augmentString(`in₅`.expectString()).toInt), (() => `in₅`.expectString()), scala.Predef.Map.empty[scala.Int, java.lang.String], true) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c"), `in₅`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _d = if (`in₅`.expectNull()) null else { + `in₅`.expectToken('{') + `in₅`.parseMap[java.lang.String, scala.Int]((() => `in₅`.expectString()), (() => `in₅`.expectInt()), scala.Predef.Map.empty[java.lang.String, scala.Int], true) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d"), `in₅`) + case _ => + `in₅`.skipValue() + } + maybeFieldNum = `in₅`.expectObjectField(__co_blocke_scalajack_json_misc_AliasHolder_fields) + } + if (required.&(15).==(0)) new co.blocke.scalajack.json.misc.AliasHolder[AliasSpec.this.Count][scala.Int](_a, _b, _c, _d) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a", "b", "c", "d")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₅`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.AliasHolder[AliasSpec.this.Count]] { + def encodeValue(`in₆`: co.blocke.scalajack.json.misc.AliasHolder[AliasSpec.this.Count], `out₅`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₆`, `out₅`) + def decodeValue(`in₇`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.AliasHolder[AliasSpec.this.Count] = r0(`in₇`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.AliasHolder[AliasSpec.this.Count]]) +} +Codec: { + val __co_blocke_scalajack_json_misc_AliasHolder2_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a", "b", "c")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: scala.collection.immutable.List[AliasSpec.this.CountX], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startArray() + in.foreach[scala.Unit](((i: scala.Option[scala.Int]) => i match { + case null => + out.burpNull() + case scala.None => + () + case scala.Some(v) => + val vv: scala.Int = v.asInstanceOf[scala.Int] + out.value(vv) + })) + out.endArray() + } + def w2(`in₂`: scala.collection.immutable.Map[scala.Predef.String, AliasSpec.this.CountX], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `in₂`.foreach[scala.Unit](((x$1: scala.Tuple2[java.lang.String, scala.Option[scala.Int]]) => x$1 match { + case scala.Tuple2(key, value) => + value match { + case scala.None => + () + case scala.Some(v) => + { + `out₂`.maybeComma() + `out₂`.valueEscaped(key) + `out₂`.colon() + `out₂`.value(`v₂`) + } + } + })) + `out₂`.endObject() + } + def w0(`in₃`: co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountX], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { + `out₃`.startObject() + `in₃`.a match { + case scala.None => + () + case scala.Some(v) => + { + `out₃`.label("a") + `out₃`.value(`v₃`) + } + } + `out₃`.label("b") + w1(`in₃`.b, `out₃`) + `out₃`.label("c") + w2(`in₃`.c, `out₃`) + `out₃`.endObject() + } + def r0(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountX] = { + var _a: scala.Option[scala.Int] = scala.None + var _b: scala.collection.immutable.List[AliasSpec.this.CountX] = null + var _c: scala.collection.immutable.Map[scala.Predef.String, AliasSpec.this.CountX] = null + var required: scala.Int = 7 + var maybeFieldNum: scala.Option[scala.Int] = `in₄`.expectFirstObjectField(__co_blocke_scalajack_json_misc_AliasHolder2_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountX]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₄`.expectNull()) null else scala.Some.apply[scala.Int](`in₄`.expectInt()) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₄`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _b = { + val parsedArray: scala.collection.mutable.ListBuffer[scala.Option[scala.Int]] = `in₄`.expectArray[scala.Option[scala.Int]]((() => if (`in₄`.expectNull()) null else scala.Some.apply[scala.Int](`in₄`.expectInt()))) + + (if (parsedArray.!=(null)) parsedArray.toList else null: scala.collection.immutable.List[scala.Option[scala.Int]]) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b"), `in₄`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _c = if (`in₄`.expectNull()) null else { + `in₄`.expectToken('{') + `in₄`.parseMap[java.lang.String, scala.Option[scala.Int]]((() => `in₄`.expectString()), (() => if (`in₄`.expectNull()) null else scala.Some.apply[scala.Int](`in₄`.expectInt())), scala.Predef.Map.empty[java.lang.String, scala.Option[scala.Int]], true) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c"), `in₄`) + case _ => + `in₄`.skipValue() + } + maybeFieldNum = `in₄`.expectObjectField(__co_blocke_scalajack_json_misc_AliasHolder2_fields) + } + if (required.&(6).==(0)) new co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountX][scala.Option[scala.Int]](_a, _b, _c) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a", "b", "c")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(6)))), `in₄`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountX]] { + def encodeValue(`in₅`: co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountX], `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₅`, `out₄`) + def decodeValue(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountX] = r0(`in₆`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountX]]) +} +Codec: { + val __co_blocke_scalajack_json_misc_AliasHolder2_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a", "b", "c")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: scala.collection.immutable.List[AliasSpec.this.CountX], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startArray() + in.foreach[scala.Unit](((i: scala.Option[scala.Int]) => i match { + case null => + out.burpNull() + case scala.None => + out.burpNull() + case scala.Some(v) => + val vv: scala.Int = v.asInstanceOf[scala.Int] + out.value(vv) + })) + out.endArray() + } + def w2(`in₂`: scala.collection.immutable.Map[scala.Predef.String, AliasSpec.this.CountX], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `in₂`.foreach[scala.Unit](((x$1: scala.Tuple2[java.lang.String, scala.Option[scala.Int]]) => x$1 match { + case scala.Tuple2(key, value) => + { + `out₂`.maybeComma() + `out₂`.valueEscaped(key) + `out₂`.colon() + value match { + case null => + `out₂`.burpNull() + case scala.None => + `out₂`.burpNull() + case scala.Some(v) => + val `vv₂`: scala.Int = `v₂`.asInstanceOf[scala.Int] + `out₂`.value(`vv₂`) + } + } + })) + `out₂`.endObject() + } + def w0(`in₃`: co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountX], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { + `out₃`.startObject() + `out₃`.label("a") + `in₃`.a match { + case null => + `out₃`.burpNull() + case scala.None => + `out₃`.burpNull() + case scala.Some(v) => + val `vv₃`: scala.Int = `v₃`.asInstanceOf[scala.Int] + `out₃`.value(`vv₃`) + } + `out₃`.label("b") + w1(`in₃`.b, `out₃`) + `out₃`.label("c") + w2(`in₃`.c, `out₃`) + `out₃`.endObject() + } + def r0(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountX] = { + var _a: scala.Option[scala.Int] = scala.None + var _b: scala.collection.immutable.List[AliasSpec.this.CountX] = null + var _c: scala.collection.immutable.Map[scala.Predef.String, AliasSpec.this.CountX] = null + var required: scala.Int = 7 + var maybeFieldNum: scala.Option[scala.Int] = `in₄`.expectFirstObjectField(__co_blocke_scalajack_json_misc_AliasHolder2_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountX]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₄`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₄`.expectInt()) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₄`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _b = { + val parsedArray: scala.collection.mutable.ListBuffer[scala.Option[scala.Int]] = `in₄`.expectArray[scala.Option[scala.Int]]((() => if (`in₄`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₄`.expectInt()))) + + (if (parsedArray.!=(null)) parsedArray.toList else null: scala.collection.immutable.List[scala.Option[scala.Int]]) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b"), `in₄`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _c = if (`in₄`.expectNull()) null else { + `in₄`.expectToken('{') + `in₄`.parseMap[java.lang.String, scala.Option[scala.Int]]((() => `in₄`.expectString()), (() => if (`in₄`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₄`.expectInt())), scala.Predef.Map.empty[java.lang.String, scala.Option[scala.Int]], true) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c"), `in₄`) + case _ => + `in₄`.skipValue() + } + maybeFieldNum = `in₄`.expectObjectField(__co_blocke_scalajack_json_misc_AliasHolder2_fields) + } + if (required.&(6).==(0)) new co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountX][scala.Option[scala.Int]](_a, _b, _c) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a", "b", "c")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(6)))), `in₄`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountX]] { + def encodeValue(`in₅`: co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountX], `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₅`, `out₄`) + def decodeValue(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountX] = r0(`in₆`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountX]]) +} +Codec: { + val __co_blocke_scalajack_json_misc_AliasHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a", "b", "c", "d")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: scala.collection.immutable.List[AliasSpec.this.CountY], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startArray() + in.foreach[scala.Unit](((i: java.lang.String) => out.valueEscaped(i))) + out.endArray() + } + def w2(`in₂`: scala.collection.immutable.Map[AliasSpec.this.CountY, scala.Predef.String], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `in₂`.foreach[scala.Unit](((x$1: scala.Tuple2[java.lang.String, java.lang.String]) => x$1 match { + case scala.Tuple2(key, value) => + { + `out₂`.maybeComma() + `out₂`.valueEscaped(key) + `out₂`.colon() + `out₂`.valueEscaped(value) + } + })) + `out₂`.endObject() + } + def w0(`in₃`: co.blocke.scalajack.json.misc.AliasHolder[AliasSpec.this.CountY], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { + `out₃`.startObject() + `out₃`.label("a") + `out₃`.valueEscaped(`in₃`.a) + `out₃`.label("b") + w1(`in₃`.b, `out₃`) + `out₃`.label("c") + w2(`in₃`.c, `out₃`) + `out₃`.label("d") + w2(`in₃`.d, `out₃`) + `out₃`.endObject() + } + def r0(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.AliasHolder[AliasSpec.this.CountY] = { + var _a: java.lang.String = "" + var _b: scala.collection.immutable.List[AliasSpec.this.CountY] = null + var _c: scala.collection.immutable.Map[AliasSpec.this.CountY, scala.Predef.String] = null + var _d: scala.collection.immutable.Map[scala.Predef.String, AliasSpec.this.CountY] = null + var required: scala.Int = 15 + var maybeFieldNum: scala.Option[scala.Int] = `in₄`.expectFirstObjectField(__co_blocke_scalajack_json_misc_AliasHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.AliasHolder[AliasSpec.this.CountY]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = `in₄`.expectString() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₄`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _b = { + val parsedArray: scala.collection.mutable.ListBuffer[java.lang.String] = `in₄`.expectArray[java.lang.String]((() => `in₄`.expectString())) + + (if (parsedArray.!=(null)) parsedArray.toList else null: scala.collection.immutable.List[java.lang.String]) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b"), `in₄`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _c = if (`in₄`.expectNull()) null else { + `in₄`.expectToken('{') + `in₄`.parseMap[java.lang.String, java.lang.String]((() => `in₄`.expectString()), (() => `in₄`.expectString()), scala.Predef.Map.empty[java.lang.String, java.lang.String], true) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c"), `in₄`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _d = if (`in₄`.expectNull()) null else { + `in₄`.expectToken('{') + `in₄`.parseMap[java.lang.String, java.lang.String]((() => `in₄`.expectString()), (() => `in₄`.expectString()), scala.Predef.Map.empty[java.lang.String, java.lang.String], true) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d"), `in₄`) + case _ => + `in₄`.skipValue() + } + maybeFieldNum = `in₄`.expectObjectField(__co_blocke_scalajack_json_misc_AliasHolder_fields) + } + if (required.&(15).==(0)) new co.blocke.scalajack.json.misc.AliasHolder[AliasSpec.this.CountY][java.lang.String](_a, _b, _c, _d) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a", "b", "c", "d")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₄`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.AliasHolder[AliasSpec.this.CountY]] { + def encodeValue(`in₅`: co.blocke.scalajack.json.misc.AliasHolder[AliasSpec.this.CountY], `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₅`, `out₄`) + def decodeValue(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.AliasHolder[AliasSpec.this.CountY] = r0(`in₆`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.AliasHolder[AliasSpec.this.CountY]]) +} +Codec: { + val __co_blocke_scalajack_json_misc_AliasHolder2_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a", "b", "c")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: scala.collection.immutable.List[AliasSpec.this.CountZ], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startArray() + in.foreach[scala.Unit](((i: scala.Option[scala.Predef.String]) => i match { + case null => + out.burpNull() + case scala.None => + () + case scala.Some(v) => + val vv: java.lang.String = v.asInstanceOf[java.lang.String] + out.valueEscaped(vv) + })) + out.endArray() + } + def w2(`in₂`: scala.collection.immutable.Map[scala.Predef.String, AliasSpec.this.CountZ], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startObject() + `in₂`.foreach[scala.Unit](((x$1: scala.Tuple2[java.lang.String, scala.Option[scala.Predef.String]]) => x$1 match { + case scala.Tuple2(key, value) => + value match { + case scala.None => + () + case scala.Some(v) => + { + `out₂`.maybeComma() + `out₂`.valueEscaped(key) + `out₂`.colon() + `out₂`.valueEscaped(`v₂`) + } + } + })) + `out₂`.endObject() + } + def w0(`in₃`: co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountZ], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { + `out₃`.startObject() + `in₃`.a match { + case scala.None => + () + case scala.Some(v) => + { + `out₃`.label("a") + `out₃`.valueEscaped(`v₃`) + } + } + `out₃`.label("b") + w1(`in₃`.b, `out₃`) + `out₃`.label("c") + w2(`in₃`.c, `out₃`) + `out₃`.endObject() + } + def r0(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountZ] = { + var _a: scala.Option[scala.Predef.String] = scala.None + var _b: scala.collection.immutable.List[AliasSpec.this.CountZ] = null + var _c: scala.collection.immutable.Map[scala.Predef.String, AliasSpec.this.CountZ] = null + var required: scala.Int = 7 + var maybeFieldNum: scala.Option[scala.Int] = `in₄`.expectFirstObjectField(__co_blocke_scalajack_json_misc_AliasHolder2_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountZ]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₄`.expectNull()) null else scala.Some.apply[java.lang.String](`in₄`.expectString()) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₄`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _b = { + val parsedArray: scala.collection.mutable.ListBuffer[scala.Option[scala.Predef.String]] = `in₄`.expectArray[scala.Option[scala.Predef.String]]((() => if (`in₄`.expectNull()) null else scala.Some.apply[java.lang.String](`in₄`.expectString()))) + + (if (parsedArray.!=(null)) parsedArray.toList else null: scala.collection.immutable.List[scala.Option[scala.Predef.String]]) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b"), `in₄`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _c = if (`in₄`.expectNull()) null else { + `in₄`.expectToken('{') + `in₄`.parseMap[java.lang.String, scala.Option[scala.Predef.String]]((() => `in₄`.expectString()), (() => if (`in₄`.expectNull()) null else scala.Some.apply[java.lang.String](`in₄`.expectString())), scala.Predef.Map.empty[java.lang.String, scala.Option[scala.Predef.String]], true) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c"), `in₄`) + case _ => + `in₄`.skipValue() + } + maybeFieldNum = `in₄`.expectObjectField(__co_blocke_scalajack_json_misc_AliasHolder2_fields) + } + if (required.&(6).==(0)) new co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountZ][scala.Option[scala.Predef.String]](_a, _b, _c) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a", "b", "c")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(6)))), `in₄`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountZ]] { + def encodeValue(`in₅`: co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountZ], `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₅`, `out₄`) + def decodeValue(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountZ] = r0(`in₆`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountZ]]) +} +Codec: { + val __co_blocke_scalajack_json_misc_ComplexEither_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.misc.ComplexEither[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + in.a match { + case scala.None => + () + case scala.Some(v) => + { + if (v.==(null)) out.burpNull() else () + v match { + case scala.Left(v) => + { + out.label("a") + out.valueEscaped(`v₂`.asInstanceOf[java.lang.String]) + } + case scala.Right(v) => + `v₃`.asInstanceOf[scala.Option[scala.Int]] match { + case scala.None => + () + case scala.Some(v) => + { + out.label("a") + out.value(`v₄`) + } + } + } + } + } + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.ComplexEither[scala.Int] = { + var _a: scala.Option[scala.util.Either[scala.Predef.String, scala.Option[scala.Int]]] = scala.None + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_ComplexEither_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.ComplexEither[scala.Int]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₂`.expectNull()) null else scala.Some.apply[scala.util.Either[scala.Predef.String, scala.Option[scala.Int]]]({ + val mark: scala.Int = `in₂`.pos + if (`in₂`.expectNull()) null else scala.util.Try.apply[scala.Option[scala.Int]](if (`in₂`.expectNull()) null else scala.Some.apply[scala.Int](`in₂`.expectInt())) match { + case scala.util.Success(rval) => + scala.Right.apply[scala.Nothing, scala.Option[scala.Int]](rval) + case scala.util.Failure(f) => + `in₂`.revertToPos(mark) + scala.util.Try.apply[java.lang.String](`in₂`.expectString()) match { + case scala.util.Success(lval) => + scala.Left.apply[java.lang.String, scala.Nothing](lval) + case scala.util.Failure(_) => + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) + } + } + }) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_ComplexEither_fields) + } + if (required.&(0).==(0)) new co.blocke.scalajack.json.misc.ComplexEither[scala.Int][scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(0)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.ComplexEither[scala.Int]] { + def encodeValue(`in₃`: co.blocke.scalajack.json.misc.ComplexEither[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.ComplexEither[scala.Int] = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.ComplexEither[scala.Int]]) +} +Codec: { + val __co_blocke_scalajack_json_misc_ComplexEither_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.misc.ComplexEither[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + in.a match { + case scala.None => + () + case scala.Some(v) => + { + if (v.==(null)) out.burpNull() else () + v match { + case scala.Left(v) => + { + out.label("a") + out.valueEscaped(`v₂`.asInstanceOf[java.lang.String]) + } + case scala.Right(v) => + `v₃`.asInstanceOf[scala.Option[scala.Int]] match { + case scala.None => + () + case scala.Some(v) => + { + out.label("a") + out.value(`v₄`) + } + } + } + } + } + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.ComplexEither[scala.Int] = { + var _a: scala.Option[scala.util.Either[scala.Predef.String, scala.Option[scala.Int]]] = scala.None + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_ComplexEither_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.ComplexEither[scala.Int]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₂`.expectNull()) null else scala.Some.apply[scala.util.Either[scala.Predef.String, scala.Option[scala.Int]]]({ + val mark: scala.Int = `in₂`.pos + if (`in₂`.expectNull()) null else scala.util.Try.apply[scala.Option[scala.Int]](if (`in₂`.expectNull()) null else scala.Some.apply[scala.Int](`in₂`.expectInt())) match { + case scala.util.Success(rval) => + scala.Right.apply[scala.Nothing, scala.Option[scala.Int]](rval) + case scala.util.Failure(f) => + `in₂`.revertToPos(mark) + scala.util.Try.apply[java.lang.String](`in₂`.expectString()) match { + case scala.util.Success(lval) => + scala.Left.apply[java.lang.String, scala.Nothing](lval) + case scala.util.Failure(_) => + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) + } + } + }) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_ComplexEither_fields) + } + if (required.&(0).==(0)) new co.blocke.scalajack.json.misc.ComplexEither[scala.Int][scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(0)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.ComplexEither[scala.Int]] { + def encodeValue(`in₃`: co.blocke.scalajack.json.misc.ComplexEither[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.ComplexEither[scala.Int] = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.ComplexEither[scala.Int]]) +} +Codec: { + val __co_blocke_scalajack_json_misc_ComplexEither_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.misc.ComplexEither[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("a") + in.a match { + case null => + out.burpNull() + case scala.None => + out.burpNull() + case scala.Some(v) => + val vv: scala.util.Either[scala.Predef.String, scala.Option[scala.Int]] = v.asInstanceOf[scala.util.Either[scala.Predef.String, scala.Option[scala.Int]]] + if (vv.==(null)) out.burpNull() else vv match { + case scala.Left(v) => + out.valueEscaped(`v₂`.asInstanceOf[java.lang.String]) + case scala.Right(v) => + `v₃`.asInstanceOf[scala.Option[scala.Int]] match { + case null => + out.burpNull() + case scala.None => + out.burpNull() + case scala.Some(v) => + val `vv₂`: scala.Int = `v₄`.asInstanceOf[scala.Int] + out.value(`vv₂`) + } + } + } + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.ComplexEither[scala.Int] = { + var _a: scala.Option[scala.util.Either[scala.Predef.String, scala.Option[scala.Int]]] = scala.None + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_ComplexEither_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.ComplexEither[scala.Int]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₂`.expectNull()) scala.None else scala.Some.apply[scala.util.Either[scala.Predef.String, scala.Option[scala.Int]]]({ + val mark: scala.Int = `in₂`.pos + if (`in₂`.expectNull()) null else scala.util.Try.apply[scala.Option[scala.Int]](if (`in₂`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₂`.expectInt())) match { + case scala.util.Success(rval) => + scala.Right.apply[scala.Nothing, scala.Option[scala.Int]](rval) + case scala.util.Failure(f) => + `in₂`.revertToPos(mark) + scala.util.Try.apply[java.lang.String](`in₂`.expectString()) match { + case scala.util.Success(lval) => + scala.Left.apply[java.lang.String, scala.Nothing](lval) + case scala.util.Failure(_) => + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) + } + } + }) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_ComplexEither_fields) + } + if (required.&(0).==(0)) new co.blocke.scalajack.json.misc.ComplexEither[scala.Int][scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(0)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.ComplexEither[scala.Int]] { + def encodeValue(`in₃`: co.blocke.scalajack.json.misc.ComplexEither[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.ComplexEither[scala.Int] = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.ComplexEither[scala.Int]]) +} +Codec: { + val __co_blocke_scalajack_json_misc_ComplexEither_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.misc.ComplexEither[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + in.a match { + case scala.None => + () + case scala.Some(v) => + { + if (v.==(null)) out.burpNull() else () + v match { + case scala.Left(v) => + { + out.label("a") + out.valueEscaped(`v₂`.asInstanceOf[java.lang.String]) + } + case scala.Right(v) => + `v₃`.asInstanceOf[scala.Option[scala.Int]] match { + case scala.None => + () + case scala.Some(v) => + { + out.label("a") + out.value(`v₄`) + } + } + } + } + } + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.ComplexEither[scala.Int] = { + var _a: scala.Option[scala.util.Either[scala.Predef.String, scala.Option[scala.Int]]] = scala.None + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_ComplexEither_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.ComplexEither[scala.Int]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₂`.expectNull()) null else scala.Some.apply[scala.util.Either[scala.Predef.String, scala.Option[scala.Int]]]({ + val mark: scala.Int = `in₂`.pos + if (`in₂`.expectNull()) null else scala.util.Try.apply[scala.Option[scala.Int]](if (`in₂`.expectNull()) null else scala.Some.apply[scala.Int](`in₂`.expectInt())) match { + case scala.util.Success(rval) => + scala.Right.apply[scala.Nothing, scala.Option[scala.Int]](rval) + case scala.util.Failure(f) => + `in₂`.revertToPos(mark) + scala.util.Try.apply[java.lang.String](`in₂`.expectString()) match { + case scala.util.Success(lval) => + scala.Left.apply[java.lang.String, scala.Nothing](lval) + case scala.util.Failure(_) => + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) + } + } + }) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_ComplexEither_fields) + } + if (required.&(0).==(0)) new co.blocke.scalajack.json.misc.ComplexEither[scala.Int][scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(0)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.ComplexEither[scala.Int]] { + def encodeValue(`in₃`: co.blocke.scalajack.json.misc.ComplexEither[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.ComplexEither[scala.Int] = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.ComplexEither[scala.Int]]) +} +Codec: { + val __co_blocke_scalajack_json_misc_ComplexEither_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.misc.ComplexEither[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + in.a match { + case scala.None => + () + case scala.Some(v) => + { + if (v.==(null)) out.burpNull() else () + v match { + case scala.Left(v) => + { + out.label("a") + out.valueEscaped(`v₂`.asInstanceOf[java.lang.String]) + } + case scala.Right(v) => + `v₃`.asInstanceOf[scala.Option[scala.Int]] match { + case scala.None => + () + case scala.Some(v) => + { + out.label("a") + out.value(`v₄`) + } + } + } + } + } + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.ComplexEither[scala.Int] = { + var _a: scala.Option[scala.util.Either[scala.Predef.String, scala.Option[scala.Int]]] = scala.None + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_ComplexEither_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.ComplexEither[scala.Int]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₂`.expectNull()) null else scala.Some.apply[scala.util.Either[scala.Predef.String, scala.Option[scala.Int]]]({ + val mark: scala.Int = `in₂`.pos + if (`in₂`.expectNull()) null else scala.util.Try.apply[scala.Option[scala.Int]](if (`in₂`.expectNull()) null else scala.Some.apply[scala.Int](`in₂`.expectInt())) match { + case scala.util.Success(rval) => + scala.Right.apply[scala.Nothing, scala.Option[scala.Int]](rval) + case scala.util.Failure(f) => + `in₂`.revertToPos(mark) + scala.util.Try.apply[java.lang.String](`in₂`.expectString()) match { + case scala.util.Success(lval) => + scala.Left.apply[java.lang.String, scala.Nothing](lval) + case scala.util.Failure(_) => + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) + } + } + }) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_ComplexEither_fields) + } + if (required.&(0).==(0)) new co.blocke.scalajack.json.misc.ComplexEither[scala.Int][scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(0)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.ComplexEither[scala.Int]] { + def encodeValue(`in₃`: co.blocke.scalajack.json.misc.ComplexEither[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.ComplexEither[scala.Int] = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.ComplexEither[scala.Int]]) +} +Codec: { + val __co_blocke_scalajack_json_misc_ComplexEither_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.misc.ComplexEither[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + in.a match { + case scala.None => + () + case scala.Some(v) => + { + if (v.==(null)) out.burpNull() else () + v match { + case scala.Left(_) => + out.label("a") + out.burpNull() + case scala.Right(v) => + `v₂`.asInstanceOf[scala.Option[scala.Int]] match { + case scala.None => + () + case scala.Some(v) => + { + out.label("a") + out.value(`v₃`) + } + } + } + } + } + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.ComplexEither[scala.Int] = { + var _a: scala.Option[scala.util.Either[scala.Predef.String, scala.Option[scala.Int]]] = scala.None + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_ComplexEither_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.ComplexEither[scala.Int]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₂`.expectNull()) null else scala.Some.apply[scala.util.Either[scala.Predef.String, scala.Option[scala.Int]]]({ + val mark: scala.Int = `in₂`.pos + if (`in₂`.expectNull()) null else scala.util.Try.apply[scala.Option[scala.Int]](if (`in₂`.expectNull()) null else scala.Some.apply[scala.Int](`in₂`.expectInt())) match { + case scala.util.Success(rval) => + scala.Right.apply[scala.Nothing, scala.Option[scala.Int]](rval) + case scala.util.Failure(f) => + `in₂`.revertToPos(mark) + scala.util.Try.apply[java.lang.String](`in₂`.expectString()) match { + case scala.util.Success(lval) => + scala.Left.apply[java.lang.String, scala.Nothing](lval) + case scala.util.Failure(_) => + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) + } + } + }) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_ComplexEither_fields) + } + if (required.&(0).==(0)) new co.blocke.scalajack.json.misc.ComplexEither[scala.Int][scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(0)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.ComplexEither[scala.Int]] { + def encodeValue(`in₃`: co.blocke.scalajack.json.misc.ComplexEither[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.ComplexEither[scala.Int] = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.ComplexEither[scala.Int]]) +} +Codec: { + val __co_blocke_scalajack_json_misc_ComplexEither_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.misc.ComplexEither[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("a") + in.a match { + case null => + out.burpNull() + case scala.None => + out.burpNull() + case scala.Some(v) => + val vv: scala.util.Either[scala.Predef.String, scala.Option[scala.Int]] = v.asInstanceOf[scala.util.Either[scala.Predef.String, scala.Option[scala.Int]]] + if (vv.==(null)) out.burpNull() else vv match { + case scala.Left(v) => + out.burpNull() + case scala.Right(v) => + `v₂`.asInstanceOf[scala.Option[scala.Int]] match { + case null => + out.burpNull() + case scala.None => + out.burpNull() + case scala.Some(v) => + val `vv₂`: scala.Int = `v₃`.asInstanceOf[scala.Int] + out.value(`vv₂`) + } + } + } + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.ComplexEither[scala.Int] = { + var _a: scala.Option[scala.util.Either[scala.Predef.String, scala.Option[scala.Int]]] = scala.None + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_ComplexEither_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.ComplexEither[scala.Int]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = if (`in₂`.expectNull()) scala.None else scala.Some.apply[scala.util.Either[scala.Predef.String, scala.Option[scala.Int]]]({ + val mark: scala.Int = `in₂`.pos + if (`in₂`.expectNull()) null else scala.util.Try.apply[scala.Option[scala.Int]](if (`in₂`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₂`.expectInt())) match { + case scala.util.Success(rval) => + scala.Right.apply[scala.Nothing, scala.Option[scala.Int]](rval) + case scala.util.Failure(f) => + `in₂`.revertToPos(mark) + scala.util.Try.apply[java.lang.String](`in₂`.expectString()) match { + case scala.util.Success(lval) => + scala.Left.apply[java.lang.String, scala.Nothing](lval) + case scala.util.Failure(_) => + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) + } + } + }) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_ComplexEither_fields) + } + if (required.&(0).==(0)) new co.blocke.scalajack.json.misc.ComplexEither[scala.Int][scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(0)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.ComplexEither[scala.Int]] { + def encodeValue(`in₃`: co.blocke.scalajack.json.misc.ComplexEither[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.ComplexEither[scala.Int] = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.ComplexEither[scala.Int]]) +} +Codec: { + val __co_blocke_scalajack_json_misc_EitherHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a", "b")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.misc.EitherHolder[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + if (in.a.==(null)) out.burpNull() else () + in.a match { + case scala.Left(v) => + { + out.label("a") + out.value(v.asInstanceOf[scala.Int]) + } + case scala.Right(v) => + { + out.label("a") + out.valueEscaped(`v₂`.asInstanceOf[java.lang.String]) + } + } + if (in.b.==(null)) out.burpNull() else () + in.b match { + case scala.Left(v) => + { + out.label("b") + out.valueEscaped(`v₃`.asInstanceOf[java.lang.String]) + } + case scala.Right(v) => + { + out.label("b") + out.value(`v₄`.asInstanceOf[scala.Int]) + } + } + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.EitherHolder[scala.Int] = { + var _a: scala.util.Either[scala.Int, scala.Predef.String] = null + var _b: scala.util.Either[scala.Predef.String, scala.Int] = null + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_EitherHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.EitherHolder[scala.Int]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = { + val mark: scala.Int = `in₂`.pos + if (`in₂`.expectNull()) null else scala.util.Try.apply[java.lang.String](`in₂`.expectString()) match { + case scala.util.Success(rval) => + scala.Right.apply[scala.Nothing, java.lang.String](rval) + case scala.util.Failure(f) => + `in₂`.revertToPos(mark) + scala.util.Try.apply[scala.Int](`in₂`.expectInt()) match { + case scala.util.Success(lval) => + scala.Left.apply[scala.Int, scala.Nothing](lval) + case scala.util.Failure(_) => + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) + } + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _b = { + val `mark₂`: scala.Int = `in₂`.pos + if (`in₂`.expectNull()) null else scala.util.Try.apply[scala.Int](`in₂`.expectInt()) match { + case scala.util.Success(rval) => + scala.Right.apply[scala.Nothing, scala.Int](`rval₂`) + case scala.util.Failure(f) => + `in₂`.revertToPos(`mark₂`) + scala.util.Try.apply[java.lang.String](`in₂`.expectString()) match { + case scala.util.Success(lval) => + scala.Left.apply[java.lang.String, scala.Nothing](`lval₂`) + case scala.util.Failure(_) => + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) + } + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_EitherHolder_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.misc.EitherHolder[scala.Int][scala.Int](_a, _b) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a", "b")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.EitherHolder[scala.Int]] { + def encodeValue(`in₃`: co.blocke.scalajack.json.misc.EitherHolder[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.EitherHolder[scala.Int] = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.EitherHolder[scala.Int]]) +} +Codec: { + val __co_blocke_scalajack_json_misc_EitherHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a", "b")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.misc.EitherHolder[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + if (in.a.==(null)) out.burpNull() else () + in.a match { + case scala.Left(_) => + out.label("a") + out.burpNull() + case scala.Right(v) => + { + out.label("a") + out.valueEscaped(v.asInstanceOf[java.lang.String]) + } + } + if (in.b.==(null)) out.burpNull() else () + in.b match { + case scala.Left(_) => + out.label("b") + out.burpNull() + case scala.Right(v) => + { + out.label("b") + out.value(`v₂`.asInstanceOf[scala.Int]) + } + } + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.EitherHolder[scala.Int] = { + var _a: scala.util.Either[scala.Int, scala.Predef.String] = null + var _b: scala.util.Either[scala.Predef.String, scala.Int] = null + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_EitherHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.EitherHolder[scala.Int]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = { + val mark: scala.Int = `in₂`.pos + if (`in₂`.expectNull()) null else scala.util.Try.apply[java.lang.String](`in₂`.expectString()) match { + case scala.util.Success(rval) => + scala.Right.apply[scala.Nothing, java.lang.String](rval) + case scala.util.Failure(f) => + `in₂`.revertToPos(mark) + scala.util.Try.apply[scala.Int](`in₂`.expectInt()) match { + case scala.util.Success(lval) => + scala.Left.apply[scala.Int, scala.Nothing](lval) + case scala.util.Failure(_) => + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) + } + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _b = { + val `mark₂`: scala.Int = `in₂`.pos + if (`in₂`.expectNull()) null else scala.util.Try.apply[scala.Int](`in₂`.expectInt()) match { + case scala.util.Success(rval) => + scala.Right.apply[scala.Nothing, scala.Int](`rval₂`) + case scala.util.Failure(f) => + `in₂`.revertToPos(`mark₂`) + scala.util.Try.apply[java.lang.String](`in₂`.expectString()) match { + case scala.util.Success(lval) => + scala.Left.apply[java.lang.String, scala.Nothing](`lval₂`) + case scala.util.Failure(_) => + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) + } + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_EitherHolder_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.misc.EitherHolder[scala.Int][scala.Int](_a, _b) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a", "b")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.EitherHolder[scala.Int]] { + def encodeValue(`in₃`: co.blocke.scalajack.json.misc.EitherHolder[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.EitherHolder[scala.Int] = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.EitherHolder[scala.Int]]) +} +Codec: { + val __co_blocke_scalajack_json_misc_EitherHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a", "b")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.misc.EitherHolder[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + if (in.a.==(null)) out.burpNull() else () + in.a match { + case scala.Left(err) => + out.label("a") + out.value("Left Error: ".+(err.toString())) + case scala.Right(v) => + { + out.label("a") + out.valueEscaped(v.asInstanceOf[java.lang.String]) + } + } + if (in.b.==(null)) out.burpNull() else () + in.b match { + case scala.Left(err) => + out.label("b") + out.value("Left Error: ".+(`err₂`.toString())) + case scala.Right(v) => + { + out.label("b") + out.value(`v₂`.asInstanceOf[scala.Int]) + } + } + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.EitherHolder[scala.Int] = { + var _a: scala.util.Either[scala.Int, scala.Predef.String] = null + var _b: scala.util.Either[scala.Predef.String, scala.Int] = null + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_EitherHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.EitherHolder[scala.Int]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = { + val mark: scala.Int = `in₂`.pos + if (`in₂`.expectNull()) null else scala.util.Try.apply[java.lang.String](`in₂`.expectString()) match { + case scala.util.Success(rval) => + scala.Right.apply[scala.Nothing, java.lang.String](rval) + case scala.util.Failure(f) => + `in₂`.revertToPos(mark) + scala.util.Try.apply[scala.Int](`in₂`.expectInt()) match { + case scala.util.Success(lval) => + scala.Left.apply[scala.Int, scala.Nothing](lval) + case scala.util.Failure(_) => + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) + } + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _b = { + val `mark₂`: scala.Int = `in₂`.pos + if (`in₂`.expectNull()) null else scala.util.Try.apply[scala.Int](`in₂`.expectInt()) match { + case scala.util.Success(rval) => + scala.Right.apply[scala.Nothing, scala.Int](`rval₂`) + case scala.util.Failure(f) => + `in₂`.revertToPos(`mark₂`) + scala.util.Try.apply[java.lang.String](`in₂`.expectString()) match { + case scala.util.Success(lval) => + scala.Left.apply[java.lang.String, scala.Nothing](`lval₂`) + case scala.util.Failure(_) => + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) + } + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_EitherHolder_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.misc.EitherHolder[scala.Int][scala.Int](_a, _b) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a", "b")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.EitherHolder[scala.Int]] { + def encodeValue(`in₃`: co.blocke.scalajack.json.misc.EitherHolder[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.EitherHolder[scala.Int] = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.EitherHolder[scala.Int]]) +} +Codec: { + val __co_blocke_scalajack_json_misc_EitherHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a", "b")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.misc.EitherHolder[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + if (in.a.==(null)) out.burpNull() else () + in.a match { + case scala.Left(err) => + throw new co.blocke.scalajack.json.JsonEitherLeftError("Left Error: ".+(err.toString())) + case scala.Right(v) => + { + out.label("a") + out.valueEscaped(v.asInstanceOf[java.lang.String]) + } + } + if (in.b.==(null)) out.burpNull() else () + in.b match { + case scala.Left(err) => + throw new co.blocke.scalajack.json.JsonEitherLeftError("Left Error: ".+(`err₂`.toString())) + case scala.Right(v) => + { + out.label("b") + out.value(`v₂`.asInstanceOf[scala.Int]) + } + } + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.EitherHolder[scala.Int] = { + var _a: scala.util.Either[scala.Int, scala.Predef.String] = null + var _b: scala.util.Either[scala.Predef.String, scala.Int] = null + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_EitherHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.EitherHolder[scala.Int]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = { + val mark: scala.Int = `in₂`.pos + if (`in₂`.expectNull()) null else scala.util.Try.apply[java.lang.String](`in₂`.expectString()) match { + case scala.util.Success(rval) => + scala.Right.apply[scala.Nothing, java.lang.String](rval) + case scala.util.Failure(f) => + `in₂`.revertToPos(mark) + scala.util.Try.apply[scala.Int](`in₂`.expectInt()) match { + case scala.util.Success(lval) => + scala.Left.apply[scala.Int, scala.Nothing](lval) + case scala.util.Failure(_) => + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) + } + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _b = { + val `mark₂`: scala.Int = `in₂`.pos + if (`in₂`.expectNull()) null else scala.util.Try.apply[scala.Int](`in₂`.expectInt()) match { + case scala.util.Success(rval) => + scala.Right.apply[scala.Nothing, scala.Int](`rval₂`) + case scala.util.Failure(f) => + `in₂`.revertToPos(`mark₂`) + scala.util.Try.apply[java.lang.String](`in₂`.expectString()) match { + case scala.util.Success(lval) => + scala.Left.apply[java.lang.String, scala.Nothing](`lval₂`) + case scala.util.Failure(_) => + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) + } + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_EitherHolder_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.misc.EitherHolder[scala.Int][scala.Int](_a, _b) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a", "b")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.EitherHolder[scala.Int]] { + def encodeValue(`in₃`: co.blocke.scalajack.json.misc.EitherHolder[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.EitherHolder[scala.Int] = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.EitherHolder[scala.Int]]) +} +Codec: { + val __co_blocke_scalajack_json_misc_LRUnionHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a", "b")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: scala.collection.immutable.Seq[scala.Option[scala.Int] | scala.Predef.String], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startArray() + in.foreach[scala.Unit](((i: scala.Option[scala.Int] | java.lang.String) => { + out.mark() + scala.util.Try.apply[scala.Unit](out.valueEscaped(i.asInstanceOf[java.lang.String])) match { + case scala.util.Success(_) => + () + case scala.util.Failure(_) => + out.revert() + i.asInstanceOf[scala.Option[scala.Int]] match { + case null => + out.burpNull() + case scala.None => + () + case scala.Some(v) => + val vv: scala.Int = v.asInstanceOf[scala.Int] + out.value(vv) + } + } + })) + out.endArray() + } + def w2(`in₂`: scala.Tuple2[scala.Option[scala.Int] | scala.Predef.String, scala.Option[scala.Int] | scala.Predef.String], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startArray() + `out₂`.mark() + scala.util.Try.apply[scala.Unit](`out₂`.valueEscaped(`in₂`._1.asInstanceOf[java.lang.String])) match { + case scala.util.Success(_) => + () + case scala.util.Failure(_) => + `out₂`.revert() + `in₂`._1.asInstanceOf[scala.Option[scala.Int]] match { + case null => + `out₂`.burpNull() + case scala.None => + `out₂`.burpNull() + case scala.Some(v) => + val `vv₂`: scala.Int = `v₂`.asInstanceOf[scala.Int] + `out₂`.value(`vv₂`) + } + } + `out₂`.mark() + scala.util.Try.apply[scala.Unit](`out₂`.valueEscaped(`in₂`._2.asInstanceOf[java.lang.String])) match { + case scala.util.Success(_) => + () + case scala.util.Failure(_) => + `out₂`.revert() + `in₂`._2.asInstanceOf[scala.Option[scala.Int]] match { + case null => + `out₂`.burpNull() + case scala.None => + `out₂`.burpNull() + case scala.Some(v) => + val `vv₃`: scala.Int = `v₃`.asInstanceOf[scala.Int] + `out₂`.value(`vv₃`) + } + } + `out₂`.endArray() + } + def w0(`in₃`: co.blocke.scalajack.json.misc.LRUnionHolder[scala.Option[scala.Int], scala.Predef.String], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { + `out₃`.startObject() + `out₃`.label("a") + w1(`in₃`.a, `out₃`) + `out₃`.label("b") + w2(`in₃`.b, `out₃`) + `out₃`.endObject() + } + def r0(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.LRUnionHolder[scala.Option[scala.Int], scala.Predef.String] = { + var _a: scala.collection.immutable.Seq[scala.Option[scala.Int] | scala.Predef.String] = null + var _b: scala.Tuple2[scala.Option[scala.Int] | scala.Predef.String, scala.Option[scala.Int] | scala.Predef.String] = null + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₄`.expectFirstObjectField(__co_blocke_scalajack_json_misc_LRUnionHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.LRUnionHolder[scala.Option[scala.Int], scala.Predef.String]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = { + val parsedArray: scala.collection.mutable.ListBuffer[scala.Option[scala.Int] | java.lang.String] = `in₄`.expectArray[scala.Option[scala.Int] | java.lang.String]((() => { + val mark: scala.Int = `in₄`.pos + scala.util.Try.apply[scala.Option[scala.Int]](if (`in₄`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₄`.expectInt())) match { + case scala.util.Success(lval) => + (lval: scala.Option[scala.Int]) + case scala.util.Failure(f) => + `in₄`.revertToPos(mark) + scala.util.Try.apply[java.lang.String](`in₄`.expectString()) match { + case scala.util.Success(rval) => + (rval: java.lang.String) + case scala.util.Failure(_) => + `in₄`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₄`) + } + } + })) + + (if (parsedArray.!=(null)) parsedArray.toSeq else null: scala.collection.immutable.Seq[scala.Option[scala.Int] | java.lang.String]) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₄`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _b = if (`in₄`.expectNull()) null else { + `in₄`.expectToken('[') + val tv: scala.Tuple2[scala.Option[scala.Int] | scala.Predef.String, scala.Option[scala.Int] | scala.Predef.String] = new scala.Tuple2[scala.Option[scala.Int] | scala.Predef.String, scala.Option[scala.Int] | scala.Predef.String][scala.Option[scala.Int] | scala.Predef.String, scala.Option[scala.Int] | scala.Predef.String]({ + val `mark₂`: scala.Int = `in₄`.pos + scala.util.Try.apply[scala.Option[scala.Int]](if (`in₄`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₄`.expectInt())) match { + case scala.util.Success(lval) => + (`lval₂`: scala.Option[scala.Int]) + case scala.util.Failure(f) => + `in₄`.revertToPos(`mark₂`) + scala.util.Try.apply[java.lang.String](`in₄`.expectString()) match { + case scala.util.Success(rval) => + (`rval₂`: java.lang.String) + case scala.util.Failure(_) => + `in₄`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₄`) + } + } + }, { + `in₄`.expectToken(',') + val `mark₃`: scala.Int = `in₄`.pos + scala.util.Try.apply[scala.Option[scala.Int]](if (`in₄`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₄`.expectInt())) match { + case scala.util.Success(lval) => + (`lval₃`: scala.Option[scala.Int]) + case scala.util.Failure(f) => + `in₄`.revertToPos(`mark₃`) + scala.util.Try.apply[java.lang.String](`in₄`.expectString()) match { + case scala.util.Success(rval) => + (`rval₃`: java.lang.String) + case scala.util.Failure(_) => + `in₄`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₄`) + } + } + }) + `in₄`.expectToken(']') + + (tv: scala.Tuple2[scala.Option[scala.Int] | scala.Predef.String, scala.Option[scala.Int] | scala.Predef.String]) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b"), `in₄`) + case _ => + `in₄`.skipValue() + } + maybeFieldNum = `in₄`.expectObjectField(__co_blocke_scalajack_json_misc_LRUnionHolder_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.misc.LRUnionHolder[scala.Option[scala.Int], scala.Predef.String][scala.Option[scala.Int], java.lang.String](_a, _b) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a", "b")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₄`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.LRUnionHolder[scala.Option[scala.Int], scala.Predef.String]] { + def encodeValue(`in₅`: co.blocke.scalajack.json.misc.LRUnionHolder[scala.Option[scala.Int], scala.Predef.String], `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₅`, `out₄`) + def decodeValue(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.LRUnionHolder[scala.Option[scala.Int], scala.Predef.String] = r0(`in₆`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.LRUnionHolder[scala.Option[scala.Int], scala.Predef.String]]) +} +Codec: { + val __co_blocke_scalajack_json_misc_LRUnionHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a", "b")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: scala.collection.immutable.Seq[scala.Option[scala.Int] | scala.Predef.String], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startArray() + in.foreach[scala.Unit](((i: scala.Option[scala.Int] | java.lang.String) => { + out.mark() + scala.util.Try.apply[scala.Unit](out.valueEscaped(i.asInstanceOf[java.lang.String])) match { + case scala.util.Success(_) => + () + case scala.util.Failure(_) => + out.revert() + i.asInstanceOf[scala.Option[scala.Int]] match { + case null => + out.burpNull() + case scala.None => + () + case scala.Some(v) => + val vv: scala.Int = v.asInstanceOf[scala.Int] + out.value(vv) + } + } + })) + out.endArray() + } + def w2(`in₂`: scala.Tuple2[scala.Option[scala.Int] | scala.Predef.String, scala.Option[scala.Int] | scala.Predef.String], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startArray() + `out₂`.mark() + scala.util.Try.apply[scala.Unit](`out₂`.valueEscaped(`in₂`._1.asInstanceOf[java.lang.String])) match { + case scala.util.Success(_) => + () + case scala.util.Failure(_) => + `out₂`.revert() + `in₂`._1.asInstanceOf[scala.Option[scala.Int]] match { + case null => + `out₂`.burpNull() + case scala.None => + `out₂`.burpNull() + case scala.Some(v) => + val `vv₂`: scala.Int = `v₂`.asInstanceOf[scala.Int] + `out₂`.value(`vv₂`) + } + } + `out₂`.mark() + scala.util.Try.apply[scala.Unit](`out₂`.valueEscaped(`in₂`._2.asInstanceOf[java.lang.String])) match { + case scala.util.Success(_) => + () + case scala.util.Failure(_) => + `out₂`.revert() + `in₂`._2.asInstanceOf[scala.Option[scala.Int]] match { + case null => + `out₂`.burpNull() + case scala.None => + `out₂`.burpNull() + case scala.Some(v) => + val `vv₃`: scala.Int = `v₃`.asInstanceOf[scala.Int] + `out₂`.value(`vv₃`) + } + } + `out₂`.endArray() + } + def w0(`in₃`: co.blocke.scalajack.json.misc.LRUnionHolder[scala.Option[scala.Int], scala.Predef.String], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { + `out₃`.startObject() + `out₃`.label("a") + w1(`in₃`.a, `out₃`) + `out₃`.label("b") + w2(`in₃`.b, `out₃`) + `out₃`.endObject() + } + def r0(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.LRUnionHolder[scala.Option[scala.Int], scala.Predef.String] = { + var _a: scala.collection.immutable.Seq[scala.Option[scala.Int] | scala.Predef.String] = null + var _b: scala.Tuple2[scala.Option[scala.Int] | scala.Predef.String, scala.Option[scala.Int] | scala.Predef.String] = null + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₄`.expectFirstObjectField(__co_blocke_scalajack_json_misc_LRUnionHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.LRUnionHolder[scala.Option[scala.Int], scala.Predef.String]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = { + val parsedArray: scala.collection.mutable.ListBuffer[scala.Option[scala.Int] | java.lang.String] = `in₄`.expectArray[scala.Option[scala.Int] | java.lang.String]((() => { + val mark: scala.Int = `in₄`.pos + scala.util.Try.apply[scala.Option[scala.Int]](if (`in₄`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₄`.expectInt())) match { + case scala.util.Success(lval) => + (lval: scala.Option[scala.Int]) + case scala.util.Failure(f) => + `in₄`.revertToPos(mark) + scala.util.Try.apply[java.lang.String](`in₄`.expectString()) match { + case scala.util.Success(rval) => + (rval: java.lang.String) + case scala.util.Failure(_) => + `in₄`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₄`) + } + } + })) + + (if (parsedArray.!=(null)) parsedArray.toSeq else null: scala.collection.immutable.Seq[scala.Option[scala.Int] | java.lang.String]) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₄`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _b = if (`in₄`.expectNull()) null else { + `in₄`.expectToken('[') + val tv: scala.Tuple2[scala.Option[scala.Int] | scala.Predef.String, scala.Option[scala.Int] | scala.Predef.String] = new scala.Tuple2[scala.Option[scala.Int] | scala.Predef.String, scala.Option[scala.Int] | scala.Predef.String][scala.Option[scala.Int] | scala.Predef.String, scala.Option[scala.Int] | scala.Predef.String]({ + val `mark₂`: scala.Int = `in₄`.pos + scala.util.Try.apply[scala.Option[scala.Int]](if (`in₄`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₄`.expectInt())) match { + case scala.util.Success(lval) => + (`lval₂`: scala.Option[scala.Int]) + case scala.util.Failure(f) => + `in₄`.revertToPos(`mark₂`) + scala.util.Try.apply[java.lang.String](`in₄`.expectString()) match { + case scala.util.Success(rval) => + (`rval₂`: java.lang.String) + case scala.util.Failure(_) => + `in₄`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₄`) + } + } + }, { + `in₄`.expectToken(',') + val `mark₃`: scala.Int = `in₄`.pos + scala.util.Try.apply[scala.Option[scala.Int]](if (`in₄`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₄`.expectInt())) match { + case scala.util.Success(lval) => + (`lval₃`: scala.Option[scala.Int]) + case scala.util.Failure(f) => + `in₄`.revertToPos(`mark₃`) + scala.util.Try.apply[java.lang.String](`in₄`.expectString()) match { + case scala.util.Success(rval) => + (`rval₃`: java.lang.String) + case scala.util.Failure(_) => + `in₄`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₄`) + } + } + }) + `in₄`.expectToken(']') + + (tv: scala.Tuple2[scala.Option[scala.Int] | scala.Predef.String, scala.Option[scala.Int] | scala.Predef.String]) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b"), `in₄`) + case _ => + `in₄`.skipValue() + } + maybeFieldNum = `in₄`.expectObjectField(__co_blocke_scalajack_json_misc_LRUnionHolder_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.misc.LRUnionHolder[scala.Option[scala.Int], scala.Predef.String][scala.Option[scala.Int], java.lang.String](_a, _b) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a", "b")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₄`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.LRUnionHolder[scala.Option[scala.Int], scala.Predef.String]] { + def encodeValue(`in₅`: co.blocke.scalajack.json.misc.LRUnionHolder[scala.Option[scala.Int], scala.Predef.String], `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₅`, `out₄`) + def decodeValue(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.LRUnionHolder[scala.Option[scala.Int], scala.Predef.String] = r0(`in₆`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.LRUnionHolder[scala.Option[scala.Int], scala.Predef.String]]) +} +Codec: { + val __co_blocke_scalajack_json_misc_LRUnionHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a", "b")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: scala.collection.immutable.Seq[scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startArray() + in.foreach[scala.Unit](((i: scala.util.Try[scala.Option[scala.Int]] | java.lang.String) => { + out.mark() + scala.util.Try.apply[scala.Unit](out.valueEscaped(i.asInstanceOf[java.lang.String])) match { + case scala.util.Success(_) => + () + case scala.util.Failure(_) => + out.revert() + if (i.asInstanceOf[scala.util.Try[scala.Option[scala.Int]]].==(null)) out.burpNull() else i.asInstanceOf[scala.util.Try[scala.Option[scala.Int]]] match { + case scala.util.Success(v) => + v match { + case null => + out.burpNull() + case scala.None => + () + case scala.Some(v) => + val vv: scala.Int = `v₂`.asInstanceOf[scala.Int] + out.value(vv) + } + case scala.util.Failure(v) => + out.burpNull() + } + } + })) + out.endArray() + } + def w2(`in₂`: scala.Tuple2[scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String, scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startArray() + `out₂`.mark() + scala.util.Try.apply[scala.Unit](`out₂`.valueEscaped(`in₂`._1.asInstanceOf[java.lang.String])) match { + case scala.util.Success(_) => + () + case scala.util.Failure(_) => + `out₂`.revert() + if (`in₂`._1.asInstanceOf[scala.util.Try[scala.Option[scala.Int]]].==(null)) `out₂`.burpNull() else `in₂`._1.asInstanceOf[scala.util.Try[scala.Option[scala.Int]]] match { + case scala.util.Success(v) => + `v₃` match { + case null => + `out₂`.burpNull() + case scala.None => + `out₂`.burpNull() + case scala.Some(v) => + val `vv₂`: scala.Int = `v₄`.asInstanceOf[scala.Int] + `out₂`.value(`vv₂`) + } + case scala.util.Failure(v) => + `out₂`.burpNull() + } + } + `out₂`.mark() + scala.util.Try.apply[scala.Unit](`out₂`.valueEscaped(`in₂`._2.asInstanceOf[java.lang.String])) match { + case scala.util.Success(_) => + () + case scala.util.Failure(_) => + `out₂`.revert() + if (`in₂`._2.asInstanceOf[scala.util.Try[scala.Option[scala.Int]]].==(null)) `out₂`.burpNull() else `in₂`._2.asInstanceOf[scala.util.Try[scala.Option[scala.Int]]] match { + case scala.util.Success(v) => + `v₅` match { + case null => + `out₂`.burpNull() + case scala.None => + `out₂`.burpNull() + case scala.Some(v) => + val `vv₃`: scala.Int = `v₆`.asInstanceOf[scala.Int] + `out₂`.value(`vv₃`) + } + case scala.util.Failure(v) => + `out₂`.burpNull() + } + } + `out₂`.endArray() + } + def w0(`in₃`: co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { + `out₃`.startObject() + `out₃`.label("a") + w1(`in₃`.a, `out₃`) + `out₃`.label("b") + w2(`in₃`.b, `out₃`) + `out₃`.endObject() + } + def r0(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String] = { + var _a: scala.collection.immutable.Seq[scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String] = null + var _b: scala.Tuple2[scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String, scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String] = null + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₄`.expectFirstObjectField(__co_blocke_scalajack_json_misc_LRUnionHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = { + val parsedArray: scala.collection.mutable.ListBuffer[scala.util.Try[scala.Option[scala.Int]] | java.lang.String] = `in₄`.expectArray[scala.util.Try[scala.Option[scala.Int]] | java.lang.String]((() => { + val mark: scala.Int = `in₄`.pos + scala.util.Try.apply[scala.util.Try[scala.Option[scala.Int]]]({ + val `mark₂`: scala.Int = `in₄`.pos + try if (`in₄`.expectNull()) null else scala.util.Success.apply[scala.Option[scala.Int]](if (`in₄`.expectNull()) null else scala.Some.apply[scala.Int](`in₄`.expectInt())) catch { + case t: java.lang.Throwable => + `in₄`.revertToPos(`mark₂`) + throw co.blocke.scalajack.json.JsonParseError.apply("Unsuccessful attempt to read Try type with failure: ".+(t.getMessage()), `in₄`) + } + }) match { + case scala.util.Success(lval) => + (lval: scala.util.Try[scala.Option[scala.Int]]) + case scala.util.Failure(f) => + `in₄`.revertToPos(mark) + scala.util.Try.apply[java.lang.String](`in₄`.expectString()) match { + case scala.util.Success(rval) => + (rval: java.lang.String) + case scala.util.Failure(_) => + `in₄`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₄`) + } + } + })) + + (if (parsedArray.!=(null)) parsedArray.toSeq else null: scala.collection.immutable.Seq[scala.util.Try[scala.Option[scala.Int]] | java.lang.String]) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₄`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _b = if (`in₄`.expectNull()) null else { + `in₄`.expectToken('[') + val tv: scala.Tuple2[scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String, scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String] = new scala.Tuple2[scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String, scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String][scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String, scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String]({ + val `mark₃`: scala.Int = `in₄`.pos + scala.util.Try.apply[scala.util.Try[scala.Option[scala.Int]]]({ + val `mark₄`: scala.Int = `in₄`.pos + try if (`in₄`.expectNull()) null else scala.util.Success.apply[scala.Option[scala.Int]](if (`in₄`.expectNull()) null else scala.Some.apply[scala.Int](`in₄`.expectInt())) catch { + case t: java.lang.Throwable => + `in₄`.revertToPos(`mark₄`) + throw co.blocke.scalajack.json.JsonParseError.apply("Unsuccessful attempt to read Try type with failure: ".+(`t₂`.getMessage()), `in₄`) + } + }) match { + case scala.util.Success(lval) => + (`lval₂`: scala.util.Try[scala.Option[scala.Int]]) + case scala.util.Failure(f) => + `in₄`.revertToPos(`mark₃`) + scala.util.Try.apply[java.lang.String](`in₄`.expectString()) match { + case scala.util.Success(rval) => + (`rval₂`: java.lang.String) + case scala.util.Failure(_) => + `in₄`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₄`) + } + } + }, { + `in₄`.expectToken(',') + val `mark₅`: scala.Int = `in₄`.pos + scala.util.Try.apply[scala.util.Try[scala.Option[scala.Int]]]({ + val `mark₆`: scala.Int = `in₄`.pos + try if (`in₄`.expectNull()) null else scala.util.Success.apply[scala.Option[scala.Int]](if (`in₄`.expectNull()) null else scala.Some.apply[scala.Int](`in₄`.expectInt())) catch { + case t: java.lang.Throwable => + `in₄`.revertToPos(`mark₆`) + throw co.blocke.scalajack.json.JsonParseError.apply("Unsuccessful attempt to read Try type with failure: ".+(`t₃`.getMessage()), `in₄`) + } + }) match { + case scala.util.Success(lval) => + (`lval₃`: scala.util.Try[scala.Option[scala.Int]]) + case scala.util.Failure(f) => + `in₄`.revertToPos(`mark₅`) + scala.util.Try.apply[java.lang.String](`in₄`.expectString()) match { + case scala.util.Success(rval) => + (`rval₃`: java.lang.String) + case scala.util.Failure(_) => + `in₄`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₄`) + } + } + }) + `in₄`.expectToken(']') + + (tv: scala.Tuple2[scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String, scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String]) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b"), `in₄`) + case _ => + `in₄`.skipValue() + } + maybeFieldNum = `in₄`.expectObjectField(__co_blocke_scalajack_json_misc_LRUnionHolder_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String][scala.util.Try[scala.Option[scala.Int]], java.lang.String](_a, _b) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a", "b")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₄`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String]] { + def encodeValue(`in₅`: co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String], `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₅`, `out₄`) + def decodeValue(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String] = r0(`in₆`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String]]) +} +Codec: { + val __co_blocke_scalajack_json_misc_LRUnionHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a", "b")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: scala.collection.immutable.Seq[scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startArray() + in.foreach[scala.Unit](((i: scala.util.Try[scala.Option[scala.Int]] | java.lang.String) => { + out.mark() + scala.util.Try.apply[scala.Unit](out.valueEscaped(i.asInstanceOf[java.lang.String])) match { + case scala.util.Success(_) => + () + case scala.util.Failure(_) => + out.revert() + if (i.asInstanceOf[scala.util.Try[scala.Option[scala.Int]]].==(null)) out.burpNull() else i.asInstanceOf[scala.util.Try[scala.Option[scala.Int]]] match { + case scala.util.Success(v) => + v match { + case null => + out.burpNull() + case scala.None => + () + case scala.Some(v) => + val vv: scala.Int = `v₂`.asInstanceOf[scala.Int] + out.value(vv) + } + case scala.util.Failure(v) => + out.burpNull() + } + } + })) + out.endArray() + } + def w2(`in₂`: scala.Tuple2[scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String, scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startArray() + `out₂`.mark() + scala.util.Try.apply[scala.Unit](`out₂`.valueEscaped(`in₂`._1.asInstanceOf[java.lang.String])) match { + case scala.util.Success(_) => + () + case scala.util.Failure(_) => + `out₂`.revert() + if (`in₂`._1.asInstanceOf[scala.util.Try[scala.Option[scala.Int]]].==(null)) `out₂`.burpNull() else `in₂`._1.asInstanceOf[scala.util.Try[scala.Option[scala.Int]]] match { + case scala.util.Success(v) => + `v₃` match { + case null => + `out₂`.burpNull() + case scala.None => + `out₂`.burpNull() + case scala.Some(v) => + val `vv₂`: scala.Int = `v₄`.asInstanceOf[scala.Int] + `out₂`.value(`vv₂`) + } + case scala.util.Failure(v) => + `out₂`.burpNull() + } + } + `out₂`.mark() + scala.util.Try.apply[scala.Unit](`out₂`.valueEscaped(`in₂`._2.asInstanceOf[java.lang.String])) match { + case scala.util.Success(_) => + () + case scala.util.Failure(_) => + `out₂`.revert() + if (`in₂`._2.asInstanceOf[scala.util.Try[scala.Option[scala.Int]]].==(null)) `out₂`.burpNull() else `in₂`._2.asInstanceOf[scala.util.Try[scala.Option[scala.Int]]] match { + case scala.util.Success(v) => + `v₅` match { + case null => + `out₂`.burpNull() + case scala.None => + `out₂`.burpNull() + case scala.Some(v) => + val `vv₃`: scala.Int = `v₆`.asInstanceOf[scala.Int] + `out₂`.value(`vv₃`) + } + case scala.util.Failure(v) => + `out₂`.burpNull() + } + } + `out₂`.endArray() + } + def w0(`in₃`: co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { + `out₃`.startObject() + `out₃`.label("a") + w1(`in₃`.a, `out₃`) + `out₃`.label("b") + w2(`in₃`.b, `out₃`) + `out₃`.endObject() + } + def r0(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String] = { + var _a: scala.collection.immutable.Seq[scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String] = null + var _b: scala.Tuple2[scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String, scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String] = null + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₄`.expectFirstObjectField(__co_blocke_scalajack_json_misc_LRUnionHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = { + val parsedArray: scala.collection.mutable.ListBuffer[scala.util.Try[scala.Option[scala.Int]] | java.lang.String] = `in₄`.expectArray[scala.util.Try[scala.Option[scala.Int]] | java.lang.String]((() => { + val mark: scala.Int = `in₄`.pos + scala.util.Try.apply[scala.util.Try[scala.Option[scala.Int]]]({ + val `mark₂`: scala.Int = `in₄`.pos + try if (`in₄`.expectNull()) null else scala.util.Success.apply[scala.Option[scala.Int]](if (`in₄`.expectNull()) null else scala.Some.apply[scala.Int](`in₄`.expectInt())) catch { + case t: java.lang.Throwable => + `in₄`.revertToPos(`mark₂`) + throw co.blocke.scalajack.json.JsonParseError.apply("Unsuccessful attempt to read Try type with failure: ".+(t.getMessage()), `in₄`) + } + }) match { + case scala.util.Success(lval) => + (lval: scala.util.Try[scala.Option[scala.Int]]) + case scala.util.Failure(f) => + `in₄`.revertToPos(mark) + scala.util.Try.apply[java.lang.String](`in₄`.expectString()) match { + case scala.util.Success(rval) => + (rval: java.lang.String) + case scala.util.Failure(_) => + `in₄`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₄`) + } + } + })) + + (if (parsedArray.!=(null)) parsedArray.toSeq else null: scala.collection.immutable.Seq[scala.util.Try[scala.Option[scala.Int]] | java.lang.String]) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₄`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _b = if (`in₄`.expectNull()) null else { + `in₄`.expectToken('[') + val tv: scala.Tuple2[scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String, scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String] = new scala.Tuple2[scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String, scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String][scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String, scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String]({ + val `mark₃`: scala.Int = `in₄`.pos + scala.util.Try.apply[scala.util.Try[scala.Option[scala.Int]]]({ + val `mark₄`: scala.Int = `in₄`.pos + try if (`in₄`.expectNull()) null else scala.util.Success.apply[scala.Option[scala.Int]](if (`in₄`.expectNull()) null else scala.Some.apply[scala.Int](`in₄`.expectInt())) catch { + case t: java.lang.Throwable => + `in₄`.revertToPos(`mark₄`) + throw co.blocke.scalajack.json.JsonParseError.apply("Unsuccessful attempt to read Try type with failure: ".+(`t₂`.getMessage()), `in₄`) + } + }) match { + case scala.util.Success(lval) => + (`lval₂`: scala.util.Try[scala.Option[scala.Int]]) + case scala.util.Failure(f) => + `in₄`.revertToPos(`mark₃`) + scala.util.Try.apply[java.lang.String](`in₄`.expectString()) match { + case scala.util.Success(rval) => + (`rval₂`: java.lang.String) + case scala.util.Failure(_) => + `in₄`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₄`) + } + } + }, { + `in₄`.expectToken(',') + val `mark₅`: scala.Int = `in₄`.pos + scala.util.Try.apply[scala.util.Try[scala.Option[scala.Int]]]({ + val `mark₆`: scala.Int = `in₄`.pos + try if (`in₄`.expectNull()) null else scala.util.Success.apply[scala.Option[scala.Int]](if (`in₄`.expectNull()) null else scala.Some.apply[scala.Int](`in₄`.expectInt())) catch { + case t: java.lang.Throwable => + `in₄`.revertToPos(`mark₆`) + throw co.blocke.scalajack.json.JsonParseError.apply("Unsuccessful attempt to read Try type with failure: ".+(`t₃`.getMessage()), `in₄`) + } + }) match { + case scala.util.Success(lval) => + (`lval₃`: scala.util.Try[scala.Option[scala.Int]]) + case scala.util.Failure(f) => + `in₄`.revertToPos(`mark₅`) + scala.util.Try.apply[java.lang.String](`in₄`.expectString()) match { + case scala.util.Success(rval) => + (`rval₃`: java.lang.String) + case scala.util.Failure(_) => + `in₄`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₄`) + } + } + }) + `in₄`.expectToken(']') + + (tv: scala.Tuple2[scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String, scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String]) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b"), `in₄`) + case _ => + `in₄`.skipValue() + } + maybeFieldNum = `in₄`.expectObjectField(__co_blocke_scalajack_json_misc_LRUnionHolder_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String][scala.util.Try[scala.Option[scala.Int]], java.lang.String](_a, _b) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a", "b")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₄`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String]] { + def encodeValue(`in₅`: co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String], `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₅`, `out₄`) + def decodeValue(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String] = r0(`in₆`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String]]) +} +Codec: { + val __co_blocke_scalajack_json_misc_LRUnionHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a", "b")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: scala.collection.immutable.Seq[scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startArray() + in.foreach[scala.Unit](((i: scala.util.Try[scala.Option[scala.Int]] | java.lang.String) => { + out.mark() + scala.util.Try.apply[scala.Unit](out.valueEscaped(i.asInstanceOf[java.lang.String])) match { + case scala.util.Success(_) => + () + case scala.util.Failure(_) => + out.revert() + if (i.asInstanceOf[scala.util.Try[scala.Option[scala.Int]]].==(null)) out.burpNull() else i.asInstanceOf[scala.util.Try[scala.Option[scala.Int]]] match { + case scala.util.Success(v) => + v match { + case null => + out.burpNull() + case scala.None => + () + case scala.Some(v) => + val vv: scala.Int = `v₂`.asInstanceOf[scala.Int] + out.value(vv) + } + case scala.util.Failure(v) => + out.burpNull() + } + } + })) + out.endArray() + } + def w2(`in₂`: scala.Tuple2[scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String, scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startArray() + `out₂`.mark() + scala.util.Try.apply[scala.Unit](`out₂`.valueEscaped(`in₂`._1.asInstanceOf[java.lang.String])) match { + case scala.util.Success(_) => + () + case scala.util.Failure(_) => + `out₂`.revert() + if (`in₂`._1.asInstanceOf[scala.util.Try[scala.Option[scala.Int]]].==(null)) `out₂`.burpNull() else `in₂`._1.asInstanceOf[scala.util.Try[scala.Option[scala.Int]]] match { + case scala.util.Success(v) => + `v₃` match { + case null => + `out₂`.burpNull() + case scala.None => + `out₂`.burpNull() + case scala.Some(v) => + val `vv₂`: scala.Int = `v₄`.asInstanceOf[scala.Int] + `out₂`.value(`vv₂`) + } + case scala.util.Failure(v) => + `out₂`.burpNull() + } + } + `out₂`.mark() + scala.util.Try.apply[scala.Unit](`out₂`.valueEscaped(`in₂`._2.asInstanceOf[java.lang.String])) match { + case scala.util.Success(_) => + () + case scala.util.Failure(_) => + `out₂`.revert() + if (`in₂`._2.asInstanceOf[scala.util.Try[scala.Option[scala.Int]]].==(null)) `out₂`.burpNull() else `in₂`._2.asInstanceOf[scala.util.Try[scala.Option[scala.Int]]] match { + case scala.util.Success(v) => + `v₅` match { + case null => + `out₂`.burpNull() + case scala.None => + `out₂`.burpNull() + case scala.Some(v) => + val `vv₃`: scala.Int = `v₆`.asInstanceOf[scala.Int] + `out₂`.value(`vv₃`) + } + case scala.util.Failure(v) => + `out₂`.burpNull() + } + } + `out₂`.endArray() + } + def w0(`in₃`: co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { + `out₃`.startObject() + `out₃`.label("a") + w1(`in₃`.a, `out₃`) + `out₃`.label("b") + w2(`in₃`.b, `out₃`) + `out₃`.endObject() + } + def r0(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String] = { + var _a: scala.collection.immutable.Seq[scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String] = null + var _b: scala.Tuple2[scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String, scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String] = null + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₄`.expectFirstObjectField(__co_blocke_scalajack_json_misc_LRUnionHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = { + val parsedArray: scala.collection.mutable.ListBuffer[scala.util.Try[scala.Option[scala.Int]] | java.lang.String] = `in₄`.expectArray[scala.util.Try[scala.Option[scala.Int]] | java.lang.String]((() => { + val mark: scala.Int = `in₄`.pos + scala.util.Try.apply[scala.util.Try[scala.Option[scala.Int]]]({ + val `mark₂`: scala.Int = `in₄`.pos + try if (`in₄`.expectNull()) null else scala.util.Success.apply[scala.Option[scala.Int]](if (`in₄`.expectNull()) null else scala.Some.apply[scala.Int](`in₄`.expectInt())) catch { + case t: java.lang.Throwable => + `in₄`.revertToPos(`mark₂`) + throw co.blocke.scalajack.json.JsonParseError.apply("Unsuccessful attempt to read Try type with failure: ".+(t.getMessage()), `in₄`) + } + }) match { + case scala.util.Success(lval) => + (lval: scala.util.Try[scala.Option[scala.Int]]) + case scala.util.Failure(f) => + `in₄`.revertToPos(mark) + scala.util.Try.apply[java.lang.String](`in₄`.expectString()) match { + case scala.util.Success(rval) => + (rval: java.lang.String) + case scala.util.Failure(_) => + `in₄`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₄`) + } + } + })) + + (if (parsedArray.!=(null)) parsedArray.toSeq else null: scala.collection.immutable.Seq[scala.util.Try[scala.Option[scala.Int]] | java.lang.String]) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₄`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _b = if (`in₄`.expectNull()) null else { + `in₄`.expectToken('[') + val tv: scala.Tuple2[scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String, scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String] = new scala.Tuple2[scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String, scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String][scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String, scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String]({ + val `mark₃`: scala.Int = `in₄`.pos + scala.util.Try.apply[scala.util.Try[scala.Option[scala.Int]]]({ + val `mark₄`: scala.Int = `in₄`.pos + try if (`in₄`.expectNull()) null else scala.util.Success.apply[scala.Option[scala.Int]](if (`in₄`.expectNull()) null else scala.Some.apply[scala.Int](`in₄`.expectInt())) catch { + case t: java.lang.Throwable => + `in₄`.revertToPos(`mark₄`) + throw co.blocke.scalajack.json.JsonParseError.apply("Unsuccessful attempt to read Try type with failure: ".+(`t₂`.getMessage()), `in₄`) + } + }) match { + case scala.util.Success(lval) => + (`lval₂`: scala.util.Try[scala.Option[scala.Int]]) + case scala.util.Failure(f) => + `in₄`.revertToPos(`mark₃`) + scala.util.Try.apply[java.lang.String](`in₄`.expectString()) match { + case scala.util.Success(rval) => + (`rval₂`: java.lang.String) + case scala.util.Failure(_) => + `in₄`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₄`) + } + } + }, { + `in₄`.expectToken(',') + val `mark₅`: scala.Int = `in₄`.pos + scala.util.Try.apply[scala.util.Try[scala.Option[scala.Int]]]({ + val `mark₆`: scala.Int = `in₄`.pos + try if (`in₄`.expectNull()) null else scala.util.Success.apply[scala.Option[scala.Int]](if (`in₄`.expectNull()) null else scala.Some.apply[scala.Int](`in₄`.expectInt())) catch { + case t: java.lang.Throwable => + `in₄`.revertToPos(`mark₆`) + throw co.blocke.scalajack.json.JsonParseError.apply("Unsuccessful attempt to read Try type with failure: ".+(`t₃`.getMessage()), `in₄`) + } + }) match { + case scala.util.Success(lval) => + (`lval₃`: scala.util.Try[scala.Option[scala.Int]]) + case scala.util.Failure(f) => + `in₄`.revertToPos(`mark₅`) + scala.util.Try.apply[java.lang.String](`in₄`.expectString()) match { + case scala.util.Success(rval) => + (`rval₃`: java.lang.String) + case scala.util.Failure(_) => + `in₄`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₄`) + } + } + }) + `in₄`.expectToken(']') + + (tv: scala.Tuple2[scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String, scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String]) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b"), `in₄`) + case _ => + `in₄`.skipValue() + } + maybeFieldNum = `in₄`.expectObjectField(__co_blocke_scalajack_json_misc_LRUnionHolder_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String][scala.util.Try[scala.Option[scala.Int]], java.lang.String](_a, _b) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a", "b")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₄`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String]] { + def encodeValue(`in₅`: co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String], `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₅`, `out₄`) + def decodeValue(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String] = r0(`in₆`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String]]) +} +Codec: { + val __co_blocke_scalajack_json_misc_Person_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("name", "age")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + val __co_blocke_scalajack_json_misc_OptionHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a", "b", "c", "d", "e", "f", "g", "h", "i", "j")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: scala.Tuple2[scala.Option[scala.Int], scala.Predef.String], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startArray() + in._1 match { + case null => + out.burpNull() + case scala.None => + out.burpNull() + case scala.Some(v) => + val vv: scala.Int = v.asInstanceOf[scala.Int] + out.value(vv) + } + out.valueEscaped(in._2) + out.endArray() + } + def w2(`in₂`: scala.collection.immutable.List[scala.Option[scala.Int]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startArray() + `in₂`.foreach[scala.Unit](((i: scala.Option[scala.Int]) => i match { + case null => + `out₂`.burpNull() + case scala.None => + () + case scala.Some(v) => + val `vv₂`: scala.Int = `v₂`.asInstanceOf[scala.Int] + `out₂`.value(`vv₂`) + })) + `out₂`.endArray() + } + def w3(`in₃`: scala.collection.immutable.Map[scala.Int, scala.Option[scala.Int]], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { + `out₃`.startObject() + `in₃`.foreach[scala.Unit](((x$1: scala.Tuple2[scala.Int, scala.Option[scala.Int]]) => x$1 match { + case scala.Tuple2(key, value) => + value match { + case scala.None => + () + case scala.Some(v) => + { + `out₃`.maybeComma() + `out₃`.valueStringified(scala.Int.int2long(key)) + `out₃`.colon() + `out₃`.value(`v₃`) + } + } + })) + `out₃`.endObject() + } + def w4(`in₄`: co.blocke.scalajack.json.misc.Person, `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₄`.==(null)) `out₄`.burpNull() else { + `out₄`.startObject() + `out₄`.label("name") + `out₄`.valueEscaped(`in₄`.name) + `out₄`.label("age") + `out₄`.value(`in₄`.age) + `out₄`.endObject() + } + def w0(`in₅`: co.blocke.scalajack.json.misc.OptionHolder[scala.Int], `out₅`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₅`.==(null)) `out₅`.burpNull() else { + `out₅`.startObject() + `in₅`.a match { + case scala.None => + () + case scala.Some(v) => + { + `out₅`.label("a") + `out₅`.value(`v₄`) + } + } + `out₅`.label("b") + w1(`in₅`.b, `out₅`) + `out₅`.label("c") + w2(`in₅`.c, `out₅`) + `out₅`.label("d") + w3(`in₅`.d, `out₅`) + if (`in₅`.e.==(null)) `out₅`.burpNull() else { + `out₅`.mark() + scala.util.Try.apply[scala.Unit](`in₅`.e.asInstanceOf[scala.Option[scala.Int]] match { + case scala.None => + () + case scala.Some(v) => + { + `out₅`.label("e") + `out₅`.value(`v₅`) + } + }) match { + case scala.util.Success(_) => + () + case scala.util.Failure(f) => + `out₅`.revert() + { + `out₅`.label("e") + `out₅`.value(`in₅`.e.asInstanceOf[scala.Int]) + } + } + } + if (`in₅`.f.==(null)) `out₅`.burpNull() else { + `out₅`.mark() + scala.util.Try.apply[scala.Unit]({ + `out₅`.label("f") + `out₅`.value(`in₅`.f.asInstanceOf[scala.Int]) + }) match { + case scala.util.Success(_) => + () + case scala.util.Failure(f) => + `out₅`.revert() + `in₅`.f.asInstanceOf[scala.Option[scala.Int]] match { + case scala.None => + () + case scala.Some(v) => + { + `out₅`.label("f") + `out₅`.value(`v₆`) + } + } + } + } + `in₅`.g match { + case scala.None => + () + case scala.Some(v) => + `v₇` match { + case scala.None => + () + case scala.Some(v) => + { + `out₅`.label("g") + `out₅`.value(`v₈`) + } + } + } + `in₅`.h match { + case scala.None => + () + case scala.Some(v) => + { + `out₅`.label("h") + w4(`v₉`, `out₅`) + } + } + if (`in₅`.i.==(null)) `out₅`.burpNull() else () + `in₅`.i match { + case scala.Left(v) => + { + `out₅`.label("i") + `out₅`.value(`v₁₀`.asInstanceOf[scala.Int]) + } + case scala.Right(v) => + `v₁₁`.asInstanceOf[scala.Option[scala.Int]] match { + case scala.None => + () + case scala.Some(v) => + { + `out₅`.label("i") + `out₅`.value(`v₁₂`) + } + } + } + if (`in₅`.j.==(null)) `out₅`.burpNull() else () + `in₅`.j match { + case scala.Left(v) => + `v₁₃`.asInstanceOf[scala.Option[scala.Int]] match { + case scala.None => + () + case scala.Some(v) => + { + `out₅`.label("j") + `out₅`.value(`v₁₄`) + } + } + case scala.Right(v) => + { + `out₅`.label("j") + `out₅`.value(`v₁₅`.asInstanceOf[scala.Int]) + } + } + `out₅`.endObject() + } + def r1(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.Person = { + var _name: java.lang.String = "" + var _age: scala.Int = 0 + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₆`.expectFirstObjectField(__co_blocke_scalajack_json_misc_Person_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.Person] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _name = `in₆`.expectString() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("name"), `in₆`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _age = `in₆`.expectInt() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("age"), `in₆`) + case _ => + `in₆`.skipValue() + } + maybeFieldNum = `in₆`.expectObjectField(__co_blocke_scalajack_json_misc_Person_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.misc.Person(_name, _age) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("name", "age")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₆`) + } + } + def r0(`in₇`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.OptionHolder[scala.Int] = { + var _a: scala.Option[scala.Int] = scala.None + var _b: scala.Tuple2[scala.Option[scala.Int], scala.Predef.String] = null + var _c: scala.collection.immutable.List[scala.Option[scala.Int]] = null + var _d: scala.collection.immutable.Map[scala.Int, scala.Option[scala.Int]] = null + var _e: scala.Int | scala.Option[scala.Int] = scala.None + var _f: scala.Option[scala.Int] | scala.Int = scala.None + var _g: scala.Option[scala.Option[scala.Int]] = scala.None + var _h: scala.Option[co.blocke.scalajack.json.misc.Person] = scala.None + var _i: scala.util.Either[scala.Int, scala.Option[scala.Int]] = scala.Predef.augmentString("r").foldRight[scala.Any]((scala.None: scala.Any))(((c: scala.Char, acc: scala.Any) => if (c.==('r')) scala.Right.apply[scala.Nothing, scala.Any](acc) else scala.Left.apply[scala.Any, scala.Nothing](acc))).asInstanceOf[scala.util.Either[scala.Int, scala.Option[scala.Int]]] + var _j: scala.util.Either[scala.Option[scala.Int], scala.Int] = scala.Predef.augmentString("l").foldRight[scala.Any]((scala.None: scala.Any))(((`c₂`: scala.Char, `acc₂`: scala.Any) => if (`c₂`.==('r')) scala.Right.apply[scala.Nothing, scala.Any](`acc₂`) else scala.Left.apply[scala.Any, scala.Nothing](`acc₂`))).asInstanceOf[scala.util.Either[scala.Option[scala.Int], scala.Int]] + var `required₂`: scala.Int = 1023 + var `maybeFieldNum₂`: scala.Option[scala.Int] = `in₇`.expectFirstObjectField(__co_blocke_scalajack_json_misc_OptionHolder_fields) + if (`maybeFieldNum₂`.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.OptionHolder[scala.Int]] else { + while (`maybeFieldNum₂`.isDefined) { + `maybeFieldNum₂`.get match { + case 0 => + if (`required₂`.&(1).!=(0)) { + `required₂` = `required₂`.^(1) + _a = if (`in₇`.expectNull()) null else scala.Some.apply[scala.Int](`in₇`.expectInt()) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₇`) + case 1 => + if (`required₂`.&(2).!=(0)) { + `required₂` = `required₂`.^(2) + _b = if (`in₇`.expectNull()) null else { + `in₇`.expectToken('[') + val tv: scala.Tuple2[scala.Option[scala.Int], scala.Predef.String] = new scala.Tuple2[scala.Option[scala.Int], scala.Predef.String][scala.Option[scala.Int], java.lang.String](if (`in₇`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₇`.expectInt()), { + `in₇`.expectToken(',') + `in₇`.expectString() + }) + `in₇`.expectToken(']') + + (tv: scala.Tuple2[scala.Option[scala.Int], scala.Predef.String]) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b"), `in₇`) + case 2 => + if (`required₂`.&(4).!=(0)) { + `required₂` = `required₂`.^(4) + _c = { + val parsedArray: scala.collection.mutable.ListBuffer[scala.Option[scala.Int]] = `in₇`.expectArray[scala.Option[scala.Int]]((() => if (`in₇`.expectNull()) null else scala.Some.apply[scala.Int](`in₇`.expectInt()))) + + (if (parsedArray.!=(null)) parsedArray.toList else null: scala.collection.immutable.List[scala.Option[scala.Int]]) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c"), `in₇`) + case 3 => + if (`required₂`.&(8).!=(0)) { + `required₂` = `required₂`.^(8) + _d = if (`in₇`.expectNull()) null else { + `in₇`.expectToken('{') + `in₇`.parseMap[scala.Int, scala.Option[scala.Int]]((() => scala.Predef.augmentString(`in₇`.expectString()).toInt), (() => if (`in₇`.expectNull()) null else scala.Some.apply[scala.Int](`in₇`.expectInt())), scala.Predef.Map.empty[scala.Int, scala.Option[scala.Int]], true) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d"), `in₇`) + case 4 => + if (`required₂`.&(16).!=(0)) { + `required₂` = `required₂`.^(16) + _e = { + val mark: scala.Int = `in₇`.pos + scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { + case scala.util.Success(lval) => + (lval: scala.Int) + case scala.util.Failure(f) => + `in₇`.revertToPos(mark) + scala.util.Try.apply[scala.Option[scala.Int]](if (`in₇`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₇`.expectInt())) match { + case scala.util.Success(rval) => + (rval: scala.Option[scala.Int]) + case scala.util.Failure(_) => + `in₇`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₇`) + } + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("e"), `in₇`) + case 5 => + if (`required₂`.&(32).!=(0)) { + `required₂` = `required₂`.^(32) + _f = { + val `mark₂`: scala.Int = `in₇`.pos + scala.util.Try.apply[scala.Option[scala.Int]](if (`in₇`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₇`.expectInt())) match { + case scala.util.Success(lval) => + (`lval₂`: scala.Option[scala.Int]) + case scala.util.Failure(f) => + `in₇`.revertToPos(`mark₂`) + scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { + case scala.util.Success(rval) => + (`rval₂`: scala.Int) + case scala.util.Failure(_) => + `in₇`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₇`) + } + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f"), `in₇`) + case 6 => + if (`required₂`.&(64).!=(0)) { + `required₂` = `required₂`.^(64) + _g = if (`in₇`.expectNull()) null else scala.Some.apply[scala.Option[scala.Int]](if (`in₇`.expectNull()) null else scala.Some.apply[scala.Int](`in₇`.expectInt())) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("g"), `in₇`) + case 7 => + if (`required₂`.&(128).!=(0)) { + `required₂` = `required₂`.^(128) + _h = if (`in₇`.expectNull()) null else scala.Some.apply[co.blocke.scalajack.json.misc.Person](r1(`in₇`)) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("h"), `in₇`) + case 8 => + if (`required₂`.&(256).!=(0)) { + `required₂` = `required₂`.^(256) + _i = { + val `mark₃`: scala.Int = `in₇`.pos + if (`in₇`.expectNull()) null else scala.util.Try.apply[scala.Option[scala.Int]](if (`in₇`.expectNull()) null else scala.Some.apply[scala.Int](`in₇`.expectInt())) match { + case scala.util.Success(rval) => + scala.Right.apply[scala.Nothing, scala.Option[scala.Int]](`rval₃`) + case scala.util.Failure(f) => + `in₇`.revertToPos(`mark₃`) + scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { + case scala.util.Success(lval) => + scala.Left.apply[scala.Int, scala.Nothing](`lval₃`) + case scala.util.Failure(_) => + `in₇`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₇`) + } + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i"), `in₇`) + case 9 => + if (`required₂`.&(512).!=(0)) { + `required₂` = `required₂`.^(512) + _j = { + val `mark₄`: scala.Int = `in₇`.pos + if (`in₇`.expectNull()) null else scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { + case scala.util.Success(rval) => + scala.Right.apply[scala.Nothing, scala.Int](`rval₄`) + case scala.util.Failure(f) => + `in₇`.revertToPos(`mark₄`) + scala.util.Try.apply[scala.Option[scala.Int]](if (`in₇`.expectNull()) null else scala.Some.apply[scala.Int](`in₇`.expectInt())) match { + case scala.util.Success(lval) => + scala.Left.apply[scala.Option[scala.Int], scala.Nothing](`lval₄`) + case scala.util.Failure(_) => + `in₇`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₇`) + } + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("j"), `in₇`) + case _ => + `in₇`.skipValue() + } + `maybeFieldNum₂` = `in₇`.expectObjectField(__co_blocke_scalajack_json_misc_OptionHolder_fields) + } + if (`required₂`.&(14).==(0)) new co.blocke.scalajack.json.misc.OptionHolder[scala.Int][scala.Int](_a, _b, _c, _d, _e, _f, _g, _h, _i, _j) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a", "b", "c", "d", "e", "f", "g", "h", "i", "j")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(`required₂`.&(14)))), `in₇`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.OptionHolder[scala.Int]] { + def encodeValue(`in₈`: co.blocke.scalajack.json.misc.OptionHolder[scala.Int], `out₆`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₈`, `out₆`) + def decodeValue(`in₉`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.OptionHolder[scala.Int] = r0(`in₉`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.OptionHolder[scala.Int]]) +} +Codec: { + val __co_blocke_scalajack_json_misc_Person_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("name", "age")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + val __co_blocke_scalajack_json_misc_OptionHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a", "b", "c", "d", "e", "f", "g", "h", "i", "j")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: scala.Tuple2[scala.Option[scala.Int], scala.Predef.String], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startArray() + in._1 match { + case null => + out.burpNull() + case scala.None => + out.burpNull() + case scala.Some(v) => + val vv: scala.Int = v.asInstanceOf[scala.Int] + out.value(vv) + } + out.valueEscaped(in._2) + out.endArray() + } + def w2(`in₂`: scala.collection.immutable.List[scala.Option[scala.Int]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startArray() + `in₂`.foreach[scala.Unit](((i: scala.Option[scala.Int]) => i match { + case null => + `out₂`.burpNull() + case scala.None => + () + case scala.Some(v) => + val `vv₂`: scala.Int = `v₂`.asInstanceOf[scala.Int] + `out₂`.value(`vv₂`) + })) + `out₂`.endArray() + } + def w3(`in₃`: scala.collection.immutable.Map[scala.Int, scala.Option[scala.Int]], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { + `out₃`.startObject() + `in₃`.foreach[scala.Unit](((x$1: scala.Tuple2[scala.Int, scala.Option[scala.Int]]) => x$1 match { + case scala.Tuple2(key, value) => + value match { + case scala.None => + () + case scala.Some(v) => + { + `out₃`.maybeComma() + `out₃`.valueStringified(scala.Int.int2long(key)) + `out₃`.colon() + `out₃`.value(`v₃`) + } + } + })) + `out₃`.endObject() + } + def w4(`in₄`: co.blocke.scalajack.json.misc.Person, `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₄`.==(null)) `out₄`.burpNull() else { + `out₄`.startObject() + `out₄`.label("name") + `out₄`.valueEscaped(`in₄`.name) + `out₄`.label("age") + `out₄`.value(`in₄`.age) + `out₄`.endObject() + } + def w0(`in₅`: co.blocke.scalajack.json.misc.OptionHolder[scala.Int], `out₅`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₅`.==(null)) `out₅`.burpNull() else { + `out₅`.startObject() + `in₅`.a match { + case scala.None => + () + case scala.Some(v) => + { + `out₅`.label("a") + `out₅`.value(`v₄`) + } + } + `out₅`.label("b") + w1(`in₅`.b, `out₅`) + `out₅`.label("c") + w2(`in₅`.c, `out₅`) + `out₅`.label("d") + w3(`in₅`.d, `out₅`) + if (`in₅`.e.==(null)) `out₅`.burpNull() else { + `out₅`.mark() + scala.util.Try.apply[scala.Unit](`in₅`.e.asInstanceOf[scala.Option[scala.Int]] match { + case scala.None => + () + case scala.Some(v) => + { + `out₅`.label("e") + `out₅`.value(`v₅`) + } + }) match { + case scala.util.Success(_) => + () + case scala.util.Failure(f) => + `out₅`.revert() + { + `out₅`.label("e") + `out₅`.value(`in₅`.e.asInstanceOf[scala.Int]) + } + } + } + if (`in₅`.f.==(null)) `out₅`.burpNull() else { + `out₅`.mark() + scala.util.Try.apply[scala.Unit]({ + `out₅`.label("f") + `out₅`.value(`in₅`.f.asInstanceOf[scala.Int]) + }) match { + case scala.util.Success(_) => + () + case scala.util.Failure(f) => + `out₅`.revert() + `in₅`.f.asInstanceOf[scala.Option[scala.Int]] match { + case scala.None => + () + case scala.Some(v) => + { + `out₅`.label("f") + `out₅`.value(`v₆`) + } + } + } + } + `in₅`.g match { + case scala.None => + () + case scala.Some(v) => + `v₇` match { + case scala.None => + () + case scala.Some(v) => + { + `out₅`.label("g") + `out₅`.value(`v₈`) + } + } + } + `in₅`.h match { + case scala.None => + () + case scala.Some(v) => + { + `out₅`.label("h") + w4(`v₉`, `out₅`) + } + } + if (`in₅`.i.==(null)) `out₅`.burpNull() else () + `in₅`.i match { + case scala.Left(v) => + { + `out₅`.label("i") + `out₅`.value(`v₁₀`.asInstanceOf[scala.Int]) + } + case scala.Right(v) => + `v₁₁`.asInstanceOf[scala.Option[scala.Int]] match { + case scala.None => + () + case scala.Some(v) => + { + `out₅`.label("i") + `out₅`.value(`v₁₂`) + } + } + } + if (`in₅`.j.==(null)) `out₅`.burpNull() else () + `in₅`.j match { + case scala.Left(v) => + `v₁₃`.asInstanceOf[scala.Option[scala.Int]] match { + case scala.None => + () + case scala.Some(v) => + { + `out₅`.label("j") + `out₅`.value(`v₁₄`) + } + } + case scala.Right(v) => + { + `out₅`.label("j") + `out₅`.value(`v₁₅`.asInstanceOf[scala.Int]) + } + } + `out₅`.endObject() + } + def r1(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.Person = { + var _name: java.lang.String = "" + var _age: scala.Int = 0 + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₆`.expectFirstObjectField(__co_blocke_scalajack_json_misc_Person_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.Person] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _name = `in₆`.expectString() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("name"), `in₆`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _age = `in₆`.expectInt() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("age"), `in₆`) + case _ => + `in₆`.skipValue() + } + maybeFieldNum = `in₆`.expectObjectField(__co_blocke_scalajack_json_misc_Person_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.misc.Person(_name, _age) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("name", "age")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₆`) + } + } + def r0(`in₇`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.OptionHolder[scala.Int] = { + var _a: scala.Option[scala.Int] = scala.None + var _b: scala.Tuple2[scala.Option[scala.Int], scala.Predef.String] = null + var _c: scala.collection.immutable.List[scala.Option[scala.Int]] = null + var _d: scala.collection.immutable.Map[scala.Int, scala.Option[scala.Int]] = null + var _e: scala.Int | scala.Option[scala.Int] = scala.None + var _f: scala.Option[scala.Int] | scala.Int = scala.None + var _g: scala.Option[scala.Option[scala.Int]] = scala.None + var _h: scala.Option[co.blocke.scalajack.json.misc.Person] = scala.None + var _i: scala.util.Either[scala.Int, scala.Option[scala.Int]] = scala.Predef.augmentString("r").foldRight[scala.Any]((scala.None: scala.Any))(((c: scala.Char, acc: scala.Any) => if (c.==('r')) scala.Right.apply[scala.Nothing, scala.Any](acc) else scala.Left.apply[scala.Any, scala.Nothing](acc))).asInstanceOf[scala.util.Either[scala.Int, scala.Option[scala.Int]]] + var _j: scala.util.Either[scala.Option[scala.Int], scala.Int] = scala.Predef.augmentString("l").foldRight[scala.Any]((scala.None: scala.Any))(((`c₂`: scala.Char, `acc₂`: scala.Any) => if (`c₂`.==('r')) scala.Right.apply[scala.Nothing, scala.Any](`acc₂`) else scala.Left.apply[scala.Any, scala.Nothing](`acc₂`))).asInstanceOf[scala.util.Either[scala.Option[scala.Int], scala.Int]] + var `required₂`: scala.Int = 1023 + var `maybeFieldNum₂`: scala.Option[scala.Int] = `in₇`.expectFirstObjectField(__co_blocke_scalajack_json_misc_OptionHolder_fields) + if (`maybeFieldNum₂`.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.OptionHolder[scala.Int]] else { + while (`maybeFieldNum₂`.isDefined) { + `maybeFieldNum₂`.get match { + case 0 => + if (`required₂`.&(1).!=(0)) { + `required₂` = `required₂`.^(1) + _a = if (`in₇`.expectNull()) null else scala.Some.apply[scala.Int](`in₇`.expectInt()) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₇`) + case 1 => + if (`required₂`.&(2).!=(0)) { + `required₂` = `required₂`.^(2) + _b = if (`in₇`.expectNull()) null else { + `in₇`.expectToken('[') + val tv: scala.Tuple2[scala.Option[scala.Int], scala.Predef.String] = new scala.Tuple2[scala.Option[scala.Int], scala.Predef.String][scala.Option[scala.Int], java.lang.String](if (`in₇`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₇`.expectInt()), { + `in₇`.expectToken(',') + `in₇`.expectString() + }) + `in₇`.expectToken(']') + + (tv: scala.Tuple2[scala.Option[scala.Int], scala.Predef.String]) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b"), `in₇`) + case 2 => + if (`required₂`.&(4).!=(0)) { + `required₂` = `required₂`.^(4) + _c = { + val parsedArray: scala.collection.mutable.ListBuffer[scala.Option[scala.Int]] = `in₇`.expectArray[scala.Option[scala.Int]]((() => if (`in₇`.expectNull()) null else scala.Some.apply[scala.Int](`in₇`.expectInt()))) + + (if (parsedArray.!=(null)) parsedArray.toList else null: scala.collection.immutable.List[scala.Option[scala.Int]]) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c"), `in₇`) + case 3 => + if (`required₂`.&(8).!=(0)) { + `required₂` = `required₂`.^(8) + _d = if (`in₇`.expectNull()) null else { + `in₇`.expectToken('{') + `in₇`.parseMap[scala.Int, scala.Option[scala.Int]]((() => scala.Predef.augmentString(`in₇`.expectString()).toInt), (() => if (`in₇`.expectNull()) null else scala.Some.apply[scala.Int](`in₇`.expectInt())), scala.Predef.Map.empty[scala.Int, scala.Option[scala.Int]], true) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d"), `in₇`) + case 4 => + if (`required₂`.&(16).!=(0)) { + `required₂` = `required₂`.^(16) + _e = { + val mark: scala.Int = `in₇`.pos + scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { + case scala.util.Success(lval) => + (lval: scala.Int) + case scala.util.Failure(f) => + `in₇`.revertToPos(mark) + scala.util.Try.apply[scala.Option[scala.Int]](if (`in₇`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₇`.expectInt())) match { + case scala.util.Success(rval) => + (rval: scala.Option[scala.Int]) + case scala.util.Failure(_) => + `in₇`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₇`) + } + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("e"), `in₇`) + case 5 => + if (`required₂`.&(32).!=(0)) { + `required₂` = `required₂`.^(32) + _f = { + val `mark₂`: scala.Int = `in₇`.pos + scala.util.Try.apply[scala.Option[scala.Int]](if (`in₇`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₇`.expectInt())) match { + case scala.util.Success(lval) => + (`lval₂`: scala.Option[scala.Int]) + case scala.util.Failure(f) => + `in₇`.revertToPos(`mark₂`) + scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { + case scala.util.Success(rval) => + (`rval₂`: scala.Int) + case scala.util.Failure(_) => + `in₇`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₇`) + } + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f"), `in₇`) + case 6 => + if (`required₂`.&(64).!=(0)) { + `required₂` = `required₂`.^(64) + _g = if (`in₇`.expectNull()) null else scala.Some.apply[scala.Option[scala.Int]](if (`in₇`.expectNull()) null else scala.Some.apply[scala.Int](`in₇`.expectInt())) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("g"), `in₇`) + case 7 => + if (`required₂`.&(128).!=(0)) { + `required₂` = `required₂`.^(128) + _h = if (`in₇`.expectNull()) null else scala.Some.apply[co.blocke.scalajack.json.misc.Person](r1(`in₇`)) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("h"), `in₇`) + case 8 => + if (`required₂`.&(256).!=(0)) { + `required₂` = `required₂`.^(256) + _i = { + val `mark₃`: scala.Int = `in₇`.pos + if (`in₇`.expectNull()) null else scala.util.Try.apply[scala.Option[scala.Int]](if (`in₇`.expectNull()) null else scala.Some.apply[scala.Int](`in₇`.expectInt())) match { + case scala.util.Success(rval) => + scala.Right.apply[scala.Nothing, scala.Option[scala.Int]](`rval₃`) + case scala.util.Failure(f) => + `in₇`.revertToPos(`mark₃`) + scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { + case scala.util.Success(lval) => + scala.Left.apply[scala.Int, scala.Nothing](`lval₃`) + case scala.util.Failure(_) => + `in₇`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₇`) + } + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i"), `in₇`) + case 9 => + if (`required₂`.&(512).!=(0)) { + `required₂` = `required₂`.^(512) + _j = { + val `mark₄`: scala.Int = `in₇`.pos + if (`in₇`.expectNull()) null else scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { + case scala.util.Success(rval) => + scala.Right.apply[scala.Nothing, scala.Int](`rval₄`) + case scala.util.Failure(f) => + `in₇`.revertToPos(`mark₄`) + scala.util.Try.apply[scala.Option[scala.Int]](if (`in₇`.expectNull()) null else scala.Some.apply[scala.Int](`in₇`.expectInt())) match { + case scala.util.Success(lval) => + scala.Left.apply[scala.Option[scala.Int], scala.Nothing](`lval₄`) + case scala.util.Failure(_) => + `in₇`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₇`) + } + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("j"), `in₇`) + case _ => + `in₇`.skipValue() + } + `maybeFieldNum₂` = `in₇`.expectObjectField(__co_blocke_scalajack_json_misc_OptionHolder_fields) + } + if (`required₂`.&(14).==(0)) new co.blocke.scalajack.json.misc.OptionHolder[scala.Int][scala.Int](_a, _b, _c, _d, _e, _f, _g, _h, _i, _j) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a", "b", "c", "d", "e", "f", "g", "h", "i", "j")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(`required₂`.&(14)))), `in₇`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.OptionHolder[scala.Int]] { + def encodeValue(`in₈`: co.blocke.scalajack.json.misc.OptionHolder[scala.Int], `out₆`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₈`, `out₆`) + def decodeValue(`in₉`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.OptionHolder[scala.Int] = r0(`in₉`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.OptionHolder[scala.Int]]) +} +Codec: { + val __co_blocke_scalajack_json_misc_Person_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("name", "age")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + val __co_blocke_scalajack_json_misc_OptionHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a", "b", "c", "d", "e", "f", "g", "h", "i", "j")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: scala.Tuple2[scala.Option[scala.Int], scala.Predef.String], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startArray() + in._1 match { + case null => + out.burpNull() + case scala.None => + out.burpNull() + case scala.Some(v) => + val vv: scala.Int = v.asInstanceOf[scala.Int] + out.value(vv) + } + out.valueEscaped(in._2) + out.endArray() + } + def w2(`in₂`: scala.collection.immutable.List[scala.Option[scala.Int]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startArray() + `in₂`.foreach[scala.Unit](((i: scala.Option[scala.Int]) => i match { + case null => + `out₂`.burpNull() + case scala.None => + `out₂`.burpNull() + case scala.Some(v) => + val `vv₂`: scala.Int = `v₂`.asInstanceOf[scala.Int] + `out₂`.value(`vv₂`) + })) + `out₂`.endArray() + } + def w3(`in₃`: scala.collection.immutable.Map[scala.Int, scala.Option[scala.Int]], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { + `out₃`.startObject() + `in₃`.foreach[scala.Unit](((x$1: scala.Tuple2[scala.Int, scala.Option[scala.Int]]) => x$1 match { + case scala.Tuple2(key, value) => + { + `out₃`.maybeComma() + `out₃`.valueStringified(scala.Int.int2long(key)) + `out₃`.colon() + value match { + case null => + `out₃`.burpNull() + case scala.None => + `out₃`.burpNull() + case scala.Some(v) => + val `vv₃`: scala.Int = `v₃`.asInstanceOf[scala.Int] + `out₃`.value(`vv₃`) + } + } + })) + `out₃`.endObject() + } + def w4(`in₄`: co.blocke.scalajack.json.misc.Person, `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₄`.==(null)) `out₄`.burpNull() else { + `out₄`.startObject() + `out₄`.label("name") + `out₄`.valueEscaped(`in₄`.name) + `out₄`.label("age") + `out₄`.value(`in₄`.age) + `out₄`.endObject() + } + def w0(`in₅`: co.blocke.scalajack.json.misc.OptionHolder[scala.Int], `out₅`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₅`.==(null)) `out₅`.burpNull() else { + `out₅`.startObject() + `out₅`.label("a") + `in₅`.a match { + case null => + `out₅`.burpNull() + case scala.None => + `out₅`.burpNull() + case scala.Some(v) => + val `vv₄`: scala.Int = `v₄`.asInstanceOf[scala.Int] + `out₅`.value(`vv₄`) + } + `out₅`.label("b") + w1(`in₅`.b, `out₅`) + `out₅`.label("c") + w2(`in₅`.c, `out₅`) + `out₅`.label("d") + w3(`in₅`.d, `out₅`) + if (`in₅`.e.==(null)) `out₅`.burpNull() else { + `out₅`.mark() + scala.util.Try.apply[scala.Unit]({ + `out₅`.label("e") + `in₅`.e.asInstanceOf[scala.Option[scala.Int]] match { + case null => + `out₅`.burpNull() + case scala.None => + `out₅`.burpNull() + case scala.Some(v) => + val `vv₅`: scala.Int = `v₅`.asInstanceOf[scala.Int] + `out₅`.value(`vv₅`) + } + }) match { + case scala.util.Success(_) => + () + case scala.util.Failure(f) => + `out₅`.revert() + { + `out₅`.label("e") + `out₅`.value(`in₅`.e.asInstanceOf[scala.Int]) + } + } + } + if (`in₅`.f.==(null)) `out₅`.burpNull() else { + `out₅`.mark() + scala.util.Try.apply[scala.Unit]({ + `out₅`.label("f") + `out₅`.value(`in₅`.f.asInstanceOf[scala.Int]) + }) match { + case scala.util.Success(_) => + () + case scala.util.Failure(f) => + `out₅`.revert() + { + `out₅`.label("f") + `in₅`.f.asInstanceOf[scala.Option[scala.Int]] match { + case null => + `out₅`.burpNull() + case scala.None => + `out₅`.burpNull() + case scala.Some(v) => + val `vv₆`: scala.Int = `v₆`.asInstanceOf[scala.Int] + `out₅`.value(`vv₆`) + } + } + } + } + `out₅`.label("g") + `in₅`.g match { + case null => + `out₅`.burpNull() + case scala.None => + `out₅`.burpNull() + case scala.Some(v) => + val `vv₇`: scala.Option[scala.Int] = `v₇`.asInstanceOf[scala.Option[scala.Int]] + `vv₇` match { + case null => + `out₅`.burpNull() + case scala.None => + `out₅`.burpNull() + case scala.Some(v) => + val `vv₈`: scala.Int = `v₈`.asInstanceOf[scala.Int] + `out₅`.value(`vv₈`) + } + } + `out₅`.label("h") + `in₅`.h match { + case null => + `out₅`.burpNull() + case scala.None => + `out₅`.burpNull() + case scala.Some(v) => + val `vv₉`: co.blocke.scalajack.json.misc.Person = `v₉`.asInstanceOf[co.blocke.scalajack.json.misc.Person] + w4(`vv₉`, `out₅`) + } + if (`in₅`.i.==(null)) `out₅`.burpNull() else () + `in₅`.i match { + case scala.Left(v) => + { + `out₅`.label("i") + `out₅`.value(`v₁₀`.asInstanceOf[scala.Int]) + } + case scala.Right(v) => + { + `out₅`.label("i") + `v₁₁`.asInstanceOf[scala.Option[scala.Int]] match { + case null => + `out₅`.burpNull() + case scala.None => + `out₅`.burpNull() + case scala.Some(v) => + val `vv₁₀`: scala.Int = `v₁₂`.asInstanceOf[scala.Int] + `out₅`.value(`vv₁₀`) + } + } + } + if (`in₅`.j.==(null)) `out₅`.burpNull() else () + `in₅`.j match { + case scala.Left(v) => + { + `out₅`.label("j") + `v₁₃`.asInstanceOf[scala.Option[scala.Int]] match { + case null => + `out₅`.burpNull() + case scala.None => + `out₅`.burpNull() + case scala.Some(v) => + val `vv₁₁`: scala.Int = `v₁₄`.asInstanceOf[scala.Int] + `out₅`.value(`vv₁₁`) + } + } + case scala.Right(v) => + { + `out₅`.label("j") + `out₅`.value(`v₁₅`.asInstanceOf[scala.Int]) + } + } + `out₅`.endObject() + } + def r1(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.Person = { + var _name: java.lang.String = "" + var _age: scala.Int = 0 + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₆`.expectFirstObjectField(__co_blocke_scalajack_json_misc_Person_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.Person] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _name = `in₆`.expectString() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("name"), `in₆`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _age = `in₆`.expectInt() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("age"), `in₆`) + case _ => + `in₆`.skipValue() + } + maybeFieldNum = `in₆`.expectObjectField(__co_blocke_scalajack_json_misc_Person_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.misc.Person(_name, _age) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("name", "age")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₆`) + } + } + def r0(`in₇`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.OptionHolder[scala.Int] = { + var _a: scala.Option[scala.Int] = scala.None + var _b: scala.Tuple2[scala.Option[scala.Int], scala.Predef.String] = null + var _c: scala.collection.immutable.List[scala.Option[scala.Int]] = null + var _d: scala.collection.immutable.Map[scala.Int, scala.Option[scala.Int]] = null + var _e: scala.Int | scala.Option[scala.Int] = scala.None + var _f: scala.Option[scala.Int] | scala.Int = scala.None + var _g: scala.Option[scala.Option[scala.Int]] = scala.None + var _h: scala.Option[co.blocke.scalajack.json.misc.Person] = scala.None + var _i: scala.util.Either[scala.Int, scala.Option[scala.Int]] = scala.Predef.augmentString("r").foldRight[scala.Any]((scala.None: scala.Any))(((c: scala.Char, acc: scala.Any) => if (c.==('r')) scala.Right.apply[scala.Nothing, scala.Any](acc) else scala.Left.apply[scala.Any, scala.Nothing](acc))).asInstanceOf[scala.util.Either[scala.Int, scala.Option[scala.Int]]] + var _j: scala.util.Either[scala.Option[scala.Int], scala.Int] = scala.Predef.augmentString("l").foldRight[scala.Any]((scala.None: scala.Any))(((`c₂`: scala.Char, `acc₂`: scala.Any) => if (`c₂`.==('r')) scala.Right.apply[scala.Nothing, scala.Any](`acc₂`) else scala.Left.apply[scala.Any, scala.Nothing](`acc₂`))).asInstanceOf[scala.util.Either[scala.Option[scala.Int], scala.Int]] + var `required₂`: scala.Int = 1023 + var `maybeFieldNum₂`: scala.Option[scala.Int] = `in₇`.expectFirstObjectField(__co_blocke_scalajack_json_misc_OptionHolder_fields) + if (`maybeFieldNum₂`.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.OptionHolder[scala.Int]] else { + while (`maybeFieldNum₂`.isDefined) { + `maybeFieldNum₂`.get match { + case 0 => + if (`required₂`.&(1).!=(0)) { + `required₂` = `required₂`.^(1) + _a = if (`in₇`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₇`.expectInt()) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₇`) + case 1 => + if (`required₂`.&(2).!=(0)) { + `required₂` = `required₂`.^(2) + _b = if (`in₇`.expectNull()) null else { + `in₇`.expectToken('[') + val tv: scala.Tuple2[scala.Option[scala.Int], scala.Predef.String] = new scala.Tuple2[scala.Option[scala.Int], scala.Predef.String][scala.Option[scala.Int], java.lang.String](if (`in₇`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₇`.expectInt()), { + `in₇`.expectToken(',') + `in₇`.expectString() + }) + `in₇`.expectToken(']') + + (tv: scala.Tuple2[scala.Option[scala.Int], scala.Predef.String]) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b"), `in₇`) + case 2 => + if (`required₂`.&(4).!=(0)) { + `required₂` = `required₂`.^(4) + _c = { + val parsedArray: scala.collection.mutable.ListBuffer[scala.Option[scala.Int]] = `in₇`.expectArray[scala.Option[scala.Int]]((() => if (`in₇`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₇`.expectInt()))) + + (if (parsedArray.!=(null)) parsedArray.toList else null: scala.collection.immutable.List[scala.Option[scala.Int]]) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c"), `in₇`) + case 3 => + if (`required₂`.&(8).!=(0)) { + `required₂` = `required₂`.^(8) + _d = if (`in₇`.expectNull()) null else { + `in₇`.expectToken('{') + `in₇`.parseMap[scala.Int, scala.Option[scala.Int]]((() => scala.Predef.augmentString(`in₇`.expectString()).toInt), (() => if (`in₇`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₇`.expectInt())), scala.Predef.Map.empty[scala.Int, scala.Option[scala.Int]], true) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d"), `in₇`) + case 4 => + if (`required₂`.&(16).!=(0)) { + `required₂` = `required₂`.^(16) + _e = { + val mark: scala.Int = `in₇`.pos + scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { + case scala.util.Success(lval) => + (lval: scala.Int) + case scala.util.Failure(f) => + `in₇`.revertToPos(mark) + scala.util.Try.apply[scala.Option[scala.Int]](if (`in₇`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₇`.expectInt())) match { + case scala.util.Success(rval) => + (rval: scala.Option[scala.Int]) + case scala.util.Failure(_) => + `in₇`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₇`) + } + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("e"), `in₇`) + case 5 => + if (`required₂`.&(32).!=(0)) { + `required₂` = `required₂`.^(32) + _f = { + val `mark₂`: scala.Int = `in₇`.pos + scala.util.Try.apply[scala.Option[scala.Int]](if (`in₇`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₇`.expectInt())) match { + case scala.util.Success(lval) => + (`lval₂`: scala.Option[scala.Int]) + case scala.util.Failure(f) => + `in₇`.revertToPos(`mark₂`) + scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { + case scala.util.Success(rval) => + (`rval₂`: scala.Int) + case scala.util.Failure(_) => + `in₇`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₇`) + } + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f"), `in₇`) + case 6 => + if (`required₂`.&(64).!=(0)) { + `required₂` = `required₂`.^(64) + _g = if (`in₇`.expectNull()) scala.None else scala.Some.apply[scala.Option[scala.Int]](if (`in₇`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₇`.expectInt())) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("g"), `in₇`) + case 7 => + if (`required₂`.&(128).!=(0)) { + `required₂` = `required₂`.^(128) + _h = if (`in₇`.expectNull()) scala.None else scala.Some.apply[co.blocke.scalajack.json.misc.Person](r1(`in₇`)) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("h"), `in₇`) + case 8 => + if (`required₂`.&(256).!=(0)) { + `required₂` = `required₂`.^(256) + _i = { + val `mark₃`: scala.Int = `in₇`.pos + if (`in₇`.expectNull()) null else scala.util.Try.apply[scala.Option[scala.Int]](if (`in₇`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₇`.expectInt())) match { + case scala.util.Success(rval) => + scala.Right.apply[scala.Nothing, scala.Option[scala.Int]](`rval₃`) + case scala.util.Failure(f) => + `in₇`.revertToPos(`mark₃`) + scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { + case scala.util.Success(lval) => + scala.Left.apply[scala.Int, scala.Nothing](`lval₃`) + case scala.util.Failure(_) => + `in₇`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₇`) + } + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i"), `in₇`) + case 9 => + if (`required₂`.&(512).!=(0)) { + `required₂` = `required₂`.^(512) + _j = { + val `mark₄`: scala.Int = `in₇`.pos + if (`in₇`.expectNull()) null else scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { + case scala.util.Success(rval) => + scala.Right.apply[scala.Nothing, scala.Int](`rval₄`) + case scala.util.Failure(f) => + `in₇`.revertToPos(`mark₄`) + scala.util.Try.apply[scala.Option[scala.Int]](if (`in₇`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₇`.expectInt())) match { + case scala.util.Success(lval) => + scala.Left.apply[scala.Option[scala.Int], scala.Nothing](`lval₄`) + case scala.util.Failure(_) => + `in₇`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₇`) + } + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("j"), `in₇`) + case _ => + `in₇`.skipValue() + } + `maybeFieldNum₂` = `in₇`.expectObjectField(__co_blocke_scalajack_json_misc_OptionHolder_fields) + } + if (`required₂`.&(14).==(0)) new co.blocke.scalajack.json.misc.OptionHolder[scala.Int][scala.Int](_a, _b, _c, _d, _e, _f, _g, _h, _i, _j) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a", "b", "c", "d", "e", "f", "g", "h", "i", "j")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(`required₂`.&(14)))), `in₇`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.OptionHolder[scala.Int]] { + def encodeValue(`in₈`: co.blocke.scalajack.json.misc.OptionHolder[scala.Int], `out₆`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₈`, `out₆`) + def decodeValue(`in₉`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.OptionHolder[scala.Int] = r0(`in₉`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.OptionHolder[scala.Int]]) +} +Codec: { + val __co_blocke_scalajack_json_misc_EitherRecipe_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.misc.EitherRecipe[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + if (in.a.==(null)) out.burpNull() else () + in.a match { + case scala.Left(v) => + { + out.label("a") + out.value(v.asInstanceOf[scala.Boolean]) + } + case scala.Right(v) => + { + if (`v₂`.asInstanceOf[scala.util.Either[scala.Option[scala.Int], scala.Predef.String]].==(null)) out.burpNull() else () + `v₂`.asInstanceOf[scala.util.Either[scala.Option[scala.Int], scala.Predef.String]] match { + case scala.Left(v) => + `v₃`.asInstanceOf[scala.Option[scala.Int]] match { + case scala.None => + () + case scala.Some(v) => + { + out.label("a") + out.value(`v₄`) + } + } + case scala.Right(v) => + { + out.label("a") + out.valueEscaped(`v₅`.asInstanceOf[java.lang.String]) + } + } + } + } + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.EitherRecipe[scala.Int] = { + var _a: scala.util.Either[scala.Boolean, scala.util.Either[scala.Option[scala.Int], scala.Predef.String]] = scala.Predef.augmentString("rl").foldRight[scala.Any]((scala.None: scala.Any))(((c: scala.Char, acc: scala.Any) => if (c.==('r')) scala.Right.apply[scala.Nothing, scala.Any](acc) else scala.Left.apply[scala.Any, scala.Nothing](acc))).asInstanceOf[scala.util.Either[scala.Boolean, scala.util.Either[scala.Option[scala.Int], scala.Predef.String]]] + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_EitherRecipe_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.EitherRecipe[scala.Int]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = { + val mark: scala.Int = `in₂`.pos + if (`in₂`.expectNull()) null else scala.util.Try.apply[scala.util.Either[scala.Option[scala.Int], scala.Predef.String]]({ + val `mark₂`: scala.Int = `in₂`.pos + if (`in₂`.expectNull()) null else scala.util.Try.apply[java.lang.String](`in₂`.expectString()) match { + case scala.util.Success(rval) => + scala.Right.apply[scala.Nothing, java.lang.String](rval) + case scala.util.Failure(f) => + `in₂`.revertToPos(`mark₂`) + scala.util.Try.apply[scala.Option[scala.Int]](if (`in₂`.expectNull()) null else scala.Some.apply[scala.Int](`in₂`.expectInt())) match { + case scala.util.Success(lval) => + scala.Left.apply[scala.Option[scala.Int], scala.Nothing](lval) + case scala.util.Failure(_) => + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) + } + } + }) match { + case scala.util.Success(rval) => + scala.Right.apply[scala.Nothing, scala.util.Either[scala.Option[scala.Int], scala.Predef.String]](`rval₂`) + case scala.util.Failure(f) => + `in₂`.revertToPos(mark) + scala.util.Try.apply[scala.Boolean](`in₂`.expectBoolean()) match { + case scala.util.Success(lval) => + scala.Left.apply[scala.Boolean, scala.Nothing](`lval₂`) + case scala.util.Failure(_) => + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) + } + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_EitherRecipe_fields) + } + if (required.&(0).==(0)) new co.blocke.scalajack.json.misc.EitherRecipe[scala.Int][scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(0)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.EitherRecipe[scala.Int]] { + def encodeValue(`in₃`: co.blocke.scalajack.json.misc.EitherRecipe[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.EitherRecipe[scala.Int] = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.EitherRecipe[scala.Int]]) +} +Codec: { + val __co_blocke_scalajack_json_misc_EitherRecipe_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.misc.EitherRecipe[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + if (in.a.==(null)) out.burpNull() else () + in.a match { + case scala.Left(v) => + { + out.label("a") + out.value(v.asInstanceOf[scala.Boolean]) + } + case scala.Right(v) => + { + if (`v₂`.asInstanceOf[scala.util.Either[scala.Option[scala.Int], scala.Predef.String]].==(null)) out.burpNull() else () + `v₂`.asInstanceOf[scala.util.Either[scala.Option[scala.Int], scala.Predef.String]] match { + case scala.Left(v) => + `v₃`.asInstanceOf[scala.Option[scala.Int]] match { + case scala.None => + () + case scala.Some(v) => + { + out.label("a") + out.value(`v₄`) + } + } + case scala.Right(v) => + { + out.label("a") + out.valueEscaped(`v₅`.asInstanceOf[java.lang.String]) + } + } + } + } + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.EitherRecipe[scala.Int] = { + var _a: scala.util.Either[scala.Boolean, scala.util.Either[scala.Option[scala.Int], scala.Predef.String]] = scala.Predef.augmentString("rl").foldRight[scala.Any]((scala.None: scala.Any))(((c: scala.Char, acc: scala.Any) => if (c.==('r')) scala.Right.apply[scala.Nothing, scala.Any](acc) else scala.Left.apply[scala.Any, scala.Nothing](acc))).asInstanceOf[scala.util.Either[scala.Boolean, scala.util.Either[scala.Option[scala.Int], scala.Predef.String]]] + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_EitherRecipe_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.EitherRecipe[scala.Int]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = { + val mark: scala.Int = `in₂`.pos + if (`in₂`.expectNull()) null else scala.util.Try.apply[scala.util.Either[scala.Option[scala.Int], scala.Predef.String]]({ + val `mark₂`: scala.Int = `in₂`.pos + if (`in₂`.expectNull()) null else scala.util.Try.apply[java.lang.String](`in₂`.expectString()) match { + case scala.util.Success(rval) => + scala.Right.apply[scala.Nothing, java.lang.String](rval) + case scala.util.Failure(f) => + `in₂`.revertToPos(`mark₂`) + scala.util.Try.apply[scala.Option[scala.Int]](if (`in₂`.expectNull()) null else scala.Some.apply[scala.Int](`in₂`.expectInt())) match { + case scala.util.Success(lval) => + scala.Left.apply[scala.Option[scala.Int], scala.Nothing](lval) + case scala.util.Failure(_) => + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) + } + } + }) match { + case scala.util.Success(rval) => + scala.Right.apply[scala.Nothing, scala.util.Either[scala.Option[scala.Int], scala.Predef.String]](`rval₂`) + case scala.util.Failure(f) => + `in₂`.revertToPos(mark) + scala.util.Try.apply[scala.Boolean](`in₂`.expectBoolean()) match { + case scala.util.Success(lval) => + scala.Left.apply[scala.Boolean, scala.Nothing](`lval₂`) + case scala.util.Failure(_) => + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) + } + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_EitherRecipe_fields) + } + if (required.&(0).==(0)) new co.blocke.scalajack.json.misc.EitherRecipe[scala.Int][scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(0)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.EitherRecipe[scala.Int]] { + def encodeValue(`in₃`: co.blocke.scalajack.json.misc.EitherRecipe[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.EitherRecipe[scala.Int] = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.EitherRecipe[scala.Int]]) +} +Codec: { + val __co_blocke_scalajack_json_misc_EitherRecipe_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.misc.EitherRecipe[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + if (in.a.==(null)) out.burpNull() else () + in.a match { + case scala.Left(v) => + { + out.label("a") + out.value(v.asInstanceOf[scala.Boolean]) + } + case scala.Right(v) => + { + if (`v₂`.asInstanceOf[scala.util.Either[scala.Option[scala.Int], scala.Predef.String]].==(null)) out.burpNull() else () + `v₂`.asInstanceOf[scala.util.Either[scala.Option[scala.Int], scala.Predef.String]] match { + case scala.Left(v) => + { + out.label("a") + `v₃`.asInstanceOf[scala.Option[scala.Int]] match { + case null => + out.burpNull() + case scala.None => + out.burpNull() + case scala.Some(v) => + val vv: scala.Int = `v₄`.asInstanceOf[scala.Int] + out.value(vv) + } + } + case scala.Right(v) => + { + out.label("a") + out.valueEscaped(`v₅`.asInstanceOf[java.lang.String]) + } + } + } + } + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.EitherRecipe[scala.Int] = { + var _a: scala.util.Either[scala.Boolean, scala.util.Either[scala.Option[scala.Int], scala.Predef.String]] = scala.Predef.augmentString("rl").foldRight[scala.Any]((scala.None: scala.Any))(((c: scala.Char, acc: scala.Any) => if (c.==('r')) scala.Right.apply[scala.Nothing, scala.Any](acc) else scala.Left.apply[scala.Any, scala.Nothing](acc))).asInstanceOf[scala.util.Either[scala.Boolean, scala.util.Either[scala.Option[scala.Int], scala.Predef.String]]] + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_EitherRecipe_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.EitherRecipe[scala.Int]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = { + val mark: scala.Int = `in₂`.pos + if (`in₂`.expectNull()) null else scala.util.Try.apply[scala.util.Either[scala.Option[scala.Int], scala.Predef.String]]({ + val `mark₂`: scala.Int = `in₂`.pos + if (`in₂`.expectNull()) null else scala.util.Try.apply[java.lang.String](`in₂`.expectString()) match { + case scala.util.Success(rval) => + scala.Right.apply[scala.Nothing, java.lang.String](rval) + case scala.util.Failure(f) => + `in₂`.revertToPos(`mark₂`) + scala.util.Try.apply[scala.Option[scala.Int]](if (`in₂`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₂`.expectInt())) match { + case scala.util.Success(lval) => + scala.Left.apply[scala.Option[scala.Int], scala.Nothing](lval) + case scala.util.Failure(_) => + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) + } + } + }) match { + case scala.util.Success(rval) => + scala.Right.apply[scala.Nothing, scala.util.Either[scala.Option[scala.Int], scala.Predef.String]](`rval₂`) + case scala.util.Failure(f) => + `in₂`.revertToPos(mark) + scala.util.Try.apply[scala.Boolean](`in₂`.expectBoolean()) match { + case scala.util.Success(lval) => + scala.Left.apply[scala.Boolean, scala.Nothing](`lval₂`) + case scala.util.Failure(_) => + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) + } + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_EitherRecipe_fields) + } + if (required.&(0).==(0)) new co.blocke.scalajack.json.misc.EitherRecipe[scala.Int][scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(0)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.EitherRecipe[scala.Int]] { + def encodeValue(`in₃`: co.blocke.scalajack.json.misc.EitherRecipe[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.EitherRecipe[scala.Int] = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.EitherRecipe[scala.Int]]) +} +Codec: { + val __co_blocke_scalajack_json_misc_Person_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("name", "age")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + val __co_blocke_scalajack_json_misc_OptionalHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a", "b", "c", "d", "e", "f", "g", "h", "i", "j")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: scala.Tuple2[java.util.Optional[scala.Int], scala.Predef.String], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startArray() + in._1.asInstanceOf[java.util.Optional[scala.Int]] match { + case null => + out.burpNull() + case o if o.isEmpty() => + out.burpNull() + case o => + val vv: scala.Int = `o₂`.get().asInstanceOf[scala.Int] + out.value(vv) + } + out.valueEscaped(in._2) + out.endArray() + } + def w2(`in₂`: scala.collection.immutable.List[java.util.Optional[scala.Int]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startArray() + `in₂`.foreach[scala.Unit](((i: java.util.Optional[scala.Int]) => i.asInstanceOf[java.util.Optional[scala.Int]] match { + case null => + `out₂`.burpNull() + case o if `o₃`.isEmpty() => + () + case o => + val `vv₂`: scala.Int = `o₄`.get().asInstanceOf[scala.Int] + `out₂`.value(`vv₂`) + })) + `out₂`.endArray() + } + def w3(`in₃`: scala.collection.immutable.Map[scala.Int, java.util.Optional[scala.Int]], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { + `out₃`.startObject() + `in₃`.foreach[scala.Unit](((x$1: scala.Tuple2[scala.Int, java.util.Optional[scala.Int]]) => x$1 match { + case scala.Tuple2(key, value) => + if (value.isEmpty().unary_!) { + `out₃`.maybeComma() + `out₃`.valueStringified(scala.Int.int2long(key)) + `out₃`.colon() + `out₃`.value(value.get()) + } else () + })) + `out₃`.endObject() + } + def w4(`in₄`: co.blocke.scalajack.json.misc.Person, `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₄`.==(null)) `out₄`.burpNull() else { + `out₄`.startObject() + `out₄`.label("name") + `out₄`.valueEscaped(`in₄`.name) + `out₄`.label("age") + `out₄`.value(`in₄`.age) + `out₄`.endObject() + } + def w0(`in₅`: co.blocke.scalajack.json.misc.OptionalHolder[scala.Int], `out₅`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₅`.==(null)) `out₅`.burpNull() else { + `out₅`.startObject() + if (`in₅`.a.isEmpty().unary_!) { + `out₅`.label("a") + `out₅`.value(`in₅`.a.get()) + } else () + `out₅`.label("b") + w1(`in₅`.b, `out₅`) + `out₅`.label("c") + w2(`in₅`.c, `out₅`) + `out₅`.label("d") + w3(`in₅`.d, `out₅`) + if (`in₅`.e.==(null)) `out₅`.burpNull() else { + `out₅`.mark() + scala.util.Try.apply[scala.Unit](if (`in₅`.e.asInstanceOf[java.util.Optional[scala.Int]].isEmpty().unary_!) { + `out₅`.label("e") + `out₅`.value(`in₅`.e.asInstanceOf[java.util.Optional[scala.Int]].get()) + } else ()) match { + case scala.util.Success(_) => + () + case scala.util.Failure(f) => + `out₅`.revert() + { + `out₅`.label("e") + `out₅`.value(`in₅`.e.asInstanceOf[scala.Int]) + } + } + } + if (`in₅`.f.==(null)) `out₅`.burpNull() else { + `out₅`.mark() + scala.util.Try.apply[scala.Unit]({ + `out₅`.label("f") + `out₅`.value(`in₅`.f.asInstanceOf[scala.Int]) + }) match { + case scala.util.Success(_) => + () + case scala.util.Failure(f) => + `out₅`.revert() + if (`in₅`.f.asInstanceOf[java.util.Optional[scala.Int]].isEmpty().unary_!) { + `out₅`.label("f") + `out₅`.value(`in₅`.f.asInstanceOf[java.util.Optional[scala.Int]].get()) + } else () + } + } + if (`in₅`.g.isEmpty().unary_!) if (`in₅`.g.get().isEmpty().unary_!) { + `out₅`.label("g") + `out₅`.value(`in₅`.g.get().get()) + } else () else () + if (`in₅`.h.isEmpty().unary_!) { + `out₅`.label("h") + w4(`in₅`.h.get(), `out₅`) + } else () + if (`in₅`.i.==(null)) `out₅`.burpNull() else () + `in₅`.i match { + case scala.Left(v) => + { + `out₅`.label("i") + `out₅`.value(v.asInstanceOf[scala.Int]) + } + case scala.Right(v) => + if (`v₂`.asInstanceOf[java.util.Optional[scala.Int]].isEmpty().unary_!) { + `out₅`.label("i") + `out₅`.value(`v₂`.asInstanceOf[java.util.Optional[scala.Int]].get()) + } else () + } + if (`in₅`.j.==(null)) `out₅`.burpNull() else () + `in₅`.j match { + case scala.Left(v) => + if (`v₃`.asInstanceOf[java.util.Optional[scala.Int]].isEmpty().unary_!) { + `out₅`.label("j") + `out₅`.value(`v₃`.asInstanceOf[java.util.Optional[scala.Int]].get()) + } else () + case scala.Right(v) => + { + `out₅`.label("j") + `out₅`.value(`v₄`.asInstanceOf[scala.Int]) + } + } + `out₅`.endObject() + } + def r1(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.Person = { + var _name: java.lang.String = "" + var _age: scala.Int = 0 + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₆`.expectFirstObjectField(__co_blocke_scalajack_json_misc_Person_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.Person] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _name = `in₆`.expectString() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("name"), `in₆`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _age = `in₆`.expectInt() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("age"), `in₆`) + case _ => + `in₆`.skipValue() + } + maybeFieldNum = `in₆`.expectObjectField(__co_blocke_scalajack_json_misc_Person_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.misc.Person(_name, _age) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("name", "age")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₆`) + } + } + def r0(`in₇`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.OptionalHolder[scala.Int] = { + var _a: java.util.Optional[scala.Int] = java.util.Optional.empty[java.lang.Object]().asInstanceOf[java.util.Optional[scala.Int]] + var _b: scala.Tuple2[java.util.Optional[scala.Int], scala.Predef.String] = null + var _c: scala.collection.immutable.List[java.util.Optional[scala.Int]] = null + var _d: scala.collection.immutable.Map[scala.Int, java.util.Optional[scala.Int]] = null + var _e: scala.Int | java.util.Optional[scala.Int] = java.util.Optional.empty[java.lang.Object]().asInstanceOf[scala.Int | java.util.Optional[scala.Int]] + var _f: java.util.Optional[scala.Int] | scala.Int = java.util.Optional.empty[java.lang.Object]().asInstanceOf[java.util.Optional[scala.Int] | scala.Int] + var _g: java.util.Optional[java.util.Optional[scala.Int]] = java.util.Optional.empty[java.lang.Object]().asInstanceOf[java.util.Optional[java.util.Optional[scala.Int]]] + var _h: java.util.Optional[co.blocke.scalajack.json.misc.Person] = java.util.Optional.empty[java.lang.Object]().asInstanceOf[java.util.Optional[co.blocke.scalajack.json.misc.Person]] + var _i: scala.util.Either[scala.Int, java.util.Optional[scala.Int]] = scala.Predef.augmentString("r").foldRight[scala.Any]((java.util.Optional.empty[java.lang.Object](): scala.Any))(((c: scala.Char, acc: scala.Any) => if (c.==('r')) scala.Right.apply[scala.Nothing, scala.Any](acc) else scala.Left.apply[scala.Any, scala.Nothing](acc))).asInstanceOf[scala.util.Either[scala.Int, java.util.Optional[scala.Int]]] + var _j: scala.util.Either[java.util.Optional[scala.Int], scala.Int] = scala.Predef.augmentString("l").foldRight[scala.Any]((java.util.Optional.empty[java.lang.Object](): scala.Any))(((`c₂`: scala.Char, `acc₂`: scala.Any) => if (`c₂`.==('r')) scala.Right.apply[scala.Nothing, scala.Any](`acc₂`) else scala.Left.apply[scala.Any, scala.Nothing](`acc₂`))).asInstanceOf[scala.util.Either[java.util.Optional[scala.Int], scala.Int]] + var `required₂`: scala.Int = 1023 + var `maybeFieldNum₂`: scala.Option[scala.Int] = `in₇`.expectFirstObjectField(__co_blocke_scalajack_json_misc_OptionalHolder_fields) + if (`maybeFieldNum₂`.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.OptionalHolder[scala.Int]] else { + while (`maybeFieldNum₂`.isDefined) { + `maybeFieldNum₂`.get match { + case 0 => + if (`required₂`.&(1).!=(0)) { + `required₂` = `required₂`.^(1) + _a = if (`in₇`.expectNull()) null else java.util.Optional.of[scala.Int](`in₇`.expectInt()) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₇`) + case 1 => + if (`required₂`.&(2).!=(0)) { + `required₂` = `required₂`.^(2) + _b = if (`in₇`.expectNull()) null else { + `in₇`.expectToken('[') + val tv: scala.Tuple2[java.util.Optional[scala.Int], scala.Predef.String] = new scala.Tuple2[java.util.Optional[scala.Int], scala.Predef.String][java.util.Optional[scala.Int], java.lang.String](if (`in₇`.expectNull()) java.util.Optional.empty[scala.Int]() else java.util.Optional.of[scala.Int](`in₇`.expectInt()), { + `in₇`.expectToken(',') + `in₇`.expectString() + }) + `in₇`.expectToken(']') + + (tv: scala.Tuple2[java.util.Optional[scala.Int], scala.Predef.String]) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b"), `in₇`) + case 2 => + if (`required₂`.&(4).!=(0)) { + `required₂` = `required₂`.^(4) + _c = { + val parsedArray: scala.collection.mutable.ListBuffer[java.util.Optional[scala.Int]] = `in₇`.expectArray[java.util.Optional[scala.Int]]((() => if (`in₇`.expectNull()) null else java.util.Optional.of[scala.Int](`in₇`.expectInt()))) + + (if (parsedArray.!=(null)) parsedArray.toList else null: scala.collection.immutable.List[java.util.Optional[scala.Int]]) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c"), `in₇`) + case 3 => + if (`required₂`.&(8).!=(0)) { + `required₂` = `required₂`.^(8) + _d = if (`in₇`.expectNull()) null else { + `in₇`.expectToken('{') + `in₇`.parseMap[scala.Int, java.util.Optional[scala.Int]]((() => scala.Predef.augmentString(`in₇`.expectString()).toInt), (() => if (`in₇`.expectNull()) null else java.util.Optional.of[scala.Int](`in₇`.expectInt())), scala.Predef.Map.empty[scala.Int, java.util.Optional[scala.Int]], true) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d"), `in₇`) + case 4 => + if (`required₂`.&(16).!=(0)) { + `required₂` = `required₂`.^(16) + _e = { + val mark: scala.Int = `in₇`.pos + scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { + case scala.util.Success(lval) => + (lval: scala.Int) + case scala.util.Failure(f) => + `in₇`.revertToPos(mark) + scala.util.Try.apply[java.util.Optional[scala.Int]](if (`in₇`.expectNull()) java.util.Optional.empty[scala.Int]() else java.util.Optional.of[scala.Int](`in₇`.expectInt())) match { + case scala.util.Success(rval) => + (rval: java.util.Optional[scala.Int]) + case scala.util.Failure(_) => + `in₇`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₇`) + } + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("e"), `in₇`) + case 5 => + if (`required₂`.&(32).!=(0)) { + `required₂` = `required₂`.^(32) + _f = { + val `mark₂`: scala.Int = `in₇`.pos + scala.util.Try.apply[java.util.Optional[scala.Int]](if (`in₇`.expectNull()) java.util.Optional.empty[scala.Int]() else java.util.Optional.of[scala.Int](`in₇`.expectInt())) match { + case scala.util.Success(lval) => + (`lval₂`: java.util.Optional[scala.Int]) + case scala.util.Failure(f) => + `in₇`.revertToPos(`mark₂`) + scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { + case scala.util.Success(rval) => + (`rval₂`: scala.Int) + case scala.util.Failure(_) => + `in₇`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₇`) + } + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f"), `in₇`) + case 6 => + if (`required₂`.&(64).!=(0)) { + `required₂` = `required₂`.^(64) + _g = if (`in₇`.expectNull()) null else java.util.Optional.of[java.util.Optional[scala.Int]](if (`in₇`.expectNull()) null else java.util.Optional.of[scala.Int](`in₇`.expectInt())) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("g"), `in₇`) + case 7 => + if (`required₂`.&(128).!=(0)) { + `required₂` = `required₂`.^(128) + _h = if (`in₇`.expectNull()) null else java.util.Optional.of[co.blocke.scalajack.json.misc.Person](r1(`in₇`)) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("h"), `in₇`) + case 8 => + if (`required₂`.&(256).!=(0)) { + `required₂` = `required₂`.^(256) + _i = { + val `mark₃`: scala.Int = `in₇`.pos + if (`in₇`.expectNull()) null else scala.util.Try.apply[java.util.Optional[scala.Int]](if (`in₇`.expectNull()) null else java.util.Optional.of[scala.Int](`in₇`.expectInt())) match { + case scala.util.Success(rval) => + scala.Right.apply[scala.Nothing, java.util.Optional[scala.Int]](`rval₃`) + case scala.util.Failure(f) => + `in₇`.revertToPos(`mark₃`) + scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { + case scala.util.Success(lval) => + scala.Left.apply[scala.Int, scala.Nothing](`lval₃`) + case scala.util.Failure(_) => + `in₇`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₇`) + } + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i"), `in₇`) + case 9 => + if (`required₂`.&(512).!=(0)) { + `required₂` = `required₂`.^(512) + _j = { + val `mark₄`: scala.Int = `in₇`.pos + if (`in₇`.expectNull()) null else scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { + case scala.util.Success(rval) => + scala.Right.apply[scala.Nothing, scala.Int](`rval₄`) + case scala.util.Failure(f) => + `in₇`.revertToPos(`mark₄`) + scala.util.Try.apply[java.util.Optional[scala.Int]](if (`in₇`.expectNull()) null else java.util.Optional.of[scala.Int](`in₇`.expectInt())) match { + case scala.util.Success(lval) => + scala.Left.apply[java.util.Optional[scala.Int], scala.Nothing](`lval₄`) + case scala.util.Failure(_) => + `in₇`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₇`) + } + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("j"), `in₇`) + case _ => + `in₇`.skipValue() + } + `maybeFieldNum₂` = `in₇`.expectObjectField(__co_blocke_scalajack_json_misc_OptionalHolder_fields) + } + if (`required₂`.&(14).==(0)) new co.blocke.scalajack.json.misc.OptionalHolder[scala.Int][scala.Int](_a, _b, _c, _d, _e, _f, _g, _h, _i, _j) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a", "b", "c", "d", "e", "f", "g", "h", "i", "j")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(`required₂`.&(14)))), `in₇`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.OptionalHolder[scala.Int]] { + def encodeValue(`in₈`: co.blocke.scalajack.json.misc.OptionalHolder[scala.Int], `out₆`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₈`, `out₆`) + def decodeValue(`in₉`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.OptionalHolder[scala.Int] = r0(`in₉`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.OptionalHolder[scala.Int]]) +} +Codec: { + val __co_blocke_scalajack_json_misc_Person_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("name", "age")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + val __co_blocke_scalajack_json_misc_OptionalHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a", "b", "c", "d", "e", "f", "g", "h", "i", "j")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: scala.Tuple2[java.util.Optional[scala.Int], scala.Predef.String], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startArray() + in._1.asInstanceOf[java.util.Optional[scala.Int]] match { + case null => + out.burpNull() + case o if o.isEmpty() => + out.burpNull() + case o => + val vv: scala.Int = `o₂`.get().asInstanceOf[scala.Int] + out.value(vv) + } + out.valueEscaped(in._2) + out.endArray() + } + def w2(`in₂`: scala.collection.immutable.List[java.util.Optional[scala.Int]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startArray() + `in₂`.foreach[scala.Unit](((i: java.util.Optional[scala.Int]) => i.asInstanceOf[java.util.Optional[scala.Int]] match { + case null => + `out₂`.burpNull() + case o if `o₃`.isEmpty() => + () + case o => + val `vv₂`: scala.Int = `o₄`.get().asInstanceOf[scala.Int] + `out₂`.value(`vv₂`) + })) + `out₂`.endArray() + } + def w3(`in₃`: scala.collection.immutable.Map[scala.Int, java.util.Optional[scala.Int]], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { + `out₃`.startObject() + `in₃`.foreach[scala.Unit](((x$1: scala.Tuple2[scala.Int, java.util.Optional[scala.Int]]) => x$1 match { + case scala.Tuple2(key, value) => + if (value.isEmpty().unary_!) { + `out₃`.maybeComma() + `out₃`.valueStringified(scala.Int.int2long(key)) + `out₃`.colon() + `out₃`.value(value.get()) + } else () + })) + `out₃`.endObject() + } + def w4(`in₄`: co.blocke.scalajack.json.misc.Person, `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₄`.==(null)) `out₄`.burpNull() else { + `out₄`.startObject() + `out₄`.label("name") + `out₄`.valueEscaped(`in₄`.name) + `out₄`.label("age") + `out₄`.value(`in₄`.age) + `out₄`.endObject() + } + def w0(`in₅`: co.blocke.scalajack.json.misc.OptionalHolder[scala.Int], `out₅`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₅`.==(null)) `out₅`.burpNull() else { + `out₅`.startObject() + if (`in₅`.a.isEmpty().unary_!) { + `out₅`.label("a") + `out₅`.value(`in₅`.a.get()) + } else () + `out₅`.label("b") + w1(`in₅`.b, `out₅`) + `out₅`.label("c") + w2(`in₅`.c, `out₅`) + `out₅`.label("d") + w3(`in₅`.d, `out₅`) + if (`in₅`.e.==(null)) `out₅`.burpNull() else { + `out₅`.mark() + scala.util.Try.apply[scala.Unit](if (`in₅`.e.asInstanceOf[java.util.Optional[scala.Int]].isEmpty().unary_!) { + `out₅`.label("e") + `out₅`.value(`in₅`.e.asInstanceOf[java.util.Optional[scala.Int]].get()) + } else ()) match { + case scala.util.Success(_) => + () + case scala.util.Failure(f) => + `out₅`.revert() + { + `out₅`.label("e") + `out₅`.value(`in₅`.e.asInstanceOf[scala.Int]) + } + } + } + if (`in₅`.f.==(null)) `out₅`.burpNull() else { + `out₅`.mark() + scala.util.Try.apply[scala.Unit]({ + `out₅`.label("f") + `out₅`.value(`in₅`.f.asInstanceOf[scala.Int]) + }) match { + case scala.util.Success(_) => + () + case scala.util.Failure(f) => + `out₅`.revert() + if (`in₅`.f.asInstanceOf[java.util.Optional[scala.Int]].isEmpty().unary_!) { + `out₅`.label("f") + `out₅`.value(`in₅`.f.asInstanceOf[java.util.Optional[scala.Int]].get()) + } else () + } + } + if (`in₅`.g.isEmpty().unary_!) if (`in₅`.g.get().isEmpty().unary_!) { + `out₅`.label("g") + `out₅`.value(`in₅`.g.get().get()) + } else () else () + if (`in₅`.h.isEmpty().unary_!) { + `out₅`.label("h") + w4(`in₅`.h.get(), `out₅`) + } else () + if (`in₅`.i.==(null)) `out₅`.burpNull() else () + `in₅`.i match { + case scala.Left(v) => + { + `out₅`.label("i") + `out₅`.value(v.asInstanceOf[scala.Int]) + } + case scala.Right(v) => + if (`v₂`.asInstanceOf[java.util.Optional[scala.Int]].isEmpty().unary_!) { + `out₅`.label("i") + `out₅`.value(`v₂`.asInstanceOf[java.util.Optional[scala.Int]].get()) + } else () + } + if (`in₅`.j.==(null)) `out₅`.burpNull() else () + `in₅`.j match { + case scala.Left(v) => + if (`v₃`.asInstanceOf[java.util.Optional[scala.Int]].isEmpty().unary_!) { + `out₅`.label("j") + `out₅`.value(`v₃`.asInstanceOf[java.util.Optional[scala.Int]].get()) + } else () + case scala.Right(v) => + { + `out₅`.label("j") + `out₅`.value(`v₄`.asInstanceOf[scala.Int]) + } + } + `out₅`.endObject() + } + def r1(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.Person = { + var _name: java.lang.String = "" + var _age: scala.Int = 0 + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₆`.expectFirstObjectField(__co_blocke_scalajack_json_misc_Person_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.Person] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _name = `in₆`.expectString() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("name"), `in₆`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _age = `in₆`.expectInt() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("age"), `in₆`) + case _ => + `in₆`.skipValue() + } + maybeFieldNum = `in₆`.expectObjectField(__co_blocke_scalajack_json_misc_Person_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.misc.Person(_name, _age) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("name", "age")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₆`) + } + } + def r0(`in₇`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.OptionalHolder[scala.Int] = { + var _a: java.util.Optional[scala.Int] = java.util.Optional.empty[java.lang.Object]().asInstanceOf[java.util.Optional[scala.Int]] + var _b: scala.Tuple2[java.util.Optional[scala.Int], scala.Predef.String] = null + var _c: scala.collection.immutable.List[java.util.Optional[scala.Int]] = null + var _d: scala.collection.immutable.Map[scala.Int, java.util.Optional[scala.Int]] = null + var _e: scala.Int | java.util.Optional[scala.Int] = java.util.Optional.empty[java.lang.Object]().asInstanceOf[scala.Int | java.util.Optional[scala.Int]] + var _f: java.util.Optional[scala.Int] | scala.Int = java.util.Optional.empty[java.lang.Object]().asInstanceOf[java.util.Optional[scala.Int] | scala.Int] + var _g: java.util.Optional[java.util.Optional[scala.Int]] = java.util.Optional.empty[java.lang.Object]().asInstanceOf[java.util.Optional[java.util.Optional[scala.Int]]] + var _h: java.util.Optional[co.blocke.scalajack.json.misc.Person] = java.util.Optional.empty[java.lang.Object]().asInstanceOf[java.util.Optional[co.blocke.scalajack.json.misc.Person]] + var _i: scala.util.Either[scala.Int, java.util.Optional[scala.Int]] = scala.Predef.augmentString("r").foldRight[scala.Any]((java.util.Optional.empty[java.lang.Object](): scala.Any))(((c: scala.Char, acc: scala.Any) => if (c.==('r')) scala.Right.apply[scala.Nothing, scala.Any](acc) else scala.Left.apply[scala.Any, scala.Nothing](acc))).asInstanceOf[scala.util.Either[scala.Int, java.util.Optional[scala.Int]]] + var _j: scala.util.Either[java.util.Optional[scala.Int], scala.Int] = scala.Predef.augmentString("l").foldRight[scala.Any]((java.util.Optional.empty[java.lang.Object](): scala.Any))(((`c₂`: scala.Char, `acc₂`: scala.Any) => if (`c₂`.==('r')) scala.Right.apply[scala.Nothing, scala.Any](`acc₂`) else scala.Left.apply[scala.Any, scala.Nothing](`acc₂`))).asInstanceOf[scala.util.Either[java.util.Optional[scala.Int], scala.Int]] + var `required₂`: scala.Int = 1023 + var `maybeFieldNum₂`: scala.Option[scala.Int] = `in₇`.expectFirstObjectField(__co_blocke_scalajack_json_misc_OptionalHolder_fields) + if (`maybeFieldNum₂`.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.OptionalHolder[scala.Int]] else { + while (`maybeFieldNum₂`.isDefined) { + `maybeFieldNum₂`.get match { + case 0 => + if (`required₂`.&(1).!=(0)) { + `required₂` = `required₂`.^(1) + _a = if (`in₇`.expectNull()) null else java.util.Optional.of[scala.Int](`in₇`.expectInt()) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₇`) + case 1 => + if (`required₂`.&(2).!=(0)) { + `required₂` = `required₂`.^(2) + _b = if (`in₇`.expectNull()) null else { + `in₇`.expectToken('[') + val tv: scala.Tuple2[java.util.Optional[scala.Int], scala.Predef.String] = new scala.Tuple2[java.util.Optional[scala.Int], scala.Predef.String][java.util.Optional[scala.Int], java.lang.String](if (`in₇`.expectNull()) java.util.Optional.empty[scala.Int]() else java.util.Optional.of[scala.Int](`in₇`.expectInt()), { + `in₇`.expectToken(',') + `in₇`.expectString() + }) + `in₇`.expectToken(']') + + (tv: scala.Tuple2[java.util.Optional[scala.Int], scala.Predef.String]) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b"), `in₇`) + case 2 => + if (`required₂`.&(4).!=(0)) { + `required₂` = `required₂`.^(4) + _c = { + val parsedArray: scala.collection.mutable.ListBuffer[java.util.Optional[scala.Int]] = `in₇`.expectArray[java.util.Optional[scala.Int]]((() => if (`in₇`.expectNull()) null else java.util.Optional.of[scala.Int](`in₇`.expectInt()))) + + (if (parsedArray.!=(null)) parsedArray.toList else null: scala.collection.immutable.List[java.util.Optional[scala.Int]]) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c"), `in₇`) + case 3 => + if (`required₂`.&(8).!=(0)) { + `required₂` = `required₂`.^(8) + _d = if (`in₇`.expectNull()) null else { + `in₇`.expectToken('{') + `in₇`.parseMap[scala.Int, java.util.Optional[scala.Int]]((() => scala.Predef.augmentString(`in₇`.expectString()).toInt), (() => if (`in₇`.expectNull()) null else java.util.Optional.of[scala.Int](`in₇`.expectInt())), scala.Predef.Map.empty[scala.Int, java.util.Optional[scala.Int]], true) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d"), `in₇`) + case 4 => + if (`required₂`.&(16).!=(0)) { + `required₂` = `required₂`.^(16) + _e = { + val mark: scala.Int = `in₇`.pos + scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { + case scala.util.Success(lval) => + (lval: scala.Int) + case scala.util.Failure(f) => + `in₇`.revertToPos(mark) + scala.util.Try.apply[java.util.Optional[scala.Int]](if (`in₇`.expectNull()) java.util.Optional.empty[scala.Int]() else java.util.Optional.of[scala.Int](`in₇`.expectInt())) match { + case scala.util.Success(rval) => + (rval: java.util.Optional[scala.Int]) + case scala.util.Failure(_) => + `in₇`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₇`) + } + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("e"), `in₇`) + case 5 => + if (`required₂`.&(32).!=(0)) { + `required₂` = `required₂`.^(32) + _f = { + val `mark₂`: scala.Int = `in₇`.pos + scala.util.Try.apply[java.util.Optional[scala.Int]](if (`in₇`.expectNull()) java.util.Optional.empty[scala.Int]() else java.util.Optional.of[scala.Int](`in₇`.expectInt())) match { + case scala.util.Success(lval) => + (`lval₂`: java.util.Optional[scala.Int]) + case scala.util.Failure(f) => + `in₇`.revertToPos(`mark₂`) + scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { + case scala.util.Success(rval) => + (`rval₂`: scala.Int) + case scala.util.Failure(_) => + `in₇`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₇`) + } + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f"), `in₇`) + case 6 => + if (`required₂`.&(64).!=(0)) { + `required₂` = `required₂`.^(64) + _g = if (`in₇`.expectNull()) null else java.util.Optional.of[java.util.Optional[scala.Int]](if (`in₇`.expectNull()) null else java.util.Optional.of[scala.Int](`in₇`.expectInt())) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("g"), `in₇`) + case 7 => + if (`required₂`.&(128).!=(0)) { + `required₂` = `required₂`.^(128) + _h = if (`in₇`.expectNull()) null else java.util.Optional.of[co.blocke.scalajack.json.misc.Person](r1(`in₇`)) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("h"), `in₇`) + case 8 => + if (`required₂`.&(256).!=(0)) { + `required₂` = `required₂`.^(256) + _i = { + val `mark₃`: scala.Int = `in₇`.pos + if (`in₇`.expectNull()) null else scala.util.Try.apply[java.util.Optional[scala.Int]](if (`in₇`.expectNull()) null else java.util.Optional.of[scala.Int](`in₇`.expectInt())) match { + case scala.util.Success(rval) => + scala.Right.apply[scala.Nothing, java.util.Optional[scala.Int]](`rval₃`) + case scala.util.Failure(f) => + `in₇`.revertToPos(`mark₃`) + scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { + case scala.util.Success(lval) => + scala.Left.apply[scala.Int, scala.Nothing](`lval₃`) + case scala.util.Failure(_) => + `in₇`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₇`) + } + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i"), `in₇`) + case 9 => + if (`required₂`.&(512).!=(0)) { + `required₂` = `required₂`.^(512) + _j = { + val `mark₄`: scala.Int = `in₇`.pos + if (`in₇`.expectNull()) null else scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { + case scala.util.Success(rval) => + scala.Right.apply[scala.Nothing, scala.Int](`rval₄`) + case scala.util.Failure(f) => + `in₇`.revertToPos(`mark₄`) + scala.util.Try.apply[java.util.Optional[scala.Int]](if (`in₇`.expectNull()) null else java.util.Optional.of[scala.Int](`in₇`.expectInt())) match { + case scala.util.Success(lval) => + scala.Left.apply[java.util.Optional[scala.Int], scala.Nothing](`lval₄`) + case scala.util.Failure(_) => + `in₇`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₇`) + } + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("j"), `in₇`) + case _ => + `in₇`.skipValue() + } + `maybeFieldNum₂` = `in₇`.expectObjectField(__co_blocke_scalajack_json_misc_OptionalHolder_fields) + } + if (`required₂`.&(14).==(0)) new co.blocke.scalajack.json.misc.OptionalHolder[scala.Int][scala.Int](_a, _b, _c, _d, _e, _f, _g, _h, _i, _j) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a", "b", "c", "d", "e", "f", "g", "h", "i", "j")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(`required₂`.&(14)))), `in₇`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.OptionalHolder[scala.Int]] { + def encodeValue(`in₈`: co.blocke.scalajack.json.misc.OptionalHolder[scala.Int], `out₆`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₈`, `out₆`) + def decodeValue(`in₉`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.OptionalHolder[scala.Int] = r0(`in₉`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.OptionalHolder[scala.Int]]) +} +Codec: { + val __co_blocke_scalajack_json_misc_Person_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("name", "age")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + val __co_blocke_scalajack_json_misc_OptionalHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a", "b", "c", "d", "e", "f", "g", "h", "i", "j")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: scala.Tuple2[java.util.Optional[scala.Int], scala.Predef.String], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startArray() + in._1.asInstanceOf[java.util.Optional[scala.Int]] match { + case null => + out.burpNull() + case o if o.isEmpty() => + out.burpNull() + case o => + val vv: scala.Int = `o₂`.get().asInstanceOf[scala.Int] + out.value(vv) + } + out.valueEscaped(in._2) + out.endArray() + } + def w2(`in₂`: scala.collection.immutable.List[java.util.Optional[scala.Int]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startArray() + `in₂`.foreach[scala.Unit](((i: java.util.Optional[scala.Int]) => i.asInstanceOf[java.util.Optional[scala.Int]] match { + case null => + `out₂`.burpNull() + case o if `o₃`.isEmpty() => + `out₂`.burpNull() + case o => + val `vv₂`: scala.Int = `o₄`.get().asInstanceOf[scala.Int] + `out₂`.value(`vv₂`) + })) + `out₂`.endArray() + } + def w3(`in₃`: scala.collection.immutable.Map[scala.Int, java.util.Optional[scala.Int]], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { + `out₃`.startObject() + `in₃`.foreach[scala.Unit](((x$1: scala.Tuple2[scala.Int, java.util.Optional[scala.Int]]) => x$1 match { + case scala.Tuple2(key, value) => + { + `out₃`.maybeComma() + `out₃`.valueStringified(scala.Int.int2long(key)) + `out₃`.colon() + value.asInstanceOf[java.util.Optional[scala.Int]] match { + case null => + `out₃`.burpNull() + case o if `o₅`.isEmpty() => + `out₃`.burpNull() + case o => + val `vv₃`: scala.Int = `o₆`.get().asInstanceOf[scala.Int] + `out₃`.value(`vv₃`) + } + } + })) + `out₃`.endObject() + } + def w4(`in₄`: co.blocke.scalajack.json.misc.Person, `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₄`.==(null)) `out₄`.burpNull() else { + `out₄`.startObject() + `out₄`.label("name") + `out₄`.valueEscaped(`in₄`.name) + `out₄`.label("age") + `out₄`.value(`in₄`.age) + `out₄`.endObject() + } + def w0(`in₅`: co.blocke.scalajack.json.misc.OptionalHolder[scala.Int], `out₅`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₅`.==(null)) `out₅`.burpNull() else { + `out₅`.startObject() + `out₅`.label("a") + `in₅`.a.asInstanceOf[java.util.Optional[scala.Int]] match { + case null => + `out₅`.burpNull() + case o if `o₇`.isEmpty() => + `out₅`.burpNull() + case o => + val `vv₄`: scala.Int = `o₈`.get().asInstanceOf[scala.Int] + `out₅`.value(`vv₄`) + } + `out₅`.label("b") + w1(`in₅`.b, `out₅`) + `out₅`.label("c") + w2(`in₅`.c, `out₅`) + `out₅`.label("d") + w3(`in₅`.d, `out₅`) + if (`in₅`.e.==(null)) `out₅`.burpNull() else { + `out₅`.mark() + scala.util.Try.apply[scala.Unit]({ + `out₅`.label("e") + `in₅`.e.asInstanceOf[java.util.Optional[scala.Int]].asInstanceOf[java.util.Optional[scala.Int]] match { + case null => + `out₅`.burpNull() + case o if `o₉`.isEmpty() => + `out₅`.burpNull() + case o => + val `vv₅`: scala.Int = `o₁₀`.get().asInstanceOf[scala.Int] + `out₅`.value(`vv₅`) + } + }) match { + case scala.util.Success(_) => + () + case scala.util.Failure(f) => + `out₅`.revert() + { + `out₅`.label("e") + `out₅`.value(`in₅`.e.asInstanceOf[scala.Int]) + } + } + } + if (`in₅`.f.==(null)) `out₅`.burpNull() else { + `out₅`.mark() + scala.util.Try.apply[scala.Unit]({ + `out₅`.label("f") + `out₅`.value(`in₅`.f.asInstanceOf[scala.Int]) + }) match { + case scala.util.Success(_) => + () + case scala.util.Failure(f) => + `out₅`.revert() + { + `out₅`.label("f") + `in₅`.f.asInstanceOf[java.util.Optional[scala.Int]].asInstanceOf[java.util.Optional[scala.Int]] match { + case null => + `out₅`.burpNull() + case o if `o₁₁`.isEmpty() => + `out₅`.burpNull() + case o => + val `vv₆`: scala.Int = `o₁₂`.get().asInstanceOf[scala.Int] + `out₅`.value(`vv₆`) + } + } + } + } + `out₅`.label("g") + `in₅`.g.asInstanceOf[java.util.Optional[java.util.Optional[scala.Int]]] match { + case null => + `out₅`.burpNull() + case o if `o₁₃`.isEmpty() => + `out₅`.burpNull() + case o => + val `vv₇`: java.util.Optional[scala.Int] = `o₁₄`.get().asInstanceOf[java.util.Optional[scala.Int]] + `vv₇`.asInstanceOf[java.util.Optional[scala.Int]] match { + case null => + `out₅`.burpNull() + case o if `o₁₅`.isEmpty() => + `out₅`.burpNull() + case o => + val `vv₈`: scala.Int = `o₁₆`.get().asInstanceOf[scala.Int] + `out₅`.value(`vv₈`) + } + } + `out₅`.label("h") + `in₅`.h.asInstanceOf[java.util.Optional[co.blocke.scalajack.json.misc.Person]] match { + case null => + `out₅`.burpNull() + case o if `o₁₇`.isEmpty() => + `out₅`.burpNull() + case o => + val `vv₉`: co.blocke.scalajack.json.misc.Person = `o₁₈`.get().asInstanceOf[co.blocke.scalajack.json.misc.Person] + w4(`vv₉`, `out₅`) + } + if (`in₅`.i.==(null)) `out₅`.burpNull() else () + `in₅`.i match { + case scala.Left(v) => + { + `out₅`.label("i") + `out₅`.value(v.asInstanceOf[scala.Int]) + } + case scala.Right(v) => + { + `out₅`.label("i") + `v₂`.asInstanceOf[java.util.Optional[scala.Int]].asInstanceOf[java.util.Optional[scala.Int]] match { + case null => + `out₅`.burpNull() + case o if `o₁₉`.isEmpty() => + `out₅`.burpNull() + case o => + val `vv₁₀`: scala.Int = `o₂₀`.get().asInstanceOf[scala.Int] + `out₅`.value(`vv₁₀`) + } + } + } + if (`in₅`.j.==(null)) `out₅`.burpNull() else () + `in₅`.j match { + case scala.Left(v) => + { + `out₅`.label("j") + `v₃`.asInstanceOf[java.util.Optional[scala.Int]].asInstanceOf[java.util.Optional[scala.Int]] match { + case null => + `out₅`.burpNull() + case o if `o₂₁`.isEmpty() => + `out₅`.burpNull() + case o => + val `vv₁₁`: scala.Int = `o₂₂`.get().asInstanceOf[scala.Int] + `out₅`.value(`vv₁₁`) + } + } + case scala.Right(v) => + { + `out₅`.label("j") + `out₅`.value(`v₄`.asInstanceOf[scala.Int]) + } + } + `out₅`.endObject() + } + def r1(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.Person = { + var _name: java.lang.String = "" + var _age: scala.Int = 0 + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₆`.expectFirstObjectField(__co_blocke_scalajack_json_misc_Person_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.Person] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _name = `in₆`.expectString() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("name"), `in₆`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _age = `in₆`.expectInt() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("age"), `in₆`) + case _ => + `in₆`.skipValue() + } + maybeFieldNum = `in₆`.expectObjectField(__co_blocke_scalajack_json_misc_Person_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.misc.Person(_name, _age) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("name", "age")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₆`) + } + } + def r0(`in₇`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.OptionalHolder[scala.Int] = { + var _a: java.util.Optional[scala.Int] = java.util.Optional.empty[java.lang.Object]().asInstanceOf[java.util.Optional[scala.Int]] + var _b: scala.Tuple2[java.util.Optional[scala.Int], scala.Predef.String] = null + var _c: scala.collection.immutable.List[java.util.Optional[scala.Int]] = null + var _d: scala.collection.immutable.Map[scala.Int, java.util.Optional[scala.Int]] = null + var _e: scala.Int | java.util.Optional[scala.Int] = java.util.Optional.empty[java.lang.Object]().asInstanceOf[scala.Int | java.util.Optional[scala.Int]] + var _f: java.util.Optional[scala.Int] | scala.Int = java.util.Optional.empty[java.lang.Object]().asInstanceOf[java.util.Optional[scala.Int] | scala.Int] + var _g: java.util.Optional[java.util.Optional[scala.Int]] = java.util.Optional.empty[java.lang.Object]().asInstanceOf[java.util.Optional[java.util.Optional[scala.Int]]] + var _h: java.util.Optional[co.blocke.scalajack.json.misc.Person] = java.util.Optional.empty[java.lang.Object]().asInstanceOf[java.util.Optional[co.blocke.scalajack.json.misc.Person]] + var _i: scala.util.Either[scala.Int, java.util.Optional[scala.Int]] = scala.Predef.augmentString("r").foldRight[scala.Any]((java.util.Optional.empty[java.lang.Object](): scala.Any))(((c: scala.Char, acc: scala.Any) => if (c.==('r')) scala.Right.apply[scala.Nothing, scala.Any](acc) else scala.Left.apply[scala.Any, scala.Nothing](acc))).asInstanceOf[scala.util.Either[scala.Int, java.util.Optional[scala.Int]]] + var _j: scala.util.Either[java.util.Optional[scala.Int], scala.Int] = scala.Predef.augmentString("l").foldRight[scala.Any]((java.util.Optional.empty[java.lang.Object](): scala.Any))(((`c₂`: scala.Char, `acc₂`: scala.Any) => if (`c₂`.==('r')) scala.Right.apply[scala.Nothing, scala.Any](`acc₂`) else scala.Left.apply[scala.Any, scala.Nothing](`acc₂`))).asInstanceOf[scala.util.Either[java.util.Optional[scala.Int], scala.Int]] + var `required₂`: scala.Int = 1023 + var `maybeFieldNum₂`: scala.Option[scala.Int] = `in₇`.expectFirstObjectField(__co_blocke_scalajack_json_misc_OptionalHolder_fields) + if (`maybeFieldNum₂`.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.OptionalHolder[scala.Int]] else { + while (`maybeFieldNum₂`.isDefined) { + `maybeFieldNum₂`.get match { + case 0 => + if (`required₂`.&(1).!=(0)) { + `required₂` = `required₂`.^(1) + _a = if (`in₇`.expectNull()) java.util.Optional.empty[scala.Int]() else java.util.Optional.of[scala.Int](`in₇`.expectInt()) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₇`) + case 1 => + if (`required₂`.&(2).!=(0)) { + `required₂` = `required₂`.^(2) + _b = if (`in₇`.expectNull()) null else { + `in₇`.expectToken('[') + val tv: scala.Tuple2[java.util.Optional[scala.Int], scala.Predef.String] = new scala.Tuple2[java.util.Optional[scala.Int], scala.Predef.String][java.util.Optional[scala.Int], java.lang.String](if (`in₇`.expectNull()) java.util.Optional.empty[scala.Int]() else java.util.Optional.of[scala.Int](`in₇`.expectInt()), { + `in₇`.expectToken(',') + `in₇`.expectString() + }) + `in₇`.expectToken(']') + + (tv: scala.Tuple2[java.util.Optional[scala.Int], scala.Predef.String]) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b"), `in₇`) + case 2 => + if (`required₂`.&(4).!=(0)) { + `required₂` = `required₂`.^(4) + _c = { + val parsedArray: scala.collection.mutable.ListBuffer[java.util.Optional[scala.Int]] = `in₇`.expectArray[java.util.Optional[scala.Int]]((() => if (`in₇`.expectNull()) java.util.Optional.empty[scala.Int]() else java.util.Optional.of[scala.Int](`in₇`.expectInt()))) + + (if (parsedArray.!=(null)) parsedArray.toList else null: scala.collection.immutable.List[java.util.Optional[scala.Int]]) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c"), `in₇`) + case 3 => + if (`required₂`.&(8).!=(0)) { + `required₂` = `required₂`.^(8) + _d = if (`in₇`.expectNull()) null else { + `in₇`.expectToken('{') + `in₇`.parseMap[scala.Int, java.util.Optional[scala.Int]]((() => scala.Predef.augmentString(`in₇`.expectString()).toInt), (() => if (`in₇`.expectNull()) java.util.Optional.empty[scala.Int]() else java.util.Optional.of[scala.Int](`in₇`.expectInt())), scala.Predef.Map.empty[scala.Int, java.util.Optional[scala.Int]], true) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d"), `in₇`) + case 4 => + if (`required₂`.&(16).!=(0)) { + `required₂` = `required₂`.^(16) + _e = { + val mark: scala.Int = `in₇`.pos + scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { + case scala.util.Success(lval) => + (lval: scala.Int) + case scala.util.Failure(f) => + `in₇`.revertToPos(mark) + scala.util.Try.apply[java.util.Optional[scala.Int]](if (`in₇`.expectNull()) java.util.Optional.empty[scala.Int]() else java.util.Optional.of[scala.Int](`in₇`.expectInt())) match { + case scala.util.Success(rval) => + (rval: java.util.Optional[scala.Int]) + case scala.util.Failure(_) => + `in₇`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₇`) + } + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("e"), `in₇`) + case 5 => + if (`required₂`.&(32).!=(0)) { + `required₂` = `required₂`.^(32) + _f = { + val `mark₂`: scala.Int = `in₇`.pos + scala.util.Try.apply[java.util.Optional[scala.Int]](if (`in₇`.expectNull()) java.util.Optional.empty[scala.Int]() else java.util.Optional.of[scala.Int](`in₇`.expectInt())) match { + case scala.util.Success(lval) => + (`lval₂`: java.util.Optional[scala.Int]) + case scala.util.Failure(f) => + `in₇`.revertToPos(`mark₂`) + scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { + case scala.util.Success(rval) => + (`rval₂`: scala.Int) + case scala.util.Failure(_) => + `in₇`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₇`) + } + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f"), `in₇`) + case 6 => + if (`required₂`.&(64).!=(0)) { + `required₂` = `required₂`.^(64) + _g = if (`in₇`.expectNull()) java.util.Optional.empty[java.util.Optional[scala.Int]]() else java.util.Optional.of[java.util.Optional[scala.Int]](if (`in₇`.expectNull()) java.util.Optional.empty[scala.Int]() else java.util.Optional.of[scala.Int](`in₇`.expectInt())) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("g"), `in₇`) + case 7 => + if (`required₂`.&(128).!=(0)) { + `required₂` = `required₂`.^(128) + _h = if (`in₇`.expectNull()) java.util.Optional.empty[co.blocke.scalajack.json.misc.Person]() else java.util.Optional.of[co.blocke.scalajack.json.misc.Person](r1(`in₇`)) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("h"), `in₇`) + case 8 => + if (`required₂`.&(256).!=(0)) { + `required₂` = `required₂`.^(256) + _i = { + val `mark₃`: scala.Int = `in₇`.pos + if (`in₇`.expectNull()) null else scala.util.Try.apply[java.util.Optional[scala.Int]](if (`in₇`.expectNull()) java.util.Optional.empty[scala.Int]() else java.util.Optional.of[scala.Int](`in₇`.expectInt())) match { + case scala.util.Success(rval) => + scala.Right.apply[scala.Nothing, java.util.Optional[scala.Int]](`rval₃`) + case scala.util.Failure(f) => + `in₇`.revertToPos(`mark₃`) + scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { + case scala.util.Success(lval) => + scala.Left.apply[scala.Int, scala.Nothing](`lval₃`) + case scala.util.Failure(_) => + `in₇`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₇`) + } + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i"), `in₇`) + case 9 => + if (`required₂`.&(512).!=(0)) { + `required₂` = `required₂`.^(512) + _j = { + val `mark₄`: scala.Int = `in₇`.pos + if (`in₇`.expectNull()) null else scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { + case scala.util.Success(rval) => + scala.Right.apply[scala.Nothing, scala.Int](`rval₄`) + case scala.util.Failure(f) => + `in₇`.revertToPos(`mark₄`) + scala.util.Try.apply[java.util.Optional[scala.Int]](if (`in₇`.expectNull()) java.util.Optional.empty[scala.Int]() else java.util.Optional.of[scala.Int](`in₇`.expectInt())) match { + case scala.util.Success(lval) => + scala.Left.apply[java.util.Optional[scala.Int], scala.Nothing](`lval₄`) + case scala.util.Failure(_) => + `in₇`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₇`) + } + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("j"), `in₇`) + case _ => + `in₇`.skipValue() + } + `maybeFieldNum₂` = `in₇`.expectObjectField(__co_blocke_scalajack_json_misc_OptionalHolder_fields) + } + if (`required₂`.&(14).==(0)) new co.blocke.scalajack.json.misc.OptionalHolder[scala.Int][scala.Int](_a, _b, _c, _d, _e, _f, _g, _h, _i, _j) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a", "b", "c", "d", "e", "f", "g", "h", "i", "j")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(`required₂`.&(14)))), `in₇`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.OptionalHolder[scala.Int]] { + def encodeValue(`in₈`: co.blocke.scalajack.json.misc.OptionalHolder[scala.Int], `out₆`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₈`, `out₆`) + def decodeValue(`in₉`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.OptionalHolder[scala.Int] = r0(`in₉`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.OptionalHolder[scala.Int]]) +} +Codec: { + val __co_blocke_scalajack_json_misc_EitherRecipeJ_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + if (in.a.==(null)) out.burpNull() else () + in.a match { + case scala.Left(v) => + { + out.label("a") + out.value(v.asInstanceOf[scala.Boolean]) + } + case scala.Right(v) => + { + if (`v₂`.asInstanceOf[scala.util.Either[java.util.Optional[scala.Int], scala.Predef.String]].==(null)) out.burpNull() else () + `v₂`.asInstanceOf[scala.util.Either[java.util.Optional[scala.Int], scala.Predef.String]] match { + case scala.Left(v) => + if (`v₃`.asInstanceOf[java.util.Optional[scala.Int]].isEmpty().unary_!) { + out.label("a") + out.value(`v₃`.asInstanceOf[java.util.Optional[scala.Int]].get()) + } else () + case scala.Right(v) => + { + out.label("a") + out.valueEscaped(`v₄`.asInstanceOf[java.lang.String]) + } + } + } + } + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int] = { + var _a: scala.util.Either[scala.Boolean, scala.util.Either[java.util.Optional[scala.Int], scala.Predef.String]] = scala.Predef.augmentString("rl").foldRight[scala.Any]((java.util.Optional.empty[java.lang.Object](): scala.Any))(((c: scala.Char, acc: scala.Any) => if (c.==('r')) scala.Right.apply[scala.Nothing, scala.Any](acc) else scala.Left.apply[scala.Any, scala.Nothing](acc))).asInstanceOf[scala.util.Either[scala.Boolean, scala.util.Either[java.util.Optional[scala.Int], scala.Predef.String]]] + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_EitherRecipeJ_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = { + val mark: scala.Int = `in₂`.pos + if (`in₂`.expectNull()) null else scala.util.Try.apply[scala.util.Either[java.util.Optional[scala.Int], scala.Predef.String]]({ + val `mark₂`: scala.Int = `in₂`.pos + if (`in₂`.expectNull()) null else scala.util.Try.apply[java.lang.String](`in₂`.expectString()) match { + case scala.util.Success(rval) => + scala.Right.apply[scala.Nothing, java.lang.String](rval) + case scala.util.Failure(f) => + `in₂`.revertToPos(`mark₂`) + scala.util.Try.apply[java.util.Optional[scala.Int]](if (`in₂`.expectNull()) null else java.util.Optional.of[scala.Int](`in₂`.expectInt())) match { + case scala.util.Success(lval) => + scala.Left.apply[java.util.Optional[scala.Int], scala.Nothing](lval) + case scala.util.Failure(_) => + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) + } + } + }) match { + case scala.util.Success(rval) => + scala.Right.apply[scala.Nothing, scala.util.Either[java.util.Optional[scala.Int], scala.Predef.String]](`rval₂`) + case scala.util.Failure(f) => + `in₂`.revertToPos(mark) + scala.util.Try.apply[scala.Boolean](`in₂`.expectBoolean()) match { + case scala.util.Success(lval) => + scala.Left.apply[scala.Boolean, scala.Nothing](`lval₂`) + case scala.util.Failure(_) => + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) + } + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_EitherRecipeJ_fields) + } + if (required.&(0).==(0)) new co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int][scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(0)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int]] { + def encodeValue(`in₃`: co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int] = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int]]) +} +Codec: { + val __co_blocke_scalajack_json_misc_EitherRecipeJ_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + if (in.a.==(null)) out.burpNull() else () + in.a match { + case scala.Left(v) => + { + out.label("a") + out.value(v.asInstanceOf[scala.Boolean]) + } + case scala.Right(v) => + { + if (`v₂`.asInstanceOf[scala.util.Either[java.util.Optional[scala.Int], scala.Predef.String]].==(null)) out.burpNull() else () + `v₂`.asInstanceOf[scala.util.Either[java.util.Optional[scala.Int], scala.Predef.String]] match { + case scala.Left(v) => + if (`v₃`.asInstanceOf[java.util.Optional[scala.Int]].isEmpty().unary_!) { + out.label("a") + out.value(`v₃`.asInstanceOf[java.util.Optional[scala.Int]].get()) + } else () + case scala.Right(v) => + { + out.label("a") + out.valueEscaped(`v₄`.asInstanceOf[java.lang.String]) + } + } + } + } + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int] = { + var _a: scala.util.Either[scala.Boolean, scala.util.Either[java.util.Optional[scala.Int], scala.Predef.String]] = scala.Predef.augmentString("rl").foldRight[scala.Any]((java.util.Optional.empty[java.lang.Object](): scala.Any))(((c: scala.Char, acc: scala.Any) => if (c.==('r')) scala.Right.apply[scala.Nothing, scala.Any](acc) else scala.Left.apply[scala.Any, scala.Nothing](acc))).asInstanceOf[scala.util.Either[scala.Boolean, scala.util.Either[java.util.Optional[scala.Int], scala.Predef.String]]] + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_EitherRecipeJ_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = { + val mark: scala.Int = `in₂`.pos + if (`in₂`.expectNull()) null else scala.util.Try.apply[scala.util.Either[java.util.Optional[scala.Int], scala.Predef.String]]({ + val `mark₂`: scala.Int = `in₂`.pos + if (`in₂`.expectNull()) null else scala.util.Try.apply[java.lang.String](`in₂`.expectString()) match { + case scala.util.Success(rval) => + scala.Right.apply[scala.Nothing, java.lang.String](rval) + case scala.util.Failure(f) => + `in₂`.revertToPos(`mark₂`) + scala.util.Try.apply[java.util.Optional[scala.Int]](if (`in₂`.expectNull()) null else java.util.Optional.of[scala.Int](`in₂`.expectInt())) match { + case scala.util.Success(lval) => + scala.Left.apply[java.util.Optional[scala.Int], scala.Nothing](lval) + case scala.util.Failure(_) => + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) + } + } + }) match { + case scala.util.Success(rval) => + scala.Right.apply[scala.Nothing, scala.util.Either[java.util.Optional[scala.Int], scala.Predef.String]](`rval₂`) + case scala.util.Failure(f) => + `in₂`.revertToPos(mark) + scala.util.Try.apply[scala.Boolean](`in₂`.expectBoolean()) match { + case scala.util.Success(lval) => + scala.Left.apply[scala.Boolean, scala.Nothing](`lval₂`) + case scala.util.Failure(_) => + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) + } + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_EitherRecipeJ_fields) + } + if (required.&(0).==(0)) new co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int][scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(0)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int]] { + def encodeValue(`in₃`: co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int] = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int]]) +} +Codec: { + val __co_blocke_scalajack_json_misc_EitherRecipeJ_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + if (in.a.==(null)) out.burpNull() else () + in.a match { + case scala.Left(v) => + { + out.label("a") + out.value(v.asInstanceOf[scala.Boolean]) + } + case scala.Right(v) => + { + if (`v₂`.asInstanceOf[scala.util.Either[java.util.Optional[scala.Int], scala.Predef.String]].==(null)) out.burpNull() else () + `v₂`.asInstanceOf[scala.util.Either[java.util.Optional[scala.Int], scala.Predef.String]] match { + case scala.Left(v) => + { + out.label("a") + `v₃`.asInstanceOf[java.util.Optional[scala.Int]].asInstanceOf[java.util.Optional[scala.Int]] match { + case null => + out.burpNull() + case o if o.isEmpty() => + out.burpNull() + case o => + val vv: scala.Int = `o₂`.get().asInstanceOf[scala.Int] + out.value(vv) + } + } + case scala.Right(v) => + { + out.label("a") + out.valueEscaped(`v₄`.asInstanceOf[java.lang.String]) + } + } + } + } + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int] = { + var _a: scala.util.Either[scala.Boolean, scala.util.Either[java.util.Optional[scala.Int], scala.Predef.String]] = scala.Predef.augmentString("rl").foldRight[scala.Any]((java.util.Optional.empty[java.lang.Object](): scala.Any))(((c: scala.Char, acc: scala.Any) => if (c.==('r')) scala.Right.apply[scala.Nothing, scala.Any](acc) else scala.Left.apply[scala.Any, scala.Nothing](acc))).asInstanceOf[scala.util.Either[scala.Boolean, scala.util.Either[java.util.Optional[scala.Int], scala.Predef.String]]] + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_EitherRecipeJ_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = { + val mark: scala.Int = `in₂`.pos + if (`in₂`.expectNull()) null else scala.util.Try.apply[scala.util.Either[java.util.Optional[scala.Int], scala.Predef.String]]({ + val `mark₂`: scala.Int = `in₂`.pos + if (`in₂`.expectNull()) null else scala.util.Try.apply[java.lang.String](`in₂`.expectString()) match { + case scala.util.Success(rval) => + scala.Right.apply[scala.Nothing, java.lang.String](rval) + case scala.util.Failure(f) => + `in₂`.revertToPos(`mark₂`) + scala.util.Try.apply[java.util.Optional[scala.Int]](if (`in₂`.expectNull()) java.util.Optional.empty[scala.Int]() else java.util.Optional.of[scala.Int](`in₂`.expectInt())) match { + case scala.util.Success(lval) => + scala.Left.apply[java.util.Optional[scala.Int], scala.Nothing](lval) + case scala.util.Failure(_) => + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) + } + } + }) match { + case scala.util.Success(rval) => + scala.Right.apply[scala.Nothing, scala.util.Either[java.util.Optional[scala.Int], scala.Predef.String]](`rval₂`) + case scala.util.Failure(f) => + `in₂`.revertToPos(mark) + scala.util.Try.apply[scala.Boolean](`in₂`.expectBoolean()) match { + case scala.util.Success(lval) => + scala.Left.apply[scala.Boolean, scala.Nothing](`lval₂`) + case scala.util.Failure(_) => + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) + } + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_EitherRecipeJ_fields) + } + if (required.&(0).==(0)) new co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int][scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(0)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int]] { + def encodeValue(`in₃`: co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int] = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int]]) +} +Codec: { + val __co_blocke_scalajack_json_misc_TryHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.misc.TryHolder[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + in.a match { + case scala.util.Failure(v) => + { + out.label("a") + out.burpNull() + } + case _ => + { + out.label("a") + out.value(in.a.get) + } + } + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.TryHolder[scala.Int] = { + var _a: scala.util.Try[scala.Int] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_TryHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.TryHolder[scala.Int]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = { + val mark: scala.Int = `in₂`.pos + try if (`in₂`.expectNull()) null else scala.util.Success.apply[scala.Int](`in₂`.expectInt()) catch { + case t: java.lang.Throwable => + `in₂`.revertToPos(mark) + throw co.blocke.scalajack.json.JsonParseError.apply("Unsuccessful attempt to read Try type with failure: ".+(t.getMessage()), `in₂`) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_TryHolder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.misc.TryHolder[scala.Int][scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.TryHolder[scala.Int]] { + def encodeValue(`in₃`: co.blocke.scalajack.json.misc.TryHolder[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.TryHolder[scala.Int] = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.TryHolder[scala.Int]]) +} +Codec: { + val __co_blocke_scalajack_json_misc_TryHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.misc.TryHolder[scala.Option[scala.Int]], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + in.a match { + case scala.util.Failure(v) => + { + out.label("a") + out.burpNull() + } + case _ => + in.a.get match { + case scala.None => + () + case scala.Some(v) => + { + out.label("a") + out.value(v) + } + } + } + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.TryHolder[scala.Option[scala.Int]] = { + var _a: scala.util.Try[scala.Option[scala.Int]] = scala.util.Success.apply[scala.None.type](scala.None) + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_TryHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.TryHolder[scala.Option[scala.Int]]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = { + val mark: scala.Int = `in₂`.pos + try if (`in₂`.expectNull()) null else scala.util.Success.apply[scala.Option[scala.Int]](if (`in₂`.expectNull()) null else scala.Some.apply[scala.Int](`in₂`.expectInt())) catch { + case t: java.lang.Throwable => + `in₂`.revertToPos(mark) + throw co.blocke.scalajack.json.JsonParseError.apply("Unsuccessful attempt to read Try type with failure: ".+(t.getMessage()), `in₂`) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_TryHolder_fields) + } + if (required.&(0).==(0)) new co.blocke.scalajack.json.misc.TryHolder[scala.Option[scala.Int]][scala.Option[scala.Int]](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(0)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.TryHolder[scala.Option[scala.Int]]] { + def encodeValue(`in₃`: co.blocke.scalajack.json.misc.TryHolder[scala.Option[scala.Int]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.TryHolder[scala.Option[scala.Int]] = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.TryHolder[scala.Option[scala.Int]]]) +} +Codec: { + val __co_blocke_scalajack_json_misc_TryHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.misc.TryHolder[scala.Option[scala.Int]], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + in.a match { + case scala.util.Failure(v) => + { + out.label("a") + out.burpNull() + } + case _ => + in.a.get match { + case scala.None => + () + case scala.Some(v) => + { + out.label("a") + out.value(v) + } + } + } + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.TryHolder[scala.Option[scala.Int]] = { + var _a: scala.util.Try[scala.Option[scala.Int]] = scala.util.Success.apply[scala.None.type](scala.None) + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_TryHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.TryHolder[scala.Option[scala.Int]]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = { + val mark: scala.Int = `in₂`.pos + try if (`in₂`.expectNull()) null else scala.util.Success.apply[scala.Option[scala.Int]](if (`in₂`.expectNull()) null else scala.Some.apply[scala.Int](`in₂`.expectInt())) catch { + case t: java.lang.Throwable => + `in₂`.revertToPos(mark) + throw co.blocke.scalajack.json.JsonParseError.apply("Unsuccessful attempt to read Try type with failure: ".+(t.getMessage()), `in₂`) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_TryHolder_fields) + } + if (required.&(0).==(0)) new co.blocke.scalajack.json.misc.TryHolder[scala.Option[scala.Int]][scala.Option[scala.Int]](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(0)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.TryHolder[scala.Option[scala.Int]]] { + def encodeValue(`in₃`: co.blocke.scalajack.json.misc.TryHolder[scala.Option[scala.Int]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.TryHolder[scala.Option[scala.Int]] = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.TryHolder[scala.Option[scala.Int]]]) +} +Codec: { + val __co_blocke_scalajack_json_misc_TryHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.misc.TryHolder[java.util.Optional[scala.Int]], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + in.a match { + case scala.util.Failure(v) => + { + out.label("a") + out.burpNull() + } + case _ => + if (in.a.get.isEmpty().unary_!) { + out.label("a") + out.value(in.a.get.get()) + } else () + } + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.TryHolder[java.util.Optional[scala.Int]] = { + var _a: scala.util.Try[java.util.Optional[scala.Int]] = scala.util.Success.apply[java.util.Optional[java.lang.Object]](java.util.Optional.empty[java.lang.Object]()).asInstanceOf[scala.util.Try[java.util.Optional[scala.Int]]] + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_TryHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.TryHolder[java.util.Optional[scala.Int]]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = { + val mark: scala.Int = `in₂`.pos + try if (`in₂`.expectNull()) null else scala.util.Success.apply[java.util.Optional[scala.Int]](if (`in₂`.expectNull()) null else java.util.Optional.of[scala.Int](`in₂`.expectInt())) catch { + case t: java.lang.Throwable => + `in₂`.revertToPos(mark) + throw co.blocke.scalajack.json.JsonParseError.apply("Unsuccessful attempt to read Try type with failure: ".+(t.getMessage()), `in₂`) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_TryHolder_fields) + } + if (required.&(0).==(0)) new co.blocke.scalajack.json.misc.TryHolder[java.util.Optional[scala.Int]][java.util.Optional[scala.Int]](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(0)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.TryHolder[java.util.Optional[scala.Int]]] { + def encodeValue(`in₃`: co.blocke.scalajack.json.misc.TryHolder[java.util.Optional[scala.Int]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.TryHolder[java.util.Optional[scala.Int]] = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.TryHolder[java.util.Optional[scala.Int]]]) +} +Codec: { + val __co_blocke_scalajack_json_misc_TryHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.misc.TryHolder[java.util.Optional[scala.Int]], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + in.a match { + case scala.util.Failure(v) => + { + out.label("a") + out.burpNull() + } + case _ => + if (in.a.get.isEmpty().unary_!) { + out.label("a") + out.value(in.a.get.get()) + } else () + } + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.TryHolder[java.util.Optional[scala.Int]] = { + var _a: scala.util.Try[java.util.Optional[scala.Int]] = scala.util.Success.apply[java.util.Optional[java.lang.Object]](java.util.Optional.empty[java.lang.Object]()).asInstanceOf[scala.util.Try[java.util.Optional[scala.Int]]] + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_TryHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.TryHolder[java.util.Optional[scala.Int]]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = { + val mark: scala.Int = `in₂`.pos + try if (`in₂`.expectNull()) null else scala.util.Success.apply[java.util.Optional[scala.Int]](if (`in₂`.expectNull()) null else java.util.Optional.of[scala.Int](`in₂`.expectInt())) catch { + case t: java.lang.Throwable => + `in₂`.revertToPos(mark) + throw co.blocke.scalajack.json.JsonParseError.apply("Unsuccessful attempt to read Try type with failure: ".+(t.getMessage()), `in₂`) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_TryHolder_fields) + } + if (required.&(0).==(0)) new co.blocke.scalajack.json.misc.TryHolder[java.util.Optional[scala.Int]][java.util.Optional[scala.Int]](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(0)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.TryHolder[java.util.Optional[scala.Int]]] { + def encodeValue(`in₃`: co.blocke.scalajack.json.misc.TryHolder[java.util.Optional[scala.Int]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.TryHolder[java.util.Optional[scala.Int]] = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.TryHolder[java.util.Optional[scala.Int]]]) +} +Codec: { + val __co_blocke_scalajack_json_misc_TryHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.misc.TryHolder[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + in.a match { + case scala.util.Failure(v) => + { + out.label("a") + out.burpNull() + } + case _ => + { + out.label("a") + out.value(in.a.get) + } + } + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.TryHolder[scala.Int] = { + var _a: scala.util.Try[scala.Int] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_TryHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.TryHolder[scala.Int]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = { + val mark: scala.Int = `in₂`.pos + try if (`in₂`.expectNull()) null else scala.util.Success.apply[scala.Int](`in₂`.expectInt()) catch { + case t: java.lang.Throwable => + `in₂`.revertToPos(mark) + throw co.blocke.scalajack.json.JsonParseError.apply("Unsuccessful attempt to read Try type with failure: ".+(t.getMessage()), `in₂`) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_TryHolder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.misc.TryHolder[scala.Int][scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.TryHolder[scala.Int]] { + def encodeValue(`in₃`: co.blocke.scalajack.json.misc.TryHolder[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.TryHolder[scala.Int] = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.TryHolder[scala.Int]]) +} +Codec: { + val __co_blocke_scalajack_json_misc_TryHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.misc.TryHolder[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + in.a match { + case scala.util.Failure(v) => + { + out.label("a") + out.value("Try Failure with msg: ".+(v.getMessage())) + } + case _ => + { + out.label("a") + out.value(in.a.get) + } + } + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.TryHolder[scala.Int] = { + var _a: scala.util.Try[scala.Int] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_TryHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.TryHolder[scala.Int]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = { + val mark: scala.Int = `in₂`.pos + try if (`in₂`.expectNull()) null else scala.util.Success.apply[scala.Int](`in₂`.expectInt()) catch { + case t: java.lang.Throwable => + `in₂`.revertToPos(mark) + throw co.blocke.scalajack.json.JsonParseError.apply("Unsuccessful attempt to read Try type with failure: ".+(t.getMessage()), `in₂`) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_TryHolder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.misc.TryHolder[scala.Int][scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.TryHolder[scala.Int]] { + def encodeValue(`in₃`: co.blocke.scalajack.json.misc.TryHolder[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.TryHolder[scala.Int] = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.TryHolder[scala.Int]]) +} +Codec: { + val __co_blocke_scalajack_json_misc_TryHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.misc.TryHolder[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + in.a match { + case scala.util.Failure(v) => + throw v + case _ => + { + out.label("a") + out.value(in.a.get) + } + } + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.TryHolder[scala.Int] = { + var _a: scala.util.Try[scala.Int] = null + var required: scala.Int = 1 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_TryHolder_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.TryHolder[scala.Int]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = { + val mark: scala.Int = `in₂`.pos + try if (`in₂`.expectNull()) null else scala.util.Success.apply[scala.Int](`in₂`.expectInt()) catch { + case t: java.lang.Throwable => + `in₂`.revertToPos(mark) + throw co.blocke.scalajack.json.JsonParseError.apply("Unsuccessful attempt to read Try type with failure: ".+(t.getMessage()), `in₂`) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_TryHolder_fields) + } + if (required.&(1).==(0)) new co.blocke.scalajack.json.misc.TryHolder[scala.Int][scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.TryHolder[scala.Int]] { + def encodeValue(`in₃`: co.blocke.scalajack.json.misc.TryHolder[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.TryHolder[scala.Int] = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.TryHolder[scala.Int]]) +} +Codec: { + val __co_blocke_scalajack_json_misc_TryHolder2_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a", "b")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w1(in: scala.collection.immutable.Seq[scala.util.Try[scala.Int]], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startArray() + in.foreach[scala.Unit](((i: scala.util.Try[scala.Int]) => if (i.==(null)) out.burpNull() else i match { + case scala.util.Success(v) => + out.value(v) + case scala.util.Failure(v) => + out.burpNull() + })) + out.endArray() + } + def w2(`in₂`: scala.Tuple2[scala.util.Try[scala.Int], scala.util.Try[scala.Int]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { + `out₂`.startArray() + if (`in₂`._1.==(null)) `out₂`.burpNull() else `in₂`._1 match { + case scala.util.Success(v) => + `out₂`.value(`v₂`) + case scala.util.Failure(v) => + `out₂`.burpNull() + } + if (`in₂`._2.==(null)) `out₂`.burpNull() else `in₂`._2 match { + case scala.util.Success(v) => + `out₂`.value(`v₃`) + case scala.util.Failure(v) => + `out₂`.burpNull() + } + `out₂`.endArray() + } + def w0(`in₃`: co.blocke.scalajack.json.misc.TryHolder2[scala.Int], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { + `out₃`.startObject() + `out₃`.label("a") + w1(`in₃`.a, `out₃`) + `out₃`.label("b") + w2(`in₃`.b, `out₃`) + `out₃`.endObject() + } + def r0(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.TryHolder2[scala.Int] = { + var _a: scala.collection.immutable.Seq[scala.util.Try[scala.Int]] = null + var _b: scala.Tuple2[scala.util.Try[scala.Int], scala.util.Try[scala.Int]] = null + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₄`.expectFirstObjectField(__co_blocke_scalajack_json_misc_TryHolder2_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.TryHolder2[scala.Int]] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _a = { + val parsedArray: scala.collection.mutable.ListBuffer[scala.util.Try[scala.Int]] = `in₄`.expectArray[scala.util.Try[scala.Int]]((() => { + val mark: scala.Int = `in₄`.pos + try if (`in₄`.expectNull()) null else scala.util.Success.apply[scala.Int](`in₄`.expectInt()) catch { + case t: java.lang.Throwable => + `in₄`.revertToPos(mark) + throw co.blocke.scalajack.json.JsonParseError.apply("Unsuccessful attempt to read Try type with failure: ".+(t.getMessage()), `in₄`) + } + })) + + (if (parsedArray.!=(null)) parsedArray.toSeq else null: scala.collection.immutable.Seq[scala.util.Try[scala.Int]]) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₄`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _b = if (`in₄`.expectNull()) null else { + `in₄`.expectToken('[') + val tv: scala.Tuple2[scala.util.Try[scala.Int], scala.util.Try[scala.Int]] = new scala.Tuple2[scala.util.Try[scala.Int], scala.util.Try[scala.Int]][scala.util.Try[scala.Int], scala.util.Try[scala.Int]]({ + val `mark₂`: scala.Int = `in₄`.pos + try if (`in₄`.expectNull()) null else scala.util.Success.apply[scala.Int](`in₄`.expectInt()) catch { + case t: java.lang.Throwable => + `in₄`.revertToPos(`mark₂`) + throw co.blocke.scalajack.json.JsonParseError.apply("Unsuccessful attempt to read Try type with failure: ".+(`t₂`.getMessage()), `in₄`) + } + }, { + `in₄`.expectToken(',') + val `mark₃`: scala.Int = `in₄`.pos + try if (`in₄`.expectNull()) null else scala.util.Success.apply[scala.Int](`in₄`.expectInt()) catch { + case t: java.lang.Throwable => + `in₄`.revertToPos(`mark₃`) + throw co.blocke.scalajack.json.JsonParseError.apply("Unsuccessful attempt to read Try type with failure: ".+(`t₃`.getMessage()), `in₄`) + } + }) + `in₄`.expectToken(']') + + (tv: scala.Tuple2[scala.util.Try[scala.Int], scala.util.Try[scala.Int]]) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b"), `in₄`) + case _ => + `in₄`.skipValue() + } + maybeFieldNum = `in₄`.expectObjectField(__co_blocke_scalajack_json_misc_TryHolder2_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.misc.TryHolder2[scala.Int][scala.Int](_a, _b) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a", "b")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₄`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.TryHolder2[scala.Int]] { + def encodeValue(`in₅`: co.blocke.scalajack.json.misc.TryHolder2[scala.Int], `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₅`, `out₄`) + def decodeValue(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.TryHolder2[scala.Int] = r0(`in₆`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.TryHolder2[scala.Int]]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleJBigDecimal_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("bd1", "bd2", "bd3", "bd4", "bd5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleJBigDecimal, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("bd1") + out.value(in.bd1) + out.label("bd2") + out.value(in.bd2) + out.label("bd3") + out.value(in.bd3) + out.label("bd4") + out.value(in.bd4) + out.label("bd5") + out.value(in.bd5) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJBigDecimal = { + var _bd1: java.math.BigDecimal = new java.math.BigDecimal(0.0) + var _bd2: java.math.BigDecimal = new java.math.BigDecimal(0.0) + var _bd3: java.math.BigDecimal = new java.math.BigDecimal(0.0) + var _bd4: java.math.BigDecimal = new java.math.BigDecimal(0.0) + var _bd5: java.math.BigDecimal = new java.math.BigDecimal(0.0) + var required: scala.Int = 31 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJBigDecimal_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJBigDecimal] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _bd1 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + new java.math.BigDecimal(n) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _bd2 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + new java.math.BigDecimal(`n₂`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _bd3 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + new java.math.BigDecimal(`n₃`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _bd4 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + new java.math.BigDecimal(`n₄`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _bd5 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + new java.math.BigDecimal(`n₅`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd5"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJBigDecimal_fields) + } + if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJBigDecimal(_bd1, _bd2, _bd3, _bd4, _bd5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("bd1", "bd2", "bd3", "bd4", "bd5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJBigDecimal] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJBigDecimal, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJBigDecimal = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJBigDecimal]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleJBigDecimal_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("bd1", "bd2", "bd3", "bd4", "bd5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleJBigDecimal, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("bd1") + out.value(in.bd1) + out.label("bd2") + out.value(in.bd2) + out.label("bd3") + out.value(in.bd3) + out.label("bd4") + out.value(in.bd4) + out.label("bd5") + out.value(in.bd5) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJBigDecimal = { + var _bd1: java.math.BigDecimal = new java.math.BigDecimal(0.0) + var _bd2: java.math.BigDecimal = new java.math.BigDecimal(0.0) + var _bd3: java.math.BigDecimal = new java.math.BigDecimal(0.0) + var _bd4: java.math.BigDecimal = new java.math.BigDecimal(0.0) + var _bd5: java.math.BigDecimal = new java.math.BigDecimal(0.0) + var required: scala.Int = 31 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJBigDecimal_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJBigDecimal] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _bd1 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + new java.math.BigDecimal(n) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _bd2 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + new java.math.BigDecimal(`n₂`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _bd3 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + new java.math.BigDecimal(`n₃`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _bd4 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + new java.math.BigDecimal(`n₄`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _bd5 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + new java.math.BigDecimal(`n₅`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd5"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJBigDecimal_fields) + } + if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJBigDecimal(_bd1, _bd2, _bd3, _bd4, _bd5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("bd1", "bd2", "bd3", "bd4", "bd5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJBigDecimal] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJBigDecimal, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJBigDecimal = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJBigDecimal]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleJBigInteger_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("bi1", "bi2", "bi3", "bi4", "bi5", "bi6", "bi7")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleJBigInteger, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("bi1") + out.value(in.bi1) + out.label("bi2") + out.value(in.bi2) + out.label("bi3") + out.value(in.bi3) + out.label("bi4") + out.value(in.bi4) + out.label("bi5") + out.value(in.bi5) + out.label("bi6") + out.value(in.bi6) + out.label("bi7") + out.value(in.bi7) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJBigInteger = { + var _bi1: java.math.BigInteger = java.math.BigInteger.valueOf(0L) + var _bi2: java.math.BigInteger = java.math.BigInteger.valueOf(0L) + var _bi3: java.math.BigInteger = java.math.BigInteger.valueOf(0L) + var _bi4: java.math.BigInteger = java.math.BigInteger.valueOf(0L) + var _bi5: java.math.BigInteger = java.math.BigInteger.valueOf(0L) + var _bi6: java.math.BigInteger = java.math.BigInteger.valueOf(0L) + var _bi7: java.math.BigInteger = java.math.BigInteger.valueOf(0L) + var required: scala.Int = 127 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJBigInteger_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJBigInteger] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _bi1 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + new java.math.BigInteger(n) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _bi2 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + new java.math.BigInteger(`n₂`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _bi3 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + new java.math.BigInteger(`n₃`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _bi4 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + new java.math.BigInteger(`n₄`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _bi5 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + new java.math.BigInteger(`n₅`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi5"), `in₂`) + case 5 => + if (required.&(32).!=(0)) { + required = required.^(32) + _bi6 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + new java.math.BigInteger(`n₆`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi6"), `in₂`) + case 6 => + if (required.&(64).!=(0)) { + required = required.^(64) + _bi7 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + new java.math.BigInteger(`n₇`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi7"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJBigInteger_fields) + } + if (required.&(127).==(0)) new co.blocke.scalajack.json.primitives.SampleJBigInteger(_bi1, _bi2, _bi3, _bi4, _bi5, _bi6, _bi7) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("bi1", "bi2", "bi3", "bi4", "bi5", "bi6", "bi7")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(127)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJBigInteger] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJBigInteger, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJBigInteger = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJBigInteger]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleJBigInteger_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("bi1", "bi2", "bi3", "bi4", "bi5", "bi6", "bi7")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleJBigInteger, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("bi1") + out.value(in.bi1) + out.label("bi2") + out.value(in.bi2) + out.label("bi3") + out.value(in.bi3) + out.label("bi4") + out.value(in.bi4) + out.label("bi5") + out.value(in.bi5) + out.label("bi6") + out.value(in.bi6) + out.label("bi7") + out.value(in.bi7) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJBigInteger = { + var _bi1: java.math.BigInteger = java.math.BigInteger.valueOf(0L) + var _bi2: java.math.BigInteger = java.math.BigInteger.valueOf(0L) + var _bi3: java.math.BigInteger = java.math.BigInteger.valueOf(0L) + var _bi4: java.math.BigInteger = java.math.BigInteger.valueOf(0L) + var _bi5: java.math.BigInteger = java.math.BigInteger.valueOf(0L) + var _bi6: java.math.BigInteger = java.math.BigInteger.valueOf(0L) + var _bi7: java.math.BigInteger = java.math.BigInteger.valueOf(0L) + var required: scala.Int = 127 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJBigInteger_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJBigInteger] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _bi1 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + new java.math.BigInteger(n) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _bi2 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + new java.math.BigInteger(`n₂`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _bi3 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + new java.math.BigInteger(`n₃`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _bi4 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + new java.math.BigInteger(`n₄`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _bi5 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + new java.math.BigInteger(`n₅`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi5"), `in₂`) + case 5 => + if (required.&(32).!=(0)) { + required = required.^(32) + _bi6 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + new java.math.BigInteger(`n₆`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi6"), `in₂`) + case 6 => + if (required.&(64).!=(0)) { + required = required.^(64) + _bi7 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + new java.math.BigInteger(`n₇`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi7"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJBigInteger_fields) + } + if (required.&(127).==(0)) new co.blocke.scalajack.json.primitives.SampleJBigInteger(_bi1, _bi2, _bi3, _bi4, _bi5, _bi6, _bi7) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("bi1", "bi2", "bi3", "bi4", "bi5", "bi6", "bi7")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(127)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJBigInteger] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJBigInteger, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJBigInteger = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJBigInteger]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleJBoolean_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("bool1", "bool2", "bool3", "bool4", "bool5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleJBoolean, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("bool1") + out.value(in.bool1) + out.label("bool2") + out.value(in.bool2) + out.label("bool3") + out.value(in.bool3) + out.label("bool4") + out.value(in.bool4) + out.label("bool5") + out.value(in.bool5) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJBoolean = { + var _bool1: java.lang.Boolean = java.lang.Boolean.FALSE + var _bool2: java.lang.Boolean = java.lang.Boolean.FALSE + var _bool3: java.lang.Boolean = java.lang.Boolean.FALSE + var _bool4: java.lang.Boolean = java.lang.Boolean.FALSE + var _bool5: java.lang.Boolean = java.lang.Boolean.FALSE + var required: scala.Int = 31 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJBoolean_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJBoolean] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _bool1 = `in₂`.expectJavaBoolean() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bool1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _bool2 = `in₂`.expectJavaBoolean() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bool2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _bool3 = `in₂`.expectJavaBoolean() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bool3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _bool4 = `in₂`.expectJavaBoolean() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bool4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _bool5 = `in₂`.expectJavaBoolean() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bool5"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJBoolean_fields) + } + if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJBoolean(_bool1, _bool2, _bool3, _bool4, _bool5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("bool1", "bool2", "bool3", "bool4", "bool5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJBoolean] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJBoolean, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJBoolean = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJBoolean]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleJBoolean_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("bool1", "bool2", "bool3", "bool4", "bool5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleJBoolean, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("bool1") + out.value(in.bool1) + out.label("bool2") + out.value(in.bool2) + out.label("bool3") + out.value(in.bool3) + out.label("bool4") + out.value(in.bool4) + out.label("bool5") + out.value(in.bool5) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJBoolean = { + var _bool1: java.lang.Boolean = java.lang.Boolean.FALSE + var _bool2: java.lang.Boolean = java.lang.Boolean.FALSE + var _bool3: java.lang.Boolean = java.lang.Boolean.FALSE + var _bool4: java.lang.Boolean = java.lang.Boolean.FALSE + var _bool5: java.lang.Boolean = java.lang.Boolean.FALSE + var required: scala.Int = 31 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJBoolean_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJBoolean] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _bool1 = `in₂`.expectJavaBoolean() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bool1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _bool2 = `in₂`.expectJavaBoolean() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bool2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _bool3 = `in₂`.expectJavaBoolean() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bool3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _bool4 = `in₂`.expectJavaBoolean() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bool4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _bool5 = `in₂`.expectJavaBoolean() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bool5"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJBoolean_fields) + } + if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJBoolean(_bool1, _bool2, _bool3, _bool4, _bool5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("bool1", "bool2", "bool3", "bool4", "bool5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJBoolean] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJBoolean, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJBoolean = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJBoolean]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleJByte_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("b1", "b2", "b3", "b4", "b5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleJByte, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("b1") + out.value(in.b1) + out.label("b2") + out.value(in.b2) + out.label("b3") + out.value(in.b3) + out.label("b4") + out.value(in.b4) + out.label("b5") + out.value(in.b5) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJByte = { + var _b1: java.lang.Byte = scala.Predef.byte2Byte(java.lang.Byte.MIN_VALUE) + var _b2: java.lang.Byte = scala.Predef.byte2Byte(java.lang.Byte.MIN_VALUE) + var _b3: java.lang.Byte = scala.Predef.byte2Byte(java.lang.Byte.MIN_VALUE) + var _b4: java.lang.Byte = scala.Predef.byte2Byte(java.lang.Byte.MIN_VALUE) + var _b5: java.lang.Byte = scala.Predef.byte2Byte(java.lang.Byte.MIN_VALUE) + var required: scala.Int = 31 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJByte_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJByte] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _b1 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Byte.valueOf(n) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _b2 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Byte.valueOf(`n₂`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _b3 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Byte.valueOf(`n₃`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _b4 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Byte.valueOf(`n₄`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _b5 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Byte.valueOf(`n₅`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b5"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJByte_fields) + } + if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJByte(_b1, _b2, _b3, _b4, _b5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("b1", "b2", "b3", "b4", "b5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJByte] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJByte, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJByte = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJByte]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleJByte_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("b1", "b2", "b3", "b4", "b5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleJByte, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("b1") + out.value(in.b1) + out.label("b2") + out.value(in.b2) + out.label("b3") + out.value(in.b3) + out.label("b4") + out.value(in.b4) + out.label("b5") + out.value(in.b5) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJByte = { + var _b1: java.lang.Byte = scala.Predef.byte2Byte(java.lang.Byte.MIN_VALUE) + var _b2: java.lang.Byte = scala.Predef.byte2Byte(java.lang.Byte.MIN_VALUE) + var _b3: java.lang.Byte = scala.Predef.byte2Byte(java.lang.Byte.MIN_VALUE) + var _b4: java.lang.Byte = scala.Predef.byte2Byte(java.lang.Byte.MIN_VALUE) + var _b5: java.lang.Byte = scala.Predef.byte2Byte(java.lang.Byte.MIN_VALUE) + var required: scala.Int = 31 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJByte_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJByte] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _b1 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Byte.valueOf(n) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _b2 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Byte.valueOf(`n₂`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _b3 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Byte.valueOf(`n₃`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _b4 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Byte.valueOf(`n₄`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _b5 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Byte.valueOf(`n₅`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b5"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJByte_fields) + } + if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJByte(_b1, _b2, _b3, _b4, _b5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("b1", "b2", "b3", "b4", "b5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJByte] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJByte, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJByte = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJByte]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleJChar_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("c1", "c2", "c3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleJChar, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("c1") + out.value(in.c1) + out.label("c2") + out.value(in.c2) + out.label("c3") + out.value(in.c3) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJChar = { + var _c1: java.lang.Character = java.lang.Character.valueOf('x') + var _c2: java.lang.Character = java.lang.Character.valueOf('x') + var _c3: java.lang.Character = java.lang.Character.valueOf('x') + var required: scala.Int = 7 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJChar_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJChar] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _c1 = { + val c: java.lang.String = `in₂`.expectString() + if (c.==(null)) null else if (c.length().==(0)) { + `in₂`.backspace() + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Character value expected but empty string found in json", `in₂`) + } else java.lang.Character.valueOf(c.charAt(0)) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _c2 = { + val `c₂`: java.lang.String = `in₂`.expectString() + if (`c₂`.==(null)) null else if (`c₂`.length().==(0)) { + `in₂`.backspace() + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Character value expected but empty string found in json", `in₂`) + } else java.lang.Character.valueOf(`c₂`.charAt(0)) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _c3 = { + val `c₃`: java.lang.String = `in₂`.expectString() + if (`c₃`.==(null)) null else if (`c₃`.length().==(0)) { + `in₂`.backspace() + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Character value expected but empty string found in json", `in₂`) + } else java.lang.Character.valueOf(`c₃`.charAt(0)) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c3"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJChar_fields) + } + if (required.&(7).==(0)) new co.blocke.scalajack.json.primitives.SampleJChar(_c1, _c2, _c3) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("c1", "c2", "c3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(7)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJChar] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJChar, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJChar = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJChar]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleJChar_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("c1", "c2", "c3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleJChar, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("c1") + out.value(in.c1) + out.label("c2") + out.value(in.c2) + out.label("c3") + out.value(in.c3) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJChar = { + var _c1: java.lang.Character = java.lang.Character.valueOf('x') + var _c2: java.lang.Character = java.lang.Character.valueOf('x') + var _c3: java.lang.Character = java.lang.Character.valueOf('x') + var required: scala.Int = 7 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJChar_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJChar] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _c1 = { + val c: java.lang.String = `in₂`.expectString() + if (c.==(null)) null else if (c.length().==(0)) { + `in₂`.backspace() + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Character value expected but empty string found in json", `in₂`) + } else java.lang.Character.valueOf(c.charAt(0)) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _c2 = { + val `c₂`: java.lang.String = `in₂`.expectString() + if (`c₂`.==(null)) null else if (`c₂`.length().==(0)) { + `in₂`.backspace() + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Character value expected but empty string found in json", `in₂`) + } else java.lang.Character.valueOf(`c₂`.charAt(0)) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _c3 = { + val `c₃`: java.lang.String = `in₂`.expectString() + if (`c₃`.==(null)) null else if (`c₃`.length().==(0)) { + `in₂`.backspace() + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Character value expected but empty string found in json", `in₂`) + } else java.lang.Character.valueOf(`c₃`.charAt(0)) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c3"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJChar_fields) + } + if (required.&(7).==(0)) new co.blocke.scalajack.json.primitives.SampleJChar(_c1, _c2, _c3) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("c1", "c2", "c3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(7)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJChar] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJChar, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJChar = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJChar]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleJDouble_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4", "d5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleJDouble, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("d1") + out.value(in.d1) + out.label("d2") + out.value(in.d2) + out.label("d3") + out.value(in.d3) + out.label("d4") + out.value(in.d4) + out.label("d5") + out.value(in.d5) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJDouble = { + var _d1: java.lang.Double = java.lang.Double.valueOf(0.0) + var _d2: java.lang.Double = java.lang.Double.valueOf(0.0) + var _d3: java.lang.Double = java.lang.Double.valueOf(0.0) + var _d4: java.lang.Double = java.lang.Double.valueOf(0.0) + var _d5: java.lang.Double = java.lang.Double.valueOf(0.0) + var required: scala.Int = 31 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJDouble_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJDouble] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _d1 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Double.valueOf(n) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _d2 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Double.valueOf(`n₂`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _d3 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Double.valueOf(`n₃`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _d4 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Double.valueOf(`n₄`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _d5 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Double.valueOf(`n₅`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d5"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJDouble_fields) + } + if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJDouble(_d1, _d2, _d3, _d4, _d5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4", "d5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJDouble] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJDouble, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJDouble = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJDouble]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleJDouble_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4", "d5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleJDouble, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("d1") + out.value(in.d1) + out.label("d2") + out.value(in.d2) + out.label("d3") + out.value(in.d3) + out.label("d4") + out.value(in.d4) + out.label("d5") + out.value(in.d5) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJDouble = { + var _d1: java.lang.Double = java.lang.Double.valueOf(0.0) + var _d2: java.lang.Double = java.lang.Double.valueOf(0.0) + var _d3: java.lang.Double = java.lang.Double.valueOf(0.0) + var _d4: java.lang.Double = java.lang.Double.valueOf(0.0) + var _d5: java.lang.Double = java.lang.Double.valueOf(0.0) + var required: scala.Int = 31 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJDouble_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJDouble] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _d1 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Double.valueOf(n) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _d2 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Double.valueOf(`n₂`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _d3 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Double.valueOf(`n₃`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _d4 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Double.valueOf(`n₄`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _d5 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Double.valueOf(`n₅`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d5"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJDouble_fields) + } + if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJDouble(_d1, _d2, _d3, _d4, _d5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4", "d5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJDouble] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJDouble, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJDouble = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJDouble]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleJFloat_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("f1", "f2", "f3", "f4", "f5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleJFloat, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("f1") + out.value(in.f1) + out.label("f2") + out.value(in.f2) + out.label("f3") + out.value(in.f3) + out.label("f4") + out.value(in.f4) + out.label("f5") + out.value(in.f5) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJFloat = { + var _f1: java.lang.Float = java.lang.Float.valueOf(0.0f) + var _f2: java.lang.Float = java.lang.Float.valueOf(0.0f) + var _f3: java.lang.Float = java.lang.Float.valueOf(0.0f) + var _f4: java.lang.Float = java.lang.Float.valueOf(0.0f) + var _f5: java.lang.Float = java.lang.Float.valueOf(0.0f) + var required: scala.Int = 31 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJFloat_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJFloat] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _f1 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Float.valueOf(n) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _f2 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Float.valueOf(`n₂`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _f3 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Float.valueOf(`n₃`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _f4 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Float.valueOf(`n₄`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _f5 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Float.valueOf(`n₅`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f5"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJFloat_fields) + } + if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJFloat(_f1, _f2, _f3, _f4, _f5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("f1", "f2", "f3", "f4", "f5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJFloat] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJFloat, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJFloat = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJFloat]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleJFloat_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("f1", "f2", "f3", "f4", "f5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleJFloat, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("f1") + out.value(in.f1) + out.label("f2") + out.value(in.f2) + out.label("f3") + out.value(in.f3) + out.label("f4") + out.value(in.f4) + out.label("f5") + out.value(in.f5) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJFloat = { + var _f1: java.lang.Float = java.lang.Float.valueOf(0.0f) + var _f2: java.lang.Float = java.lang.Float.valueOf(0.0f) + var _f3: java.lang.Float = java.lang.Float.valueOf(0.0f) + var _f4: java.lang.Float = java.lang.Float.valueOf(0.0f) + var _f5: java.lang.Float = java.lang.Float.valueOf(0.0f) + var required: scala.Int = 31 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJFloat_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJFloat] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _f1 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Float.valueOf(n) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _f2 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Float.valueOf(`n₂`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _f3 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Float.valueOf(`n₃`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _f4 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Float.valueOf(`n₄`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _f5 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Float.valueOf(`n₅`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f5"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJFloat_fields) + } + if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJFloat(_f1, _f2, _f3, _f4, _f5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("f1", "f2", "f3", "f4", "f5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJFloat] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJFloat, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJFloat = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJFloat]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleJInt_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("i1", "i2", "i3", "i4", "i5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleJInt, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("i1") + out.value(in.i1) + out.label("i2") + out.value(in.i2) + out.label("i3") + out.value(in.i3) + out.label("i4") + out.value(in.i4) + out.label("i5") + out.value(in.i5) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJInt = { + var _i1: java.lang.Integer = java.lang.Integer.valueOf(0) + var _i2: java.lang.Integer = java.lang.Integer.valueOf(0) + var _i3: java.lang.Integer = java.lang.Integer.valueOf(0) + var _i4: java.lang.Integer = java.lang.Integer.valueOf(0) + var _i5: java.lang.Integer = java.lang.Integer.valueOf(0) + var required: scala.Int = 31 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJInt_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJInt] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _i1 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Integer.valueOf(n) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _i2 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Integer.valueOf(`n₂`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _i3 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Integer.valueOf(`n₃`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _i4 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Integer.valueOf(`n₄`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _i5 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Integer.valueOf(`n₅`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i5"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJInt_fields) + } + if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJInt(_i1, _i2, _i3, _i4, _i5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("i1", "i2", "i3", "i4", "i5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJInt] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJInt, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJInt = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJInt]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleJInt_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("i1", "i2", "i3", "i4", "i5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleJInt, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("i1") + out.value(in.i1) + out.label("i2") + out.value(in.i2) + out.label("i3") + out.value(in.i3) + out.label("i4") + out.value(in.i4) + out.label("i5") + out.value(in.i5) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJInt = { + var _i1: java.lang.Integer = java.lang.Integer.valueOf(0) + var _i2: java.lang.Integer = java.lang.Integer.valueOf(0) + var _i3: java.lang.Integer = java.lang.Integer.valueOf(0) + var _i4: java.lang.Integer = java.lang.Integer.valueOf(0) + var _i5: java.lang.Integer = java.lang.Integer.valueOf(0) + var required: scala.Int = 31 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJInt_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJInt] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _i1 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Integer.valueOf(n) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _i2 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Integer.valueOf(`n₂`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _i3 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Integer.valueOf(`n₃`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _i4 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Integer.valueOf(`n₄`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _i5 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Integer.valueOf(`n₅`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i5"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJInt_fields) + } + if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJInt(_i1, _i2, _i3, _i4, _i5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("i1", "i2", "i3", "i4", "i5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJInt] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJInt, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJInt = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJInt]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleJLong_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("l1", "l2", "l3", "l4", "l5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleJLong, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("l1") + out.value(in.l1) + out.label("l2") + out.value(in.l2) + out.label("l3") + out.value(in.l3) + out.label("l4") + out.value(in.l4) + out.label("l5") + out.value(in.l5) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJLong = { + var _l1: java.lang.Long = java.lang.Long.valueOf(0L) + var _l2: java.lang.Long = java.lang.Long.valueOf(0L) + var _l3: java.lang.Long = java.lang.Long.valueOf(0L) + var _l4: java.lang.Long = java.lang.Long.valueOf(0L) + var _l5: java.lang.Long = java.lang.Long.valueOf(0L) + var required: scala.Int = 31 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJLong_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJLong] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _l1 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Long.valueOf(n) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _l2 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Long.valueOf(`n₂`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _l3 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Long.valueOf(`n₃`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _l4 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Long.valueOf(`n₄`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _l5 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Long.valueOf(`n₅`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l5"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJLong_fields) + } + if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJLong(_l1, _l2, _l3, _l4, _l5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("l1", "l2", "l3", "l4", "l5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJLong] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJLong, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJLong = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJLong]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleJLong_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("l1", "l2", "l3", "l4", "l5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleJLong, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("l1") + out.value(in.l1) + out.label("l2") + out.value(in.l2) + out.label("l3") + out.value(in.l3) + out.label("l4") + out.value(in.l4) + out.label("l5") + out.value(in.l5) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJLong = { + var _l1: java.lang.Long = java.lang.Long.valueOf(0L) + var _l2: java.lang.Long = java.lang.Long.valueOf(0L) + var _l3: java.lang.Long = java.lang.Long.valueOf(0L) + var _l4: java.lang.Long = java.lang.Long.valueOf(0L) + var _l5: java.lang.Long = java.lang.Long.valueOf(0L) + var required: scala.Int = 31 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJLong_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJLong] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _l1 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Long.valueOf(n) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _l2 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Long.valueOf(`n₂`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _l3 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Long.valueOf(`n₃`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _l4 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Long.valueOf(`n₄`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _l5 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Long.valueOf(`n₅`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l5"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJLong_fields) + } + if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJLong(_l1, _l2, _l3, _l4, _l5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("l1", "l2", "l3", "l4", "l5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJLong] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJLong, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJLong = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJLong]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleJNumber_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("n1", "n2", "n3", "n4", "n5", "n6", "n7", "n8", "n9", "n10", "n11", "n12", "n13", "n14", "n15", "n16", "n17")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleJNumber, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("n1") + out.value(in.n1) + out.label("n2") + out.value(in.n2) + out.label("n3") + out.value(in.n3) + out.label("n4") + out.value(in.n4) + out.label("n5") + out.value(in.n5) + out.label("n6") + out.value(in.n6) + out.label("n7") + out.value(in.n7) + out.label("n8") + out.value(in.n8) + out.label("n9") + out.value(in.n9) + out.label("n10") + out.value(in.n10) + out.label("n11") + out.value(in.n11) + out.label("n12") + out.value(in.n12) + out.label("n13") + out.value(in.n13) + out.label("n14") + out.value(in.n14) + out.label("n15") + out.value(in.n15) + out.label("n16") + out.value(in.n16) + out.label("n17") + out.value(in.n17) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJNumber = { + var _n1: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n2: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n3: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n4: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n5: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n6: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n7: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n8: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n9: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n10: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n11: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n12: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n13: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n14: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n15: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n16: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n17: java.lang.Number = null.asInstanceOf[java.lang.Number] + var required: scala.Int = 131071 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJNumber_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJNumber] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _n1 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(n) match { + case d if d.isValidByte => + java.lang.Byte.valueOf(d.toByteExact) + case d if `d₂`.isValidShort => + java.lang.Short.valueOf(`d₂`.toShortExact) + case d if `d₃`.isValidInt => + java.lang.Integer.valueOf(`d₃`.toIntExact) + case d if `d₄`.isValidLong => + java.lang.Long.valueOf(`d₄`.toLongExact) + case d if `d₅`.isDecimalFloat => + java.lang.Float.valueOf(`d₅`.toFloat) + case d if `d₆`.isDecimalDouble => + java.lang.Double.valueOf(`d₆`.toDouble) + case d => + (`d₇`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _n2 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₂`) match { + case d if `d₈`.isValidByte => + java.lang.Byte.valueOf(`d₈`.toByteExact) + case d if `d₉`.isValidShort => + java.lang.Short.valueOf(`d₉`.toShortExact) + case d if `d₁₀`.isValidInt => + java.lang.Integer.valueOf(`d₁₀`.toIntExact) + case d if `d₁₁`.isValidLong => + java.lang.Long.valueOf(`d₁₁`.toLongExact) + case d if `d₁₂`.isDecimalFloat => + java.lang.Float.valueOf(`d₁₂`.toFloat) + case d if `d₁₃`.isDecimalDouble => + java.lang.Double.valueOf(`d₁₃`.toDouble) + case d => + (`d₁₄`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _n3 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₃`) match { + case d if `d₁₅`.isValidByte => + java.lang.Byte.valueOf(`d₁₅`.toByteExact) + case d if `d₁₆`.isValidShort => + java.lang.Short.valueOf(`d₁₆`.toShortExact) + case d if `d₁₇`.isValidInt => + java.lang.Integer.valueOf(`d₁₇`.toIntExact) + case d if `d₁₈`.isValidLong => + java.lang.Long.valueOf(`d₁₈`.toLongExact) + case d if `d₁₉`.isDecimalFloat => + java.lang.Float.valueOf(`d₁₉`.toFloat) + case d if `d₂₀`.isDecimalDouble => + java.lang.Double.valueOf(`d₂₀`.toDouble) + case d => + (`d₂₁`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _n4 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₄`) match { + case d if `d₂₂`.isValidByte => + java.lang.Byte.valueOf(`d₂₂`.toByteExact) + case d if `d₂₃`.isValidShort => + java.lang.Short.valueOf(`d₂₃`.toShortExact) + case d if `d₂₄`.isValidInt => + java.lang.Integer.valueOf(`d₂₄`.toIntExact) + case d if `d₂₅`.isValidLong => + java.lang.Long.valueOf(`d₂₅`.toLongExact) + case d if `d₂₆`.isDecimalFloat => + java.lang.Float.valueOf(`d₂₆`.toFloat) + case d if `d₂₇`.isDecimalDouble => + java.lang.Double.valueOf(`d₂₇`.toDouble) + case d => + (`d₂₈`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _n5 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₅`) match { + case d if `d₂₉`.isValidByte => + java.lang.Byte.valueOf(`d₂₉`.toByteExact) + case d if `d₃₀`.isValidShort => + java.lang.Short.valueOf(`d₃₀`.toShortExact) + case d if `d₃₁`.isValidInt => + java.lang.Integer.valueOf(`d₃₁`.toIntExact) + case d if `d₃₂`.isValidLong => + java.lang.Long.valueOf(`d₃₂`.toLongExact) + case d if `d₃₃`.isDecimalFloat => + java.lang.Float.valueOf(`d₃₃`.toFloat) + case d if `d₃₄`.isDecimalDouble => + java.lang.Double.valueOf(`d₃₄`.toDouble) + case d => + (`d₃₅`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n5"), `in₂`) + case 5 => + if (required.&(32).!=(0)) { + required = required.^(32) + _n6 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₆`) match { + case d if `d₃₆`.isValidByte => + java.lang.Byte.valueOf(`d₃₆`.toByteExact) + case d if `d₃₇`.isValidShort => + java.lang.Short.valueOf(`d₃₇`.toShortExact) + case d if `d₃₈`.isValidInt => + java.lang.Integer.valueOf(`d₃₈`.toIntExact) + case d if `d₃₉`.isValidLong => + java.lang.Long.valueOf(`d₃₉`.toLongExact) + case d if `d₄₀`.isDecimalFloat => + java.lang.Float.valueOf(`d₄₀`.toFloat) + case d if `d₄₁`.isDecimalDouble => + java.lang.Double.valueOf(`d₄₁`.toDouble) + case d => + (`d₄₂`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n6"), `in₂`) + case 6 => + if (required.&(64).!=(0)) { + required = required.^(64) + _n7 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₇`) match { + case d if `d₄₃`.isValidByte => + java.lang.Byte.valueOf(`d₄₃`.toByteExact) + case d if `d₄₄`.isValidShort => + java.lang.Short.valueOf(`d₄₄`.toShortExact) + case d if `d₄₅`.isValidInt => + java.lang.Integer.valueOf(`d₄₅`.toIntExact) + case d if `d₄₆`.isValidLong => + java.lang.Long.valueOf(`d₄₆`.toLongExact) + case d if `d₄₇`.isDecimalFloat => + java.lang.Float.valueOf(`d₄₇`.toFloat) + case d if `d₄₈`.isDecimalDouble => + java.lang.Double.valueOf(`d₄₈`.toDouble) + case d => + (`d₄₉`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n7"), `in₂`) + case 7 => + if (required.&(128).!=(0)) { + required = required.^(128) + _n8 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₈`) match { + case d if `d₅₀`.isValidByte => + java.lang.Byte.valueOf(`d₅₀`.toByteExact) + case d if `d₅₁`.isValidShort => + java.lang.Short.valueOf(`d₅₁`.toShortExact) + case d if `d₅₂`.isValidInt => + java.lang.Integer.valueOf(`d₅₂`.toIntExact) + case d if `d₅₃`.isValidLong => + java.lang.Long.valueOf(`d₅₃`.toLongExact) + case d if `d₅₄`.isDecimalFloat => + java.lang.Float.valueOf(`d₅₄`.toFloat) + case d if `d₅₅`.isDecimalDouble => + java.lang.Double.valueOf(`d₅₅`.toDouble) + case d => + (`d₅₆`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n8"), `in₂`) + case 8 => + if (required.&(256).!=(0)) { + required = required.^(256) + _n9 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₉`) match { + case d if `d₅₇`.isValidByte => + java.lang.Byte.valueOf(`d₅₇`.toByteExact) + case d if `d₅₈`.isValidShort => + java.lang.Short.valueOf(`d₅₈`.toShortExact) + case d if `d₅₉`.isValidInt => + java.lang.Integer.valueOf(`d₅₉`.toIntExact) + case d if `d₆₀`.isValidLong => + java.lang.Long.valueOf(`d₆₀`.toLongExact) + case d if `d₆₁`.isDecimalFloat => + java.lang.Float.valueOf(`d₆₁`.toFloat) + case d if `d₆₂`.isDecimalDouble => + java.lang.Double.valueOf(`d₆₂`.toDouble) + case d => + (`d₆₃`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n9"), `in₂`) + case 9 => + if (required.&(512).!=(0)) { + required = required.^(512) + _n10 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₁₀`) match { + case d if `d₆₄`.isValidByte => + java.lang.Byte.valueOf(`d₆₄`.toByteExact) + case d if `d₆₅`.isValidShort => + java.lang.Short.valueOf(`d₆₅`.toShortExact) + case d if `d₆₆`.isValidInt => + java.lang.Integer.valueOf(`d₆₆`.toIntExact) + case d if `d₆₇`.isValidLong => + java.lang.Long.valueOf(`d₆₇`.toLongExact) + case d if `d₆₈`.isDecimalFloat => + java.lang.Float.valueOf(`d₆₈`.toFloat) + case d if `d₆₉`.isDecimalDouble => + java.lang.Double.valueOf(`d₆₉`.toDouble) + case d => + (`d₇₀`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n10"), `in₂`) + case 10 => + if (required.&(1024).!=(0)) { + required = required.^(1024) + _n11 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₁₁`) match { + case d if `d₇₁`.isValidByte => + java.lang.Byte.valueOf(`d₇₁`.toByteExact) + case d if `d₇₂`.isValidShort => + java.lang.Short.valueOf(`d₇₂`.toShortExact) + case d if `d₇₃`.isValidInt => + java.lang.Integer.valueOf(`d₇₃`.toIntExact) + case d if `d₇₄`.isValidLong => + java.lang.Long.valueOf(`d₇₄`.toLongExact) + case d if `d₇₅`.isDecimalFloat => + java.lang.Float.valueOf(`d₇₅`.toFloat) + case d if `d₇₆`.isDecimalDouble => + java.lang.Double.valueOf(`d₇₆`.toDouble) + case d => + (`d₇₇`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n11"), `in₂`) + case 11 => + if (required.&(2048).!=(0)) { + required = required.^(2048) + _n12 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₁₂`) match { + case d if `d₇₈`.isValidByte => + java.lang.Byte.valueOf(`d₇₈`.toByteExact) + case d if `d₇₉`.isValidShort => + java.lang.Short.valueOf(`d₇₉`.toShortExact) + case d if `d₈₀`.isValidInt => + java.lang.Integer.valueOf(`d₈₀`.toIntExact) + case d if `d₈₁`.isValidLong => + java.lang.Long.valueOf(`d₈₁`.toLongExact) + case d if `d₈₂`.isDecimalFloat => + java.lang.Float.valueOf(`d₈₂`.toFloat) + case d if `d₈₃`.isDecimalDouble => + java.lang.Double.valueOf(`d₈₃`.toDouble) + case d => + (`d₈₄`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n12"), `in₂`) + case 12 => + if (required.&(4096).!=(0)) { + required = required.^(4096) + _n13 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₁₃`) match { + case d if `d₈₅`.isValidByte => + java.lang.Byte.valueOf(`d₈₅`.toByteExact) + case d if `d₈₆`.isValidShort => + java.lang.Short.valueOf(`d₈₆`.toShortExact) + case d if `d₈₇`.isValidInt => + java.lang.Integer.valueOf(`d₈₇`.toIntExact) + case d if `d₈₈`.isValidLong => + java.lang.Long.valueOf(`d₈₈`.toLongExact) + case d if `d₈₉`.isDecimalFloat => + java.lang.Float.valueOf(`d₈₉`.toFloat) + case d if `d₉₀`.isDecimalDouble => + java.lang.Double.valueOf(`d₉₀`.toDouble) + case d => + (`d₉₁`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n13"), `in₂`) + case 13 => + if (required.&(8192).!=(0)) { + required = required.^(8192) + _n14 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₁₄`) match { + case d if `d₉₂`.isValidByte => + java.lang.Byte.valueOf(`d₉₂`.toByteExact) + case d if `d₉₃`.isValidShort => + java.lang.Short.valueOf(`d₉₃`.toShortExact) + case d if `d₉₄`.isValidInt => + java.lang.Integer.valueOf(`d₉₄`.toIntExact) + case d if `d₉₅`.isValidLong => + java.lang.Long.valueOf(`d₉₅`.toLongExact) + case d if `d₉₆`.isDecimalFloat => + java.lang.Float.valueOf(`d₉₆`.toFloat) + case d if `d₉₇`.isDecimalDouble => + java.lang.Double.valueOf(`d₉₇`.toDouble) + case d => + (`d₉₈`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n14"), `in₂`) + case 14 => + if (required.&(16384).!=(0)) { + required = required.^(16384) + _n15 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₁₅`) match { + case d if `d₉₉`.isValidByte => + java.lang.Byte.valueOf(`d₉₉`.toByteExact) + case d if `d₁₀₀`.isValidShort => + java.lang.Short.valueOf(`d₁₀₀`.toShortExact) + case d if `d₁₀₁`.isValidInt => + java.lang.Integer.valueOf(`d₁₀₁`.toIntExact) + case d if `d₁₀₂`.isValidLong => + java.lang.Long.valueOf(`d₁₀₂`.toLongExact) + case d if `d₁₀₃`.isDecimalFloat => + java.lang.Float.valueOf(`d₁₀₃`.toFloat) + case d if `d₁₀₄`.isDecimalDouble => + java.lang.Double.valueOf(`d₁₀₄`.toDouble) + case d => + (`d₁₀₅`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n15"), `in₂`) + case 15 => + if (required.&(32768).!=(0)) { + required = required.^(32768) + _n16 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₁₆`) match { + case d if `d₁₀₆`.isValidByte => + java.lang.Byte.valueOf(`d₁₀₆`.toByteExact) + case d if `d₁₀₇`.isValidShort => + java.lang.Short.valueOf(`d₁₀₇`.toShortExact) + case d if `d₁₀₈`.isValidInt => + java.lang.Integer.valueOf(`d₁₀₈`.toIntExact) + case d if `d₁₀₉`.isValidLong => + java.lang.Long.valueOf(`d₁₀₉`.toLongExact) + case d if `d₁₁₀`.isDecimalFloat => + java.lang.Float.valueOf(`d₁₁₀`.toFloat) + case d if `d₁₁₁`.isDecimalDouble => + java.lang.Double.valueOf(`d₁₁₁`.toDouble) + case d => + (`d₁₁₂`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n16"), `in₂`) + case 16 => + if (required.&(65536).!=(0)) { + required = required.^(65536) + _n17 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₁₇`) match { + case d if `d₁₁₃`.isValidByte => + java.lang.Byte.valueOf(`d₁₁₃`.toByteExact) + case d if `d₁₁₄`.isValidShort => + java.lang.Short.valueOf(`d₁₁₄`.toShortExact) + case d if `d₁₁₅`.isValidInt => + java.lang.Integer.valueOf(`d₁₁₅`.toIntExact) + case d if `d₁₁₆`.isValidLong => + java.lang.Long.valueOf(`d₁₁₆`.toLongExact) + case d if `d₁₁₇`.isDecimalFloat => + java.lang.Float.valueOf(`d₁₁₇`.toFloat) + case d if `d₁₁₈`.isDecimalDouble => + java.lang.Double.valueOf(`d₁₁₈`.toDouble) + case d => + (`d₁₁₉`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n17"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJNumber_fields) + } + if (required.&(131071).==(0)) new co.blocke.scalajack.json.primitives.SampleJNumber(_n1, _n2, _n3, _n4, _n5, _n6, _n7, _n8, _n9, _n10, _n11, _n12, _n13, _n14, _n15, _n16, _n17) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("n1", "n2", "n3", "n4", "n5", "n6", "n7", "n8", "n9", "n10", "n11", "n12", "n13", "n14", "n15", "n16", "n17")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(131071)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJNumber] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJNumber, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJNumber = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJNumber]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleJNumber_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("n1", "n2", "n3", "n4", "n5", "n6", "n7", "n8", "n9", "n10", "n11", "n12", "n13", "n14", "n15", "n16", "n17")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleJNumber, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("n1") + out.value(in.n1) + out.label("n2") + out.value(in.n2) + out.label("n3") + out.value(in.n3) + out.label("n4") + out.value(in.n4) + out.label("n5") + out.value(in.n5) + out.label("n6") + out.value(in.n6) + out.label("n7") + out.value(in.n7) + out.label("n8") + out.value(in.n8) + out.label("n9") + out.value(in.n9) + out.label("n10") + out.value(in.n10) + out.label("n11") + out.value(in.n11) + out.label("n12") + out.value(in.n12) + out.label("n13") + out.value(in.n13) + out.label("n14") + out.value(in.n14) + out.label("n15") + out.value(in.n15) + out.label("n16") + out.value(in.n16) + out.label("n17") + out.value(in.n17) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJNumber = { + var _n1: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n2: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n3: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n4: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n5: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n6: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n7: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n8: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n9: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n10: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n11: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n12: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n13: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n14: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n15: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n16: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n17: java.lang.Number = null.asInstanceOf[java.lang.Number] + var required: scala.Int = 131071 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJNumber_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJNumber] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _n1 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(n) match { + case d if d.isValidByte => + java.lang.Byte.valueOf(d.toByteExact) + case d if `d₂`.isValidShort => + java.lang.Short.valueOf(`d₂`.toShortExact) + case d if `d₃`.isValidInt => + java.lang.Integer.valueOf(`d₃`.toIntExact) + case d if `d₄`.isValidLong => + java.lang.Long.valueOf(`d₄`.toLongExact) + case d if `d₅`.isDecimalFloat => + java.lang.Float.valueOf(`d₅`.toFloat) + case d if `d₆`.isDecimalDouble => + java.lang.Double.valueOf(`d₆`.toDouble) + case d => + (`d₇`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _n2 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₂`) match { + case d if `d₈`.isValidByte => + java.lang.Byte.valueOf(`d₈`.toByteExact) + case d if `d₉`.isValidShort => + java.lang.Short.valueOf(`d₉`.toShortExact) + case d if `d₁₀`.isValidInt => + java.lang.Integer.valueOf(`d₁₀`.toIntExact) + case d if `d₁₁`.isValidLong => + java.lang.Long.valueOf(`d₁₁`.toLongExact) + case d if `d₁₂`.isDecimalFloat => + java.lang.Float.valueOf(`d₁₂`.toFloat) + case d if `d₁₃`.isDecimalDouble => + java.lang.Double.valueOf(`d₁₃`.toDouble) + case d => + (`d₁₄`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _n3 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₃`) match { + case d if `d₁₅`.isValidByte => + java.lang.Byte.valueOf(`d₁₅`.toByteExact) + case d if `d₁₆`.isValidShort => + java.lang.Short.valueOf(`d₁₆`.toShortExact) + case d if `d₁₇`.isValidInt => + java.lang.Integer.valueOf(`d₁₇`.toIntExact) + case d if `d₁₈`.isValidLong => + java.lang.Long.valueOf(`d₁₈`.toLongExact) + case d if `d₁₉`.isDecimalFloat => + java.lang.Float.valueOf(`d₁₉`.toFloat) + case d if `d₂₀`.isDecimalDouble => + java.lang.Double.valueOf(`d₂₀`.toDouble) + case d => + (`d₂₁`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _n4 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₄`) match { + case d if `d₂₂`.isValidByte => + java.lang.Byte.valueOf(`d₂₂`.toByteExact) + case d if `d₂₃`.isValidShort => + java.lang.Short.valueOf(`d₂₃`.toShortExact) + case d if `d₂₄`.isValidInt => + java.lang.Integer.valueOf(`d₂₄`.toIntExact) + case d if `d₂₅`.isValidLong => + java.lang.Long.valueOf(`d₂₅`.toLongExact) + case d if `d₂₆`.isDecimalFloat => + java.lang.Float.valueOf(`d₂₆`.toFloat) + case d if `d₂₇`.isDecimalDouble => + java.lang.Double.valueOf(`d₂₇`.toDouble) + case d => + (`d₂₈`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _n5 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₅`) match { + case d if `d₂₉`.isValidByte => + java.lang.Byte.valueOf(`d₂₉`.toByteExact) + case d if `d₃₀`.isValidShort => + java.lang.Short.valueOf(`d₃₀`.toShortExact) + case d if `d₃₁`.isValidInt => + java.lang.Integer.valueOf(`d₃₁`.toIntExact) + case d if `d₃₂`.isValidLong => + java.lang.Long.valueOf(`d₃₂`.toLongExact) + case d if `d₃₃`.isDecimalFloat => + java.lang.Float.valueOf(`d₃₃`.toFloat) + case d if `d₃₄`.isDecimalDouble => + java.lang.Double.valueOf(`d₃₄`.toDouble) + case d => + (`d₃₅`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n5"), `in₂`) + case 5 => + if (required.&(32).!=(0)) { + required = required.^(32) + _n6 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₆`) match { + case d if `d₃₆`.isValidByte => + java.lang.Byte.valueOf(`d₃₆`.toByteExact) + case d if `d₃₇`.isValidShort => + java.lang.Short.valueOf(`d₃₇`.toShortExact) + case d if `d₃₈`.isValidInt => + java.lang.Integer.valueOf(`d₃₈`.toIntExact) + case d if `d₃₉`.isValidLong => + java.lang.Long.valueOf(`d₃₉`.toLongExact) + case d if `d₄₀`.isDecimalFloat => + java.lang.Float.valueOf(`d₄₀`.toFloat) + case d if `d₄₁`.isDecimalDouble => + java.lang.Double.valueOf(`d₄₁`.toDouble) + case d => + (`d₄₂`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n6"), `in₂`) + case 6 => + if (required.&(64).!=(0)) { + required = required.^(64) + _n7 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₇`) match { + case d if `d₄₃`.isValidByte => + java.lang.Byte.valueOf(`d₄₃`.toByteExact) + case d if `d₄₄`.isValidShort => + java.lang.Short.valueOf(`d₄₄`.toShortExact) + case d if `d₄₅`.isValidInt => + java.lang.Integer.valueOf(`d₄₅`.toIntExact) + case d if `d₄₆`.isValidLong => + java.lang.Long.valueOf(`d₄₆`.toLongExact) + case d if `d₄₇`.isDecimalFloat => + java.lang.Float.valueOf(`d₄₇`.toFloat) + case d if `d₄₈`.isDecimalDouble => + java.lang.Double.valueOf(`d₄₈`.toDouble) + case d => + (`d₄₉`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n7"), `in₂`) + case 7 => + if (required.&(128).!=(0)) { + required = required.^(128) + _n8 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₈`) match { + case d if `d₅₀`.isValidByte => + java.lang.Byte.valueOf(`d₅₀`.toByteExact) + case d if `d₅₁`.isValidShort => + java.lang.Short.valueOf(`d₅₁`.toShortExact) + case d if `d₅₂`.isValidInt => + java.lang.Integer.valueOf(`d₅₂`.toIntExact) + case d if `d₅₃`.isValidLong => + java.lang.Long.valueOf(`d₅₃`.toLongExact) + case d if `d₅₄`.isDecimalFloat => + java.lang.Float.valueOf(`d₅₄`.toFloat) + case d if `d₅₅`.isDecimalDouble => + java.lang.Double.valueOf(`d₅₅`.toDouble) + case d => + (`d₅₆`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n8"), `in₂`) + case 8 => + if (required.&(256).!=(0)) { + required = required.^(256) + _n9 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₉`) match { + case d if `d₅₇`.isValidByte => + java.lang.Byte.valueOf(`d₅₇`.toByteExact) + case d if `d₅₈`.isValidShort => + java.lang.Short.valueOf(`d₅₈`.toShortExact) + case d if `d₅₉`.isValidInt => + java.lang.Integer.valueOf(`d₅₉`.toIntExact) + case d if `d₆₀`.isValidLong => + java.lang.Long.valueOf(`d₆₀`.toLongExact) + case d if `d₆₁`.isDecimalFloat => + java.lang.Float.valueOf(`d₆₁`.toFloat) + case d if `d₆₂`.isDecimalDouble => + java.lang.Double.valueOf(`d₆₂`.toDouble) + case d => + (`d₆₃`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n9"), `in₂`) + case 9 => + if (required.&(512).!=(0)) { + required = required.^(512) + _n10 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₁₀`) match { + case d if `d₆₄`.isValidByte => + java.lang.Byte.valueOf(`d₆₄`.toByteExact) + case d if `d₆₅`.isValidShort => + java.lang.Short.valueOf(`d₆₅`.toShortExact) + case d if `d₆₆`.isValidInt => + java.lang.Integer.valueOf(`d₆₆`.toIntExact) + case d if `d₆₇`.isValidLong => + java.lang.Long.valueOf(`d₆₇`.toLongExact) + case d if `d₆₈`.isDecimalFloat => + java.lang.Float.valueOf(`d₆₈`.toFloat) + case d if `d₆₉`.isDecimalDouble => + java.lang.Double.valueOf(`d₆₉`.toDouble) + case d => + (`d₇₀`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n10"), `in₂`) + case 10 => + if (required.&(1024).!=(0)) { + required = required.^(1024) + _n11 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₁₁`) match { + case d if `d₇₁`.isValidByte => + java.lang.Byte.valueOf(`d₇₁`.toByteExact) + case d if `d₇₂`.isValidShort => + java.lang.Short.valueOf(`d₇₂`.toShortExact) + case d if `d₇₃`.isValidInt => + java.lang.Integer.valueOf(`d₇₃`.toIntExact) + case d if `d₇₄`.isValidLong => + java.lang.Long.valueOf(`d₇₄`.toLongExact) + case d if `d₇₅`.isDecimalFloat => + java.lang.Float.valueOf(`d₇₅`.toFloat) + case d if `d₇₆`.isDecimalDouble => + java.lang.Double.valueOf(`d₇₆`.toDouble) + case d => + (`d₇₇`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n11"), `in₂`) + case 11 => + if (required.&(2048).!=(0)) { + required = required.^(2048) + _n12 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₁₂`) match { + case d if `d₇₈`.isValidByte => + java.lang.Byte.valueOf(`d₇₈`.toByteExact) + case d if `d₇₉`.isValidShort => + java.lang.Short.valueOf(`d₇₉`.toShortExact) + case d if `d₈₀`.isValidInt => + java.lang.Integer.valueOf(`d₈₀`.toIntExact) + case d if `d₈₁`.isValidLong => + java.lang.Long.valueOf(`d₈₁`.toLongExact) + case d if `d₈₂`.isDecimalFloat => + java.lang.Float.valueOf(`d₈₂`.toFloat) + case d if `d₈₃`.isDecimalDouble => + java.lang.Double.valueOf(`d₈₃`.toDouble) + case d => + (`d₈₄`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n12"), `in₂`) + case 12 => + if (required.&(4096).!=(0)) { + required = required.^(4096) + _n13 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₁₃`) match { + case d if `d₈₅`.isValidByte => + java.lang.Byte.valueOf(`d₈₅`.toByteExact) + case d if `d₈₆`.isValidShort => + java.lang.Short.valueOf(`d₈₆`.toShortExact) + case d if `d₈₇`.isValidInt => + java.lang.Integer.valueOf(`d₈₇`.toIntExact) + case d if `d₈₈`.isValidLong => + java.lang.Long.valueOf(`d₈₈`.toLongExact) + case d if `d₈₉`.isDecimalFloat => + java.lang.Float.valueOf(`d₈₉`.toFloat) + case d if `d₉₀`.isDecimalDouble => + java.lang.Double.valueOf(`d₉₀`.toDouble) + case d => + (`d₉₁`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n13"), `in₂`) + case 13 => + if (required.&(8192).!=(0)) { + required = required.^(8192) + _n14 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₁₄`) match { + case d if `d₉₂`.isValidByte => + java.lang.Byte.valueOf(`d₉₂`.toByteExact) + case d if `d₉₃`.isValidShort => + java.lang.Short.valueOf(`d₉₃`.toShortExact) + case d if `d₉₄`.isValidInt => + java.lang.Integer.valueOf(`d₉₄`.toIntExact) + case d if `d₉₅`.isValidLong => + java.lang.Long.valueOf(`d₉₅`.toLongExact) + case d if `d₉₆`.isDecimalFloat => + java.lang.Float.valueOf(`d₉₆`.toFloat) + case d if `d₉₇`.isDecimalDouble => + java.lang.Double.valueOf(`d₉₇`.toDouble) + case d => + (`d₉₈`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n14"), `in₂`) + case 14 => + if (required.&(16384).!=(0)) { + required = required.^(16384) + _n15 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₁₅`) match { + case d if `d₉₉`.isValidByte => + java.lang.Byte.valueOf(`d₉₉`.toByteExact) + case d if `d₁₀₀`.isValidShort => + java.lang.Short.valueOf(`d₁₀₀`.toShortExact) + case d if `d₁₀₁`.isValidInt => + java.lang.Integer.valueOf(`d₁₀₁`.toIntExact) + case d if `d₁₀₂`.isValidLong => + java.lang.Long.valueOf(`d₁₀₂`.toLongExact) + case d if `d₁₀₃`.isDecimalFloat => + java.lang.Float.valueOf(`d₁₀₃`.toFloat) + case d if `d₁₀₄`.isDecimalDouble => + java.lang.Double.valueOf(`d₁₀₄`.toDouble) + case d => + (`d₁₀₅`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n15"), `in₂`) + case 15 => + if (required.&(32768).!=(0)) { + required = required.^(32768) + _n16 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₁₆`) match { + case d if `d₁₀₆`.isValidByte => + java.lang.Byte.valueOf(`d₁₀₆`.toByteExact) + case d if `d₁₀₇`.isValidShort => + java.lang.Short.valueOf(`d₁₀₇`.toShortExact) + case d if `d₁₀₈`.isValidInt => + java.lang.Integer.valueOf(`d₁₀₈`.toIntExact) + case d if `d₁₀₉`.isValidLong => + java.lang.Long.valueOf(`d₁₀₉`.toLongExact) + case d if `d₁₁₀`.isDecimalFloat => + java.lang.Float.valueOf(`d₁₁₀`.toFloat) + case d if `d₁₁₁`.isDecimalDouble => + java.lang.Double.valueOf(`d₁₁₁`.toDouble) + case d => + (`d₁₁₂`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n16"), `in₂`) + case 16 => + if (required.&(65536).!=(0)) { + required = required.^(65536) + _n17 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₁₇`) match { + case d if `d₁₁₃`.isValidByte => + java.lang.Byte.valueOf(`d₁₁₃`.toByteExact) + case d if `d₁₁₄`.isValidShort => + java.lang.Short.valueOf(`d₁₁₄`.toShortExact) + case d if `d₁₁₅`.isValidInt => + java.lang.Integer.valueOf(`d₁₁₅`.toIntExact) + case d if `d₁₁₆`.isValidLong => + java.lang.Long.valueOf(`d₁₁₆`.toLongExact) + case d if `d₁₁₇`.isDecimalFloat => + java.lang.Float.valueOf(`d₁₁₇`.toFloat) + case d if `d₁₁₈`.isDecimalDouble => + java.lang.Double.valueOf(`d₁₁₈`.toDouble) + case d => + (`d₁₁₉`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n17"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJNumber_fields) + } + if (required.&(131071).==(0)) new co.blocke.scalajack.json.primitives.SampleJNumber(_n1, _n2, _n3, _n4, _n5, _n6, _n7, _n8, _n9, _n10, _n11, _n12, _n13, _n14, _n15, _n16, _n17) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("n1", "n2", "n3", "n4", "n5", "n6", "n7", "n8", "n9", "n10", "n11", "n12", "n13", "n14", "n15", "n16", "n17")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(131071)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJNumber] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJNumber, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJNumber = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJNumber]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleJShort_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("s1", "s2", "s3", "s4", "s5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleJShort, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("s1") + out.value(in.s1) + out.label("s2") + out.value(in.s2) + out.label("s3") + out.value(in.s3) + out.label("s4") + out.value(in.s4) + out.label("s5") + out.value(in.s5) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJShort = { + var _s1: java.lang.Short = java.lang.Short.valueOf(0.toShort) + var _s2: java.lang.Short = java.lang.Short.valueOf(0.toShort) + var _s3: java.lang.Short = java.lang.Short.valueOf(0.toShort) + var _s4: java.lang.Short = java.lang.Short.valueOf(0.toShort) + var _s5: java.lang.Short = java.lang.Short.valueOf(0.toShort) + var required: scala.Int = 31 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJShort_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJShort] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _s1 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Short.valueOf(n) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _s2 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Short.valueOf(`n₂`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _s3 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Short.valueOf(`n₃`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _s4 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Short.valueOf(`n₄`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _s5 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Short.valueOf(`n₅`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s5"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJShort_fields) + } + if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJShort(_s1, _s2, _s3, _s4, _s5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("s1", "s2", "s3", "s4", "s5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJShort] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJShort, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJShort = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJShort]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleJShort_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("s1", "s2", "s3", "s4", "s5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleJShort, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("s1") + out.value(in.s1) + out.label("s2") + out.value(in.s2) + out.label("s3") + out.value(in.s3) + out.label("s4") + out.value(in.s4) + out.label("s5") + out.value(in.s5) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJShort = { + var _s1: java.lang.Short = java.lang.Short.valueOf(0.toShort) + var _s2: java.lang.Short = java.lang.Short.valueOf(0.toShort) + var _s3: java.lang.Short = java.lang.Short.valueOf(0.toShort) + var _s4: java.lang.Short = java.lang.Short.valueOf(0.toShort) + var _s5: java.lang.Short = java.lang.Short.valueOf(0.toShort) + var required: scala.Int = 31 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJShort_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJShort] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _s1 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Short.valueOf(n) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _s2 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Short.valueOf(`n₂`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _s3 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Short.valueOf(`n₃`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _s4 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Short.valueOf(`n₄`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _s5 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Short.valueOf(`n₅`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s5"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJShort_fields) + } + if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJShort(_s1, _s2, _s3, _s4, _s5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("s1", "s2", "s3", "s4", "s5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJShort] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJShort, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJShort = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJShort]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleJBigDecimal_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("bd1", "bd2", "bd3", "bd4", "bd5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleJBigDecimal, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("bd1") + out.value(in.bd1) + out.label("bd2") + out.value(in.bd2) + out.label("bd3") + out.value(in.bd3) + out.label("bd4") + out.value(in.bd4) + out.label("bd5") + out.value(in.bd5) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJBigDecimal = { + var _bd1: java.math.BigDecimal = new java.math.BigDecimal(0.0) + var _bd2: java.math.BigDecimal = new java.math.BigDecimal(0.0) + var _bd3: java.math.BigDecimal = new java.math.BigDecimal(0.0) + var _bd4: java.math.BigDecimal = new java.math.BigDecimal(0.0) + var _bd5: java.math.BigDecimal = new java.math.BigDecimal(0.0) + var required: scala.Int = 31 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJBigDecimal_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJBigDecimal] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _bd1 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + new java.math.BigDecimal(n) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _bd2 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + new java.math.BigDecimal(`n₂`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _bd3 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + new java.math.BigDecimal(`n₃`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _bd4 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + new java.math.BigDecimal(`n₄`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _bd5 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + new java.math.BigDecimal(`n₅`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd5"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJBigDecimal_fields) + } + if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJBigDecimal(_bd1, _bd2, _bd3, _bd4, _bd5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("bd1", "bd2", "bd3", "bd4", "bd5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJBigDecimal] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJBigDecimal, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJBigDecimal = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJBigDecimal]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleJBigInteger_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("bi1", "bi2", "bi3", "bi4", "bi5", "bi6", "bi7")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleJBigInteger, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("bi1") + out.value(in.bi1) + out.label("bi2") + out.value(in.bi2) + out.label("bi3") + out.value(in.bi3) + out.label("bi4") + out.value(in.bi4) + out.label("bi5") + out.value(in.bi5) + out.label("bi6") + out.value(in.bi6) + out.label("bi7") + out.value(in.bi7) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJBigInteger = { + var _bi1: java.math.BigInteger = java.math.BigInteger.valueOf(0L) + var _bi2: java.math.BigInteger = java.math.BigInteger.valueOf(0L) + var _bi3: java.math.BigInteger = java.math.BigInteger.valueOf(0L) + var _bi4: java.math.BigInteger = java.math.BigInteger.valueOf(0L) + var _bi5: java.math.BigInteger = java.math.BigInteger.valueOf(0L) + var _bi6: java.math.BigInteger = java.math.BigInteger.valueOf(0L) + var _bi7: java.math.BigInteger = java.math.BigInteger.valueOf(0L) + var required: scala.Int = 127 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJBigInteger_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJBigInteger] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _bi1 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + new java.math.BigInteger(n) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _bi2 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + new java.math.BigInteger(`n₂`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _bi3 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + new java.math.BigInteger(`n₃`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _bi4 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + new java.math.BigInteger(`n₄`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _bi5 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + new java.math.BigInteger(`n₅`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi5"), `in₂`) + case 5 => + if (required.&(32).!=(0)) { + required = required.^(32) + _bi6 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + new java.math.BigInteger(`n₆`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi6"), `in₂`) + case 6 => + if (required.&(64).!=(0)) { + required = required.^(64) + _bi7 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + new java.math.BigInteger(`n₇`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi7"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJBigInteger_fields) + } + if (required.&(127).==(0)) new co.blocke.scalajack.json.primitives.SampleJBigInteger(_bi1, _bi2, _bi3, _bi4, _bi5, _bi6, _bi7) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("bi1", "bi2", "bi3", "bi4", "bi5", "bi6", "bi7")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(127)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJBigInteger] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJBigInteger, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJBigInteger = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJBigInteger]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleJBoolean_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("bool1", "bool2", "bool3", "bool4", "bool5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleJBoolean, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("bool1") + out.value(in.bool1) + out.label("bool2") + out.value(in.bool2) + out.label("bool3") + out.value(in.bool3) + out.label("bool4") + out.value(in.bool4) + out.label("bool5") + out.value(in.bool5) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJBoolean = { + var _bool1: java.lang.Boolean = java.lang.Boolean.FALSE + var _bool2: java.lang.Boolean = java.lang.Boolean.FALSE + var _bool3: java.lang.Boolean = java.lang.Boolean.FALSE + var _bool4: java.lang.Boolean = java.lang.Boolean.FALSE + var _bool5: java.lang.Boolean = java.lang.Boolean.FALSE + var required: scala.Int = 31 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJBoolean_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJBoolean] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _bool1 = `in₂`.expectJavaBoolean() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bool1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _bool2 = `in₂`.expectJavaBoolean() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bool2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _bool3 = `in₂`.expectJavaBoolean() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bool3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _bool4 = `in₂`.expectJavaBoolean() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bool4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _bool5 = `in₂`.expectJavaBoolean() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bool5"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJBoolean_fields) + } + if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJBoolean(_bool1, _bool2, _bool3, _bool4, _bool5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("bool1", "bool2", "bool3", "bool4", "bool5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJBoolean] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJBoolean, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJBoolean = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJBoolean]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleJByte_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("b1", "b2", "b3", "b4", "b5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleJByte, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("b1") + out.value(in.b1) + out.label("b2") + out.value(in.b2) + out.label("b3") + out.value(in.b3) + out.label("b4") + out.value(in.b4) + out.label("b5") + out.value(in.b5) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJByte = { + var _b1: java.lang.Byte = scala.Predef.byte2Byte(java.lang.Byte.MIN_VALUE) + var _b2: java.lang.Byte = scala.Predef.byte2Byte(java.lang.Byte.MIN_VALUE) + var _b3: java.lang.Byte = scala.Predef.byte2Byte(java.lang.Byte.MIN_VALUE) + var _b4: java.lang.Byte = scala.Predef.byte2Byte(java.lang.Byte.MIN_VALUE) + var _b5: java.lang.Byte = scala.Predef.byte2Byte(java.lang.Byte.MIN_VALUE) + var required: scala.Int = 31 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJByte_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJByte] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _b1 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Byte.valueOf(n) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _b2 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Byte.valueOf(`n₂`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _b3 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Byte.valueOf(`n₃`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _b4 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Byte.valueOf(`n₄`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _b5 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Byte.valueOf(`n₅`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b5"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJByte_fields) + } + if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJByte(_b1, _b2, _b3, _b4, _b5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("b1", "b2", "b3", "b4", "b5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJByte] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJByte, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJByte = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJByte]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleJChar_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("c1", "c2", "c3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleJChar, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("c1") + out.value(in.c1) + out.label("c2") + out.value(in.c2) + out.label("c3") + out.value(in.c3) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJChar = { + var _c1: java.lang.Character = java.lang.Character.valueOf('x') + var _c2: java.lang.Character = java.lang.Character.valueOf('x') + var _c3: java.lang.Character = java.lang.Character.valueOf('x') + var required: scala.Int = 7 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJChar_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJChar] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _c1 = { + val c: java.lang.String = `in₂`.expectString() + if (c.==(null)) null else if (c.length().==(0)) { + `in₂`.backspace() + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Character value expected but empty string found in json", `in₂`) + } else java.lang.Character.valueOf(c.charAt(0)) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _c2 = { + val `c₂`: java.lang.String = `in₂`.expectString() + if (`c₂`.==(null)) null else if (`c₂`.length().==(0)) { + `in₂`.backspace() + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Character value expected but empty string found in json", `in₂`) + } else java.lang.Character.valueOf(`c₂`.charAt(0)) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _c3 = { + val `c₃`: java.lang.String = `in₂`.expectString() + if (`c₃`.==(null)) null else if (`c₃`.length().==(0)) { + `in₂`.backspace() + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Character value expected but empty string found in json", `in₂`) + } else java.lang.Character.valueOf(`c₃`.charAt(0)) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c3"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJChar_fields) + } + if (required.&(7).==(0)) new co.blocke.scalajack.json.primitives.SampleJChar(_c1, _c2, _c3) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("c1", "c2", "c3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(7)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJChar] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJChar, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJChar = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJChar]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleJChar_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("c1", "c2", "c3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleJChar, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("c1") + out.value(in.c1) + out.label("c2") + out.value(in.c2) + out.label("c3") + out.value(in.c3) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJChar = { + var _c1: java.lang.Character = java.lang.Character.valueOf('x') + var _c2: java.lang.Character = java.lang.Character.valueOf('x') + var _c3: java.lang.Character = java.lang.Character.valueOf('x') + var required: scala.Int = 7 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJChar_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJChar] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _c1 = { + val c: java.lang.String = `in₂`.expectString() + if (c.==(null)) null else if (c.length().==(0)) { + `in₂`.backspace() + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Character value expected but empty string found in json", `in₂`) + } else java.lang.Character.valueOf(c.charAt(0)) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _c2 = { + val `c₂`: java.lang.String = `in₂`.expectString() + if (`c₂`.==(null)) null else if (`c₂`.length().==(0)) { + `in₂`.backspace() + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Character value expected but empty string found in json", `in₂`) + } else java.lang.Character.valueOf(`c₂`.charAt(0)) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _c3 = { + val `c₃`: java.lang.String = `in₂`.expectString() + if (`c₃`.==(null)) null else if (`c₃`.length().==(0)) { + `in₂`.backspace() + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Character value expected but empty string found in json", `in₂`) + } else java.lang.Character.valueOf(`c₃`.charAt(0)) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c3"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJChar_fields) + } + if (required.&(7).==(0)) new co.blocke.scalajack.json.primitives.SampleJChar(_c1, _c2, _c3) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("c1", "c2", "c3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(7)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJChar] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJChar, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJChar = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJChar]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleJChar_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("c1", "c2", "c3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleJChar, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("c1") + out.value(in.c1) + out.label("c2") + out.value(in.c2) + out.label("c3") + out.value(in.c3) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJChar = { + var _c1: java.lang.Character = java.lang.Character.valueOf('x') + var _c2: java.lang.Character = java.lang.Character.valueOf('x') + var _c3: java.lang.Character = java.lang.Character.valueOf('x') + var required: scala.Int = 7 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJChar_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJChar] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _c1 = { + val c: java.lang.String = `in₂`.expectString() + if (c.==(null)) null else if (c.length().==(0)) { + `in₂`.backspace() + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Character value expected but empty string found in json", `in₂`) + } else java.lang.Character.valueOf(c.charAt(0)) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _c2 = { + val `c₂`: java.lang.String = `in₂`.expectString() + if (`c₂`.==(null)) null else if (`c₂`.length().==(0)) { + `in₂`.backspace() + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Character value expected but empty string found in json", `in₂`) + } else java.lang.Character.valueOf(`c₂`.charAt(0)) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _c3 = { + val `c₃`: java.lang.String = `in₂`.expectString() + if (`c₃`.==(null)) null else if (`c₃`.length().==(0)) { + `in₂`.backspace() + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Character value expected but empty string found in json", `in₂`) + } else java.lang.Character.valueOf(`c₃`.charAt(0)) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c3"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJChar_fields) + } + if (required.&(7).==(0)) new co.blocke.scalajack.json.primitives.SampleJChar(_c1, _c2, _c3) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("c1", "c2", "c3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(7)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJChar] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJChar, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJChar = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJChar]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleJDouble_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4", "d5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleJDouble, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("d1") + out.value(in.d1) + out.label("d2") + out.value(in.d2) + out.label("d3") + out.value(in.d3) + out.label("d4") + out.value(in.d4) + out.label("d5") + out.value(in.d5) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJDouble = { + var _d1: java.lang.Double = java.lang.Double.valueOf(0.0) + var _d2: java.lang.Double = java.lang.Double.valueOf(0.0) + var _d3: java.lang.Double = java.lang.Double.valueOf(0.0) + var _d4: java.lang.Double = java.lang.Double.valueOf(0.0) + var _d5: java.lang.Double = java.lang.Double.valueOf(0.0) + var required: scala.Int = 31 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJDouble_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJDouble] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _d1 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Double.valueOf(n) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _d2 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Double.valueOf(`n₂`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _d3 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Double.valueOf(`n₃`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _d4 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Double.valueOf(`n₄`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _d5 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Double.valueOf(`n₅`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d5"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJDouble_fields) + } + if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJDouble(_d1, _d2, _d3, _d4, _d5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4", "d5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJDouble] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJDouble, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJDouble = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJDouble]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleJFloat_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("f1", "f2", "f3", "f4", "f5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleJFloat, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("f1") + out.value(in.f1) + out.label("f2") + out.value(in.f2) + out.label("f3") + out.value(in.f3) + out.label("f4") + out.value(in.f4) + out.label("f5") + out.value(in.f5) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJFloat = { + var _f1: java.lang.Float = java.lang.Float.valueOf(0.0f) + var _f2: java.lang.Float = java.lang.Float.valueOf(0.0f) + var _f3: java.lang.Float = java.lang.Float.valueOf(0.0f) + var _f4: java.lang.Float = java.lang.Float.valueOf(0.0f) + var _f5: java.lang.Float = java.lang.Float.valueOf(0.0f) + var required: scala.Int = 31 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJFloat_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJFloat] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _f1 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Float.valueOf(n) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _f2 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Float.valueOf(`n₂`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _f3 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Float.valueOf(`n₃`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _f4 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Float.valueOf(`n₄`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _f5 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Float.valueOf(`n₅`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f5"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJFloat_fields) + } + if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJFloat(_f1, _f2, _f3, _f4, _f5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("f1", "f2", "f3", "f4", "f5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJFloat] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJFloat, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJFloat = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJFloat]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleJInt_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("i1", "i2", "i3", "i4", "i5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleJInt, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("i1") + out.value(in.i1) + out.label("i2") + out.value(in.i2) + out.label("i3") + out.value(in.i3) + out.label("i4") + out.value(in.i4) + out.label("i5") + out.value(in.i5) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJInt = { + var _i1: java.lang.Integer = java.lang.Integer.valueOf(0) + var _i2: java.lang.Integer = java.lang.Integer.valueOf(0) + var _i3: java.lang.Integer = java.lang.Integer.valueOf(0) + var _i4: java.lang.Integer = java.lang.Integer.valueOf(0) + var _i5: java.lang.Integer = java.lang.Integer.valueOf(0) + var required: scala.Int = 31 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJInt_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJInt] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _i1 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Integer.valueOf(n) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _i2 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Integer.valueOf(`n₂`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _i3 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Integer.valueOf(`n₃`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _i4 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Integer.valueOf(`n₄`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _i5 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Integer.valueOf(`n₅`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i5"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJInt_fields) + } + if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJInt(_i1, _i2, _i3, _i4, _i5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("i1", "i2", "i3", "i4", "i5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJInt] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJInt, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJInt = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJInt]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleJInt_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("i1", "i2", "i3", "i4", "i5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleJInt, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("i1") + out.value(in.i1) + out.label("i2") + out.value(in.i2) + out.label("i3") + out.value(in.i3) + out.label("i4") + out.value(in.i4) + out.label("i5") + out.value(in.i5) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJInt = { + var _i1: java.lang.Integer = java.lang.Integer.valueOf(0) + var _i2: java.lang.Integer = java.lang.Integer.valueOf(0) + var _i3: java.lang.Integer = java.lang.Integer.valueOf(0) + var _i4: java.lang.Integer = java.lang.Integer.valueOf(0) + var _i5: java.lang.Integer = java.lang.Integer.valueOf(0) + var required: scala.Int = 31 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJInt_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJInt] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _i1 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Integer.valueOf(n) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _i2 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Integer.valueOf(`n₂`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _i3 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Integer.valueOf(`n₃`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _i4 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Integer.valueOf(`n₄`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _i5 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Integer.valueOf(`n₅`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i5"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJInt_fields) + } + if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJInt(_i1, _i2, _i3, _i4, _i5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("i1", "i2", "i3", "i4", "i5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJInt] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJInt, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJInt = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJInt]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleJLong_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("l1", "l2", "l3", "l4", "l5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleJLong, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("l1") + out.value(in.l1) + out.label("l2") + out.value(in.l2) + out.label("l3") + out.value(in.l3) + out.label("l4") + out.value(in.l4) + out.label("l5") + out.value(in.l5) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJLong = { + var _l1: java.lang.Long = java.lang.Long.valueOf(0L) + var _l2: java.lang.Long = java.lang.Long.valueOf(0L) + var _l3: java.lang.Long = java.lang.Long.valueOf(0L) + var _l4: java.lang.Long = java.lang.Long.valueOf(0L) + var _l5: java.lang.Long = java.lang.Long.valueOf(0L) + var required: scala.Int = 31 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJLong_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJLong] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _l1 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Long.valueOf(n) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _l2 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Long.valueOf(`n₂`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _l3 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Long.valueOf(`n₃`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _l4 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Long.valueOf(`n₄`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _l5 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Long.valueOf(`n₅`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l5"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJLong_fields) + } + if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJLong(_l1, _l2, _l3, _l4, _l5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("l1", "l2", "l3", "l4", "l5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJLong] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJLong, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJLong = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJLong]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleJLong_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("l1", "l2", "l3", "l4", "l5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleJLong, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("l1") + out.value(in.l1) + out.label("l2") + out.value(in.l2) + out.label("l3") + out.value(in.l3) + out.label("l4") + out.value(in.l4) + out.label("l5") + out.value(in.l5) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJLong = { + var _l1: java.lang.Long = java.lang.Long.valueOf(0L) + var _l2: java.lang.Long = java.lang.Long.valueOf(0L) + var _l3: java.lang.Long = java.lang.Long.valueOf(0L) + var _l4: java.lang.Long = java.lang.Long.valueOf(0L) + var _l5: java.lang.Long = java.lang.Long.valueOf(0L) + var required: scala.Int = 31 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJLong_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJLong] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _l1 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Long.valueOf(n) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _l2 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Long.valueOf(`n₂`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _l3 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Long.valueOf(`n₃`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _l4 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Long.valueOf(`n₄`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _l5 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Long.valueOf(`n₅`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l5"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJLong_fields) + } + if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJLong(_l1, _l2, _l3, _l4, _l5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("l1", "l2", "l3", "l4", "l5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJLong] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJLong, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJLong = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJLong]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleJNumber_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("n1", "n2", "n3", "n4", "n5", "n6", "n7", "n8", "n9", "n10", "n11", "n12", "n13", "n14", "n15", "n16", "n17")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleJNumber, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("n1") + out.value(in.n1) + out.label("n2") + out.value(in.n2) + out.label("n3") + out.value(in.n3) + out.label("n4") + out.value(in.n4) + out.label("n5") + out.value(in.n5) + out.label("n6") + out.value(in.n6) + out.label("n7") + out.value(in.n7) + out.label("n8") + out.value(in.n8) + out.label("n9") + out.value(in.n9) + out.label("n10") + out.value(in.n10) + out.label("n11") + out.value(in.n11) + out.label("n12") + out.value(in.n12) + out.label("n13") + out.value(in.n13) + out.label("n14") + out.value(in.n14) + out.label("n15") + out.value(in.n15) + out.label("n16") + out.value(in.n16) + out.label("n17") + out.value(in.n17) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJNumber = { + var _n1: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n2: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n3: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n4: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n5: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n6: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n7: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n8: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n9: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n10: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n11: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n12: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n13: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n14: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n15: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n16: java.lang.Number = null.asInstanceOf[java.lang.Number] + var _n17: java.lang.Number = null.asInstanceOf[java.lang.Number] + var required: scala.Int = 131071 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJNumber_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJNumber] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _n1 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(n) match { + case d if d.isValidByte => + java.lang.Byte.valueOf(d.toByteExact) + case d if `d₂`.isValidShort => + java.lang.Short.valueOf(`d₂`.toShortExact) + case d if `d₃`.isValidInt => + java.lang.Integer.valueOf(`d₃`.toIntExact) + case d if `d₄`.isValidLong => + java.lang.Long.valueOf(`d₄`.toLongExact) + case d if `d₅`.isDecimalFloat => + java.lang.Float.valueOf(`d₅`.toFloat) + case d if `d₆`.isDecimalDouble => + java.lang.Double.valueOf(`d₆`.toDouble) + case d => + (`d₇`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _n2 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₂`) match { + case d if `d₈`.isValidByte => + java.lang.Byte.valueOf(`d₈`.toByteExact) + case d if `d₉`.isValidShort => + java.lang.Short.valueOf(`d₉`.toShortExact) + case d if `d₁₀`.isValidInt => + java.lang.Integer.valueOf(`d₁₀`.toIntExact) + case d if `d₁₁`.isValidLong => + java.lang.Long.valueOf(`d₁₁`.toLongExact) + case d if `d₁₂`.isDecimalFloat => + java.lang.Float.valueOf(`d₁₂`.toFloat) + case d if `d₁₃`.isDecimalDouble => + java.lang.Double.valueOf(`d₁₃`.toDouble) + case d => + (`d₁₄`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _n3 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₃`) match { + case d if `d₁₅`.isValidByte => + java.lang.Byte.valueOf(`d₁₅`.toByteExact) + case d if `d₁₆`.isValidShort => + java.lang.Short.valueOf(`d₁₆`.toShortExact) + case d if `d₁₇`.isValidInt => + java.lang.Integer.valueOf(`d₁₇`.toIntExact) + case d if `d₁₈`.isValidLong => + java.lang.Long.valueOf(`d₁₈`.toLongExact) + case d if `d₁₉`.isDecimalFloat => + java.lang.Float.valueOf(`d₁₉`.toFloat) + case d if `d₂₀`.isDecimalDouble => + java.lang.Double.valueOf(`d₂₀`.toDouble) + case d => + (`d₂₁`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _n4 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₄`) match { + case d if `d₂₂`.isValidByte => + java.lang.Byte.valueOf(`d₂₂`.toByteExact) + case d if `d₂₃`.isValidShort => + java.lang.Short.valueOf(`d₂₃`.toShortExact) + case d if `d₂₄`.isValidInt => + java.lang.Integer.valueOf(`d₂₄`.toIntExact) + case d if `d₂₅`.isValidLong => + java.lang.Long.valueOf(`d₂₅`.toLongExact) + case d if `d₂₆`.isDecimalFloat => + java.lang.Float.valueOf(`d₂₆`.toFloat) + case d if `d₂₇`.isDecimalDouble => + java.lang.Double.valueOf(`d₂₇`.toDouble) + case d => + (`d₂₈`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _n5 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₅`) match { + case d if `d₂₉`.isValidByte => + java.lang.Byte.valueOf(`d₂₉`.toByteExact) + case d if `d₃₀`.isValidShort => + java.lang.Short.valueOf(`d₃₀`.toShortExact) + case d if `d₃₁`.isValidInt => + java.lang.Integer.valueOf(`d₃₁`.toIntExact) + case d if `d₃₂`.isValidLong => + java.lang.Long.valueOf(`d₃₂`.toLongExact) + case d if `d₃₃`.isDecimalFloat => + java.lang.Float.valueOf(`d₃₃`.toFloat) + case d if `d₃₄`.isDecimalDouble => + java.lang.Double.valueOf(`d₃₄`.toDouble) + case d => + (`d₃₅`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n5"), `in₂`) + case 5 => + if (required.&(32).!=(0)) { + required = required.^(32) + _n6 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₆`) match { + case d if `d₃₆`.isValidByte => + java.lang.Byte.valueOf(`d₃₆`.toByteExact) + case d if `d₃₇`.isValidShort => + java.lang.Short.valueOf(`d₃₇`.toShortExact) + case d if `d₃₈`.isValidInt => + java.lang.Integer.valueOf(`d₃₈`.toIntExact) + case d if `d₃₉`.isValidLong => + java.lang.Long.valueOf(`d₃₉`.toLongExact) + case d if `d₄₀`.isDecimalFloat => + java.lang.Float.valueOf(`d₄₀`.toFloat) + case d if `d₄₁`.isDecimalDouble => + java.lang.Double.valueOf(`d₄₁`.toDouble) + case d => + (`d₄₂`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n6"), `in₂`) + case 6 => + if (required.&(64).!=(0)) { + required = required.^(64) + _n7 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₇`) match { + case d if `d₄₃`.isValidByte => + java.lang.Byte.valueOf(`d₄₃`.toByteExact) + case d if `d₄₄`.isValidShort => + java.lang.Short.valueOf(`d₄₄`.toShortExact) + case d if `d₄₅`.isValidInt => + java.lang.Integer.valueOf(`d₄₅`.toIntExact) + case d if `d₄₆`.isValidLong => + java.lang.Long.valueOf(`d₄₆`.toLongExact) + case d if `d₄₇`.isDecimalFloat => + java.lang.Float.valueOf(`d₄₇`.toFloat) + case d if `d₄₈`.isDecimalDouble => + java.lang.Double.valueOf(`d₄₈`.toDouble) + case d => + (`d₄₉`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n7"), `in₂`) + case 7 => + if (required.&(128).!=(0)) { + required = required.^(128) + _n8 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₈`) match { + case d if `d₅₀`.isValidByte => + java.lang.Byte.valueOf(`d₅₀`.toByteExact) + case d if `d₅₁`.isValidShort => + java.lang.Short.valueOf(`d₅₁`.toShortExact) + case d if `d₅₂`.isValidInt => + java.lang.Integer.valueOf(`d₅₂`.toIntExact) + case d if `d₅₃`.isValidLong => + java.lang.Long.valueOf(`d₅₃`.toLongExact) + case d if `d₅₄`.isDecimalFloat => + java.lang.Float.valueOf(`d₅₄`.toFloat) + case d if `d₅₅`.isDecimalDouble => + java.lang.Double.valueOf(`d₅₅`.toDouble) + case d => + (`d₅₆`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n8"), `in₂`) + case 8 => + if (required.&(256).!=(0)) { + required = required.^(256) + _n9 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₉`) match { + case d if `d₅₇`.isValidByte => + java.lang.Byte.valueOf(`d₅₇`.toByteExact) + case d if `d₅₈`.isValidShort => + java.lang.Short.valueOf(`d₅₈`.toShortExact) + case d if `d₅₉`.isValidInt => + java.lang.Integer.valueOf(`d₅₉`.toIntExact) + case d if `d₆₀`.isValidLong => + java.lang.Long.valueOf(`d₆₀`.toLongExact) + case d if `d₆₁`.isDecimalFloat => + java.lang.Float.valueOf(`d₆₁`.toFloat) + case d if `d₆₂`.isDecimalDouble => + java.lang.Double.valueOf(`d₆₂`.toDouble) + case d => + (`d₆₃`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n9"), `in₂`) + case 9 => + if (required.&(512).!=(0)) { + required = required.^(512) + _n10 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₁₀`) match { + case d if `d₆₄`.isValidByte => + java.lang.Byte.valueOf(`d₆₄`.toByteExact) + case d if `d₆₅`.isValidShort => + java.lang.Short.valueOf(`d₆₅`.toShortExact) + case d if `d₆₆`.isValidInt => + java.lang.Integer.valueOf(`d₆₆`.toIntExact) + case d if `d₆₇`.isValidLong => + java.lang.Long.valueOf(`d₆₇`.toLongExact) + case d if `d₆₈`.isDecimalFloat => + java.lang.Float.valueOf(`d₆₈`.toFloat) + case d if `d₆₉`.isDecimalDouble => + java.lang.Double.valueOf(`d₆₉`.toDouble) + case d => + (`d₇₀`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n10"), `in₂`) + case 10 => + if (required.&(1024).!=(0)) { + required = required.^(1024) + _n11 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₁₁`) match { + case d if `d₇₁`.isValidByte => + java.lang.Byte.valueOf(`d₇₁`.toByteExact) + case d if `d₇₂`.isValidShort => + java.lang.Short.valueOf(`d₇₂`.toShortExact) + case d if `d₇₃`.isValidInt => + java.lang.Integer.valueOf(`d₇₃`.toIntExact) + case d if `d₇₄`.isValidLong => + java.lang.Long.valueOf(`d₇₄`.toLongExact) + case d if `d₇₅`.isDecimalFloat => + java.lang.Float.valueOf(`d₇₅`.toFloat) + case d if `d₇₆`.isDecimalDouble => + java.lang.Double.valueOf(`d₇₆`.toDouble) + case d => + (`d₇₇`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n11"), `in₂`) + case 11 => + if (required.&(2048).!=(0)) { + required = required.^(2048) + _n12 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₁₂`) match { + case d if `d₇₈`.isValidByte => + java.lang.Byte.valueOf(`d₇₈`.toByteExact) + case d if `d₇₉`.isValidShort => + java.lang.Short.valueOf(`d₇₉`.toShortExact) + case d if `d₈₀`.isValidInt => + java.lang.Integer.valueOf(`d₈₀`.toIntExact) + case d if `d₈₁`.isValidLong => + java.lang.Long.valueOf(`d₈₁`.toLongExact) + case d if `d₈₂`.isDecimalFloat => + java.lang.Float.valueOf(`d₈₂`.toFloat) + case d if `d₈₃`.isDecimalDouble => + java.lang.Double.valueOf(`d₈₃`.toDouble) + case d => + (`d₈₄`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n12"), `in₂`) + case 12 => + if (required.&(4096).!=(0)) { + required = required.^(4096) + _n13 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₁₃`) match { + case d if `d₈₅`.isValidByte => + java.lang.Byte.valueOf(`d₈₅`.toByteExact) + case d if `d₈₆`.isValidShort => + java.lang.Short.valueOf(`d₈₆`.toShortExact) + case d if `d₈₇`.isValidInt => + java.lang.Integer.valueOf(`d₈₇`.toIntExact) + case d if `d₈₈`.isValidLong => + java.lang.Long.valueOf(`d₈₈`.toLongExact) + case d if `d₈₉`.isDecimalFloat => + java.lang.Float.valueOf(`d₈₉`.toFloat) + case d if `d₉₀`.isDecimalDouble => + java.lang.Double.valueOf(`d₉₀`.toDouble) + case d => + (`d₉₁`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n13"), `in₂`) + case 13 => + if (required.&(8192).!=(0)) { + required = required.^(8192) + _n14 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₁₄`) match { + case d if `d₉₂`.isValidByte => + java.lang.Byte.valueOf(`d₉₂`.toByteExact) + case d if `d₉₃`.isValidShort => + java.lang.Short.valueOf(`d₉₃`.toShortExact) + case d if `d₉₄`.isValidInt => + java.lang.Integer.valueOf(`d₉₄`.toIntExact) + case d if `d₉₅`.isValidLong => + java.lang.Long.valueOf(`d₉₅`.toLongExact) + case d if `d₉₆`.isDecimalFloat => + java.lang.Float.valueOf(`d₉₆`.toFloat) + case d if `d₉₇`.isDecimalDouble => + java.lang.Double.valueOf(`d₉₇`.toDouble) + case d => + (`d₉₈`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n14"), `in₂`) + case 14 => + if (required.&(16384).!=(0)) { + required = required.^(16384) + _n15 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₁₅`) match { + case d if `d₉₉`.isValidByte => + java.lang.Byte.valueOf(`d₉₉`.toByteExact) + case d if `d₁₀₀`.isValidShort => + java.lang.Short.valueOf(`d₁₀₀`.toShortExact) + case d if `d₁₀₁`.isValidInt => + java.lang.Integer.valueOf(`d₁₀₁`.toIntExact) + case d if `d₁₀₂`.isValidLong => + java.lang.Long.valueOf(`d₁₀₂`.toLongExact) + case d if `d₁₀₃`.isDecimalFloat => + java.lang.Float.valueOf(`d₁₀₃`.toFloat) + case d if `d₁₀₄`.isDecimalDouble => + java.lang.Double.valueOf(`d₁₀₄`.toDouble) + case d => + (`d₁₀₅`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n15"), `in₂`) + case 15 => + if (required.&(32768).!=(0)) { + required = required.^(32768) + _n16 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₁₆`) match { + case d if `d₁₀₆`.isValidByte => + java.lang.Byte.valueOf(`d₁₀₆`.toByteExact) + case d if `d₁₀₇`.isValidShort => + java.lang.Short.valueOf(`d₁₀₇`.toShortExact) + case d if `d₁₀₈`.isValidInt => + java.lang.Integer.valueOf(`d₁₀₈`.toIntExact) + case d if `d₁₀₉`.isValidLong => + java.lang.Long.valueOf(`d₁₀₉`.toLongExact) + case d if `d₁₁₀`.isDecimalFloat => + java.lang.Float.valueOf(`d₁₁₀`.toFloat) + case d if `d₁₁₁`.isDecimalDouble => + java.lang.Double.valueOf(`d₁₁₁`.toDouble) + case d => + (`d₁₁₂`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n16"), `in₂`) + case 16 => + if (required.&(65536).!=(0)) { + required = required.^(65536) + _n17 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + scala.math.BigDecimal.apply(`n₁₇`) match { + case d if `d₁₁₃`.isValidByte => + java.lang.Byte.valueOf(`d₁₁₃`.toByteExact) + case d if `d₁₁₄`.isValidShort => + java.lang.Short.valueOf(`d₁₁₄`.toShortExact) + case d if `d₁₁₅`.isValidInt => + java.lang.Integer.valueOf(`d₁₁₅`.toIntExact) + case d if `d₁₁₆`.isValidLong => + java.lang.Long.valueOf(`d₁₁₆`.toLongExact) + case d if `d₁₁₇`.isDecimalFloat => + java.lang.Float.valueOf(`d₁₁₇`.toFloat) + case d if `d₁₁₈`.isDecimalDouble => + java.lang.Double.valueOf(`d₁₁₈`.toDouble) + case d => + (`d₁₁₉`: scala.math.BigDecimal) + } + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n17"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJNumber_fields) + } + if (required.&(131071).==(0)) new co.blocke.scalajack.json.primitives.SampleJNumber(_n1, _n2, _n3, _n4, _n5, _n6, _n7, _n8, _n9, _n10, _n11, _n12, _n13, _n14, _n15, _n16, _n17) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("n1", "n2", "n3", "n4", "n5", "n6", "n7", "n8", "n9", "n10", "n11", "n12", "n13", "n14", "n15", "n16", "n17")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(131071)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJNumber] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJNumber, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJNumber = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJNumber]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleJByte_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("b1", "b2", "b3", "b4", "b5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleJByte, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("b1") + out.value(in.b1) + out.label("b2") + out.value(in.b2) + out.label("b3") + out.value(in.b3) + out.label("b4") + out.value(in.b4) + out.label("b5") + out.value(in.b5) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJByte = { + var _b1: java.lang.Byte = scala.Predef.byte2Byte(java.lang.Byte.MIN_VALUE) + var _b2: java.lang.Byte = scala.Predef.byte2Byte(java.lang.Byte.MIN_VALUE) + var _b3: java.lang.Byte = scala.Predef.byte2Byte(java.lang.Byte.MIN_VALUE) + var _b4: java.lang.Byte = scala.Predef.byte2Byte(java.lang.Byte.MIN_VALUE) + var _b5: java.lang.Byte = scala.Predef.byte2Byte(java.lang.Byte.MIN_VALUE) + var required: scala.Int = 31 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJByte_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJByte] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _b1 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Byte.valueOf(n) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _b2 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Byte.valueOf(`n₂`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _b3 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Byte.valueOf(`n₃`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _b4 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Byte.valueOf(`n₄`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _b5 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Byte.valueOf(`n₅`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b5"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJByte_fields) + } + if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJByte(_b1, _b2, _b3, _b4, _b5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("b1", "b2", "b3", "b4", "b5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJByte] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJByte, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJByte = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJByte]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleJShort_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("s1", "s2", "s3", "s4", "s5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleJShort, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("s1") + out.value(in.s1) + out.label("s2") + out.value(in.s2) + out.label("s3") + out.value(in.s3) + out.label("s4") + out.value(in.s4) + out.label("s5") + out.value(in.s5) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJShort = { + var _s1: java.lang.Short = java.lang.Short.valueOf(0.toShort) + var _s2: java.lang.Short = java.lang.Short.valueOf(0.toShort) + var _s3: java.lang.Short = java.lang.Short.valueOf(0.toShort) + var _s4: java.lang.Short = java.lang.Short.valueOf(0.toShort) + var _s5: java.lang.Short = java.lang.Short.valueOf(0.toShort) + var required: scala.Int = 31 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJShort_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJShort] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _s1 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Short.valueOf(n) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _s2 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Short.valueOf(`n₂`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _s3 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Short.valueOf(`n₃`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _s4 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Short.valueOf(`n₄`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _s5 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Short.valueOf(`n₅`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s5"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJShort_fields) + } + if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJShort(_s1, _s2, _s3, _s4, _s5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("s1", "s2", "s3", "s4", "s5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJShort] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJShort, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJShort = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJShort]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleJShort_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("s1", "s2", "s3", "s4", "s5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleJShort, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("s1") + out.value(in.s1) + out.label("s2") + out.value(in.s2) + out.label("s3") + out.value(in.s3) + out.label("s4") + out.value(in.s4) + out.label("s5") + out.value(in.s5) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJShort = { + var _s1: java.lang.Short = java.lang.Short.valueOf(0.toShort) + var _s2: java.lang.Short = java.lang.Short.valueOf(0.toShort) + var _s3: java.lang.Short = java.lang.Short.valueOf(0.toShort) + var _s4: java.lang.Short = java.lang.Short.valueOf(0.toShort) + var _s5: java.lang.Short = java.lang.Short.valueOf(0.toShort) + var required: scala.Int = 31 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJShort_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJShort] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _s1 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Short.valueOf(n) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _s2 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Short.valueOf(`n₂`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _s3 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Short.valueOf(`n₃`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _s4 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Short.valueOf(`n₄`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _s5 = `in₂`.expectNumberOrNull() match { + case null => + null + case n => + java.lang.Short.valueOf(`n₅`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s5"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJShort_fields) + } + if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJShort(_s1, _s2, _s3, _s4, _s5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("s1", "s2", "s3", "s4", "s5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJShort] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJShort, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJShort = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJShort]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleBigDecimal_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("bd1", "bd2", "bd3", "bd4", "bd5", "bd6")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleBigDecimal, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("bd1") + out.value(in.bd1) + out.label("bd2") + out.value(in.bd2) + out.label("bd3") + out.value(in.bd3) + out.label("bd4") + out.value(in.bd4) + out.label("bd5") + out.value(in.bd5) + out.label("bd6") + out.value(in.bd6) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleBigDecimal = { + var _bd1: scala.math.BigDecimal = scala.math.BigDecimal.apply(0) + var _bd2: scala.math.BigDecimal = scala.math.BigDecimal.apply(0) + var _bd3: scala.math.BigDecimal = scala.math.BigDecimal.apply(0) + var _bd4: scala.math.BigDecimal = scala.math.BigDecimal.apply(0) + var _bd5: scala.math.BigDecimal = scala.math.BigDecimal.apply(0) + var _bd6: scala.math.BigDecimal = scala.math.BigDecimal.apply(0) + var required: scala.Int = 63 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleBigDecimal_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleBigDecimal] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _bd1 = `in₂`.expectNumberOrNull() match { + case null => + null + case s => + scala.math.BigDecimal.apply(s) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _bd2 = `in₂`.expectNumberOrNull() match { + case null => + null + case s => + scala.math.BigDecimal.apply(`s₂`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _bd3 = `in₂`.expectNumberOrNull() match { + case null => + null + case s => + scala.math.BigDecimal.apply(`s₃`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _bd4 = `in₂`.expectNumberOrNull() match { + case null => + null + case s => + scala.math.BigDecimal.apply(`s₄`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _bd5 = `in₂`.expectNumberOrNull() match { + case null => + null + case s => + scala.math.BigDecimal.apply(`s₅`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd5"), `in₂`) + case 5 => + if (required.&(32).!=(0)) { + required = required.^(32) + _bd6 = `in₂`.expectNumberOrNull() match { + case null => + null + case s => + scala.math.BigDecimal.apply(`s₆`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd6"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleBigDecimal_fields) + } + if (required.&(63).==(0)) new co.blocke.scalajack.json.primitives.SampleBigDecimal(_bd1, _bd2, _bd3, _bd4, _bd5, _bd6) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("bd1", "bd2", "bd3", "bd4", "bd5", "bd6")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(63)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleBigDecimal] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleBigDecimal, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleBigDecimal = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleBigDecimal]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleBigInt_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("bi1", "bi2", "bi3", "bi4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleBigInt, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("bi1") + out.value(in.bi1) + out.label("bi2") + out.value(in.bi2) + out.label("bi3") + out.value(in.bi3) + out.label("bi4") + out.value(in.bi4) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleBigInt = { + var _bi1: scala.math.BigInt = scala.math.BigInt.apply(0) + var _bi2: scala.math.BigInt = scala.math.BigInt.apply(0) + var _bi3: scala.math.BigInt = scala.math.BigInt.apply(0) + var _bi4: scala.math.BigInt = scala.math.BigInt.apply(0) + var required: scala.Int = 15 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleBigInt_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleBigInt] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _bi1 = `in₂`.expectNumberOrNull() match { + case null => + null + case s => + scala.math.BigInt.apply(s) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _bi2 = `in₂`.expectNumberOrNull() match { + case null => + null + case s => + scala.math.BigInt.apply(`s₂`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _bi3 = `in₂`.expectNumberOrNull() match { + case null => + null + case s => + scala.math.BigInt.apply(`s₃`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _bi4 = `in₂`.expectNumberOrNull() match { + case null => + null + case s => + scala.math.BigInt.apply(`s₄`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi4"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleBigInt_fields) + } + if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleBigInt(_bi1, _bi2, _bi3, _bi4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("bi1", "bi2", "bi3", "bi4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleBigInt] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleBigInt, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleBigInt = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleBigInt]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleBoolean_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("bool1", "bool2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleBoolean, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("bool1") + out.value(in.bool1) + out.label("bool2") + out.value(in.bool2) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleBoolean = { + var _bool1: scala.Boolean = false + var _bool2: scala.Boolean = false + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleBoolean_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleBoolean] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _bool1 = `in₂`.expectBoolean() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bool1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _bool2 = `in₂`.expectBoolean() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bool2"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleBoolean_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleBoolean(_bool1, _bool2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("bool1", "bool2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleBoolean] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleBoolean, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleBoolean = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleBoolean]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleByte_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("b1", "b2", "b3", "b4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleByte, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("b1") + out.value(in.b1) + out.label("b2") + out.value(in.b2) + out.label("b3") + out.value(in.b3) + out.label("b4") + out.value(in.b4) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleByte = { + var _b1: scala.Byte = 0.toByte + var _b2: scala.Byte = 0.toByte + var _b3: scala.Byte = 0.toByte + var _b4: scala.Byte = 0.toByte + var required: scala.Int = 15 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleByte_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleByte] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _b1 = `in₂`.expectInt().toByte + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _b2 = `in₂`.expectInt().toByte + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _b3 = `in₂`.expectInt().toByte + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _b4 = `in₂`.expectInt().toByte + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b4"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleByte_fields) + } + if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleByte(_b1, _b2, _b3, _b4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("b1", "b2", "b3", "b4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleByte] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleByte, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleByte = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleByte]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleChar_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("c1", "c2", "c3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleChar, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("c1") + out.value(in.c1) + out.label("c2") + out.value(in.c2) + out.label("c3") + out.value(in.c3) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleChar = { + var _c1: scala.Char = 0.toChar + var _c2: scala.Char = 0.toChar + var _c3: scala.Char = 0.toChar + var required: scala.Int = 7 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleChar_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleChar] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _c1 = `in₂`.expectString() match { + case null => + `in₂`.backspace() + `in₂`.backspace() + `in₂`.backspace() + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Char value cannot be null", `in₂`) + case "" => + `in₂`.backspace() + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Char value expected but empty string found in json", `in₂`) + case c => + c.charAt(0) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _c2 = `in₂`.expectString() match { + case null => + `in₂`.backspace() + `in₂`.backspace() + `in₂`.backspace() + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Char value cannot be null", `in₂`) + case "" => + `in₂`.backspace() + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Char value expected but empty string found in json", `in₂`) + case c => + `c₂`.charAt(0) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _c3 = `in₂`.expectString() match { + case null => + `in₂`.backspace() + `in₂`.backspace() + `in₂`.backspace() + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Char value cannot be null", `in₂`) + case "" => + `in₂`.backspace() + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Char value expected but empty string found in json", `in₂`) + case c => + `c₃`.charAt(0) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c3"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleChar_fields) + } + if (required.&(7).==(0)) new co.blocke.scalajack.json.primitives.SampleChar(_c1, _c2, _c3) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("c1", "c2", "c3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(7)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleChar] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleChar, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleChar = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleChar]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleDouble_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleDouble, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("d1") + out.value(in.d1) + out.label("d2") + out.value(in.d2) + out.label("d3") + out.value(in.d3) + out.label("d4") + out.value(in.d4) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleDouble = { + var _d1: scala.Double = 0.0 + var _d2: scala.Double = 0.0 + var _d3: scala.Double = 0.0 + var _d4: scala.Double = 0.0 + var required: scala.Int = 15 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleDouble_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleDouble] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _d1 = `in₂`.expectDouble() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _d2 = `in₂`.expectDouble() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _d3 = `in₂`.expectDouble() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _d4 = `in₂`.expectDouble() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d4"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleDouble_fields) + } + if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleDouble(_d1, _d2, _d3, _d4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleDouble] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleDouble, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleDouble = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleDouble]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleFloat_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("f1", "f2", "f3", "f4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleFloat, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("f1") + out.value(in.f1) + out.label("f2") + out.value(in.f2) + out.label("f3") + out.value(in.f3) + out.label("f4") + out.value(in.f4) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleFloat = { + var _f1: scala.Float = 0.0f + var _f2: scala.Float = 0.0f + var _f3: scala.Float = 0.0f + var _f4: scala.Float = 0.0f + var required: scala.Int = 15 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleFloat_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleFloat] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _f1 = `in₂`.expectFloat() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _f2 = `in₂`.expectFloat() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _f3 = `in₂`.expectFloat() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _f4 = `in₂`.expectFloat() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f4"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleFloat_fields) + } + if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleFloat(_f1, _f2, _f3, _f4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("f1", "f2", "f3", "f4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleFloat] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleFloat, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleFloat = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleFloat]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleInt_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("i1", "i2", "i3", "i4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleInt, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("i1") + out.value(in.i1) + out.label("i2") + out.value(in.i2) + out.label("i3") + out.value(in.i3) + out.label("i4") + out.value(in.i4) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleInt = { + var _i1: scala.Int = 0 + var _i2: scala.Int = 0 + var _i3: scala.Int = 0 + var _i4: scala.Int = 0 + var required: scala.Int = 15 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleInt_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleInt] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _i1 = `in₂`.expectInt() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _i2 = `in₂`.expectInt() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _i3 = `in₂`.expectInt() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _i4 = `in₂`.expectInt() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i4"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleInt_fields) + } + if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleInt(_i1, _i2, _i3, _i4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("i1", "i2", "i3", "i4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleInt] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleInt, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleInt = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleInt]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleLong_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("l1", "l2", "l3", "l4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleLong, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("l1") + out.value(in.l1) + out.label("l2") + out.value(in.l2) + out.label("l3") + out.value(in.l3) + out.label("l4") + out.value(in.l4) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLong = { + var _l1: scala.Long = 0L + var _l2: scala.Long = 0L + var _l3: scala.Long = 0L + var _l4: scala.Long = 0L + var required: scala.Int = 15 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleLong_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleLong] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _l1 = `in₂`.expectLong() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _l2 = `in₂`.expectLong() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _l3 = `in₂`.expectLong() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _l4 = `in₂`.expectLong() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l4"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleLong_fields) + } + if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleLong(_l1, _l2, _l3, _l4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("l1", "l2", "l3", "l4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLong] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleLong, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLong = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLong]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleShort_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("s1", "s2", "s3", "s4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleShort, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("s1") + out.value(in.s1) + out.label("s2") + out.value(in.s2) + out.label("s3") + out.value(in.s3) + out.label("s4") + out.value(in.s4) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleShort = { + var _s1: scala.Short = 0.toShort + var _s2: scala.Short = 0.toShort + var _s3: scala.Short = 0.toShort + var _s4: scala.Short = 0.toShort + var required: scala.Int = 15 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleShort_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleShort] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _s1 = `in₂`.expectInt().toShort + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _s2 = `in₂`.expectInt().toShort + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _s3 = `in₂`.expectInt().toShort + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _s4 = `in₂`.expectInt().toShort + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s4"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleShort_fields) + } + if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleShort(_s1, _s2, _s3, _s4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("s1", "s2", "s3", "s4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleShort] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleShort, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleShort = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleShort]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleString_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("s1", "s2", "s3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleString, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("s1") + out.valueEscaped(in.s1) + out.label("s2") + out.valueEscaped(in.s2) + out.label("s3") + out.valueEscaped(in.s3) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleString = { + var _s1: java.lang.String = "" + var _s2: java.lang.String = "" + var _s3: java.lang.String = "" + var required: scala.Int = 7 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleString_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleString] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _s1 = `in₂`.expectString() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _s2 = `in₂`.expectString() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _s3 = `in₂`.expectString() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s3"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleString_fields) + } + if (required.&(7).==(0)) new co.blocke.scalajack.json.primitives.SampleString(_s1, _s2, _s3) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("s1", "s2", "s3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(7)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleString] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleString, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleString = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleString]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleBigDecimal_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("bd1", "bd2", "bd3", "bd4", "bd5", "bd6")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleBigDecimal, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("bd1") + out.value(in.bd1) + out.label("bd2") + out.value(in.bd2) + out.label("bd3") + out.value(in.bd3) + out.label("bd4") + out.value(in.bd4) + out.label("bd5") + out.value(in.bd5) + out.label("bd6") + out.value(in.bd6) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleBigDecimal = { + var _bd1: scala.math.BigDecimal = scala.math.BigDecimal.apply(0) + var _bd2: scala.math.BigDecimal = scala.math.BigDecimal.apply(0) + var _bd3: scala.math.BigDecimal = scala.math.BigDecimal.apply(0) + var _bd4: scala.math.BigDecimal = scala.math.BigDecimal.apply(0) + var _bd5: scala.math.BigDecimal = scala.math.BigDecimal.apply(0) + var _bd6: scala.math.BigDecimal = scala.math.BigDecimal.apply(0) + var required: scala.Int = 63 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleBigDecimal_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleBigDecimal] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _bd1 = `in₂`.expectNumberOrNull() match { + case null => + null + case s => + scala.math.BigDecimal.apply(s) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _bd2 = `in₂`.expectNumberOrNull() match { + case null => + null + case s => + scala.math.BigDecimal.apply(`s₂`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _bd3 = `in₂`.expectNumberOrNull() match { + case null => + null + case s => + scala.math.BigDecimal.apply(`s₃`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _bd4 = `in₂`.expectNumberOrNull() match { + case null => + null + case s => + scala.math.BigDecimal.apply(`s₄`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _bd5 = `in₂`.expectNumberOrNull() match { + case null => + null + case s => + scala.math.BigDecimal.apply(`s₅`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd5"), `in₂`) + case 5 => + if (required.&(32).!=(0)) { + required = required.^(32) + _bd6 = `in₂`.expectNumberOrNull() match { + case null => + null + case s => + scala.math.BigDecimal.apply(`s₆`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd6"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleBigDecimal_fields) + } + if (required.&(63).==(0)) new co.blocke.scalajack.json.primitives.SampleBigDecimal(_bd1, _bd2, _bd3, _bd4, _bd5, _bd6) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("bd1", "bd2", "bd3", "bd4", "bd5", "bd6")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(63)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleBigDecimal] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleBigDecimal, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleBigDecimal = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleBigDecimal]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleBigInt_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("bi1", "bi2", "bi3", "bi4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleBigInt, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("bi1") + out.value(in.bi1) + out.label("bi2") + out.value(in.bi2) + out.label("bi3") + out.value(in.bi3) + out.label("bi4") + out.value(in.bi4) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleBigInt = { + var _bi1: scala.math.BigInt = scala.math.BigInt.apply(0) + var _bi2: scala.math.BigInt = scala.math.BigInt.apply(0) + var _bi3: scala.math.BigInt = scala.math.BigInt.apply(0) + var _bi4: scala.math.BigInt = scala.math.BigInt.apply(0) + var required: scala.Int = 15 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleBigInt_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleBigInt] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _bi1 = `in₂`.expectNumberOrNull() match { + case null => + null + case s => + scala.math.BigInt.apply(s) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _bi2 = `in₂`.expectNumberOrNull() match { + case null => + null + case s => + scala.math.BigInt.apply(`s₂`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _bi3 = `in₂`.expectNumberOrNull() match { + case null => + null + case s => + scala.math.BigInt.apply(`s₃`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _bi4 = `in₂`.expectNumberOrNull() match { + case null => + null + case s => + scala.math.BigInt.apply(`s₄`) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi4"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleBigInt_fields) + } + if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleBigInt(_bi1, _bi2, _bi3, _bi4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("bi1", "bi2", "bi3", "bi4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleBigInt] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleBigInt, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleBigInt = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleBigInt]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleBoolean_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("bool1", "bool2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleBoolean, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("bool1") + out.value(in.bool1) + out.label("bool2") + out.value(in.bool2) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleBoolean = { + var _bool1: scala.Boolean = false + var _bool2: scala.Boolean = false + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleBoolean_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleBoolean] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _bool1 = `in₂`.expectBoolean() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bool1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _bool2 = `in₂`.expectBoolean() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bool2"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleBoolean_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleBoolean(_bool1, _bool2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("bool1", "bool2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleBoolean] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleBoolean, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleBoolean = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleBoolean]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleByte_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("b1", "b2", "b3", "b4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleByte, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("b1") + out.value(in.b1) + out.label("b2") + out.value(in.b2) + out.label("b3") + out.value(in.b3) + out.label("b4") + out.value(in.b4) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleByte = { + var _b1: scala.Byte = 0.toByte + var _b2: scala.Byte = 0.toByte + var _b3: scala.Byte = 0.toByte + var _b4: scala.Byte = 0.toByte + var required: scala.Int = 15 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleByte_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleByte] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _b1 = `in₂`.expectInt().toByte + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _b2 = `in₂`.expectInt().toByte + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _b3 = `in₂`.expectInt().toByte + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _b4 = `in₂`.expectInt().toByte + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b4"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleByte_fields) + } + if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleByte(_b1, _b2, _b3, _b4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("b1", "b2", "b3", "b4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleByte] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleByte, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleByte = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleByte]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleChar_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("c1", "c2", "c3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleChar, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("c1") + out.value(in.c1) + out.label("c2") + out.value(in.c2) + out.label("c3") + out.value(in.c3) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleChar = { + var _c1: scala.Char = 0.toChar + var _c2: scala.Char = 0.toChar + var _c3: scala.Char = 0.toChar + var required: scala.Int = 7 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleChar_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleChar] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _c1 = `in₂`.expectString() match { + case null => + `in₂`.backspace() + `in₂`.backspace() + `in₂`.backspace() + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Char value cannot be null", `in₂`) + case "" => + `in₂`.backspace() + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Char value expected but empty string found in json", `in₂`) + case c => + c.charAt(0) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _c2 = `in₂`.expectString() match { + case null => + `in₂`.backspace() + `in₂`.backspace() + `in₂`.backspace() + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Char value cannot be null", `in₂`) + case "" => + `in₂`.backspace() + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Char value expected but empty string found in json", `in₂`) + case c => + `c₂`.charAt(0) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _c3 = `in₂`.expectString() match { + case null => + `in₂`.backspace() + `in₂`.backspace() + `in₂`.backspace() + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Char value cannot be null", `in₂`) + case "" => + `in₂`.backspace() + `in₂`.backspace() + throw co.blocke.scalajack.json.JsonParseError.apply("Char value expected but empty string found in json", `in₂`) + case c => + `c₃`.charAt(0) + } + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c3"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleChar_fields) + } + if (required.&(7).==(0)) new co.blocke.scalajack.json.primitives.SampleChar(_c1, _c2, _c3) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("c1", "c2", "c3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(7)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleChar] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleChar, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleChar = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleChar]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleDouble_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleDouble, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("d1") + out.value(in.d1) + out.label("d2") + out.value(in.d2) + out.label("d3") + out.value(in.d3) + out.label("d4") + out.value(in.d4) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleDouble = { + var _d1: scala.Double = 0.0 + var _d2: scala.Double = 0.0 + var _d3: scala.Double = 0.0 + var _d4: scala.Double = 0.0 + var required: scala.Int = 15 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleDouble_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleDouble] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _d1 = `in₂`.expectDouble() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _d2 = `in₂`.expectDouble() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _d3 = `in₂`.expectDouble() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _d4 = `in₂`.expectDouble() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d4"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleDouble_fields) + } + if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleDouble(_d1, _d2, _d3, _d4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleDouble] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleDouble, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleDouble = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleDouble]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleFloat_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("f1", "f2", "f3", "f4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleFloat, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("f1") + out.value(in.f1) + out.label("f2") + out.value(in.f2) + out.label("f3") + out.value(in.f3) + out.label("f4") + out.value(in.f4) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleFloat = { + var _f1: scala.Float = 0.0f + var _f2: scala.Float = 0.0f + var _f3: scala.Float = 0.0f + var _f4: scala.Float = 0.0f + var required: scala.Int = 15 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleFloat_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleFloat] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _f1 = `in₂`.expectFloat() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _f2 = `in₂`.expectFloat() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _f3 = `in₂`.expectFloat() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _f4 = `in₂`.expectFloat() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f4"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleFloat_fields) + } + if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleFloat(_f1, _f2, _f3, _f4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("f1", "f2", "f3", "f4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleFloat] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleFloat, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleFloat = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleFloat]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleInt_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("i1", "i2", "i3", "i4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleInt, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("i1") + out.value(in.i1) + out.label("i2") + out.value(in.i2) + out.label("i3") + out.value(in.i3) + out.label("i4") + out.value(in.i4) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleInt = { + var _i1: scala.Int = 0 + var _i2: scala.Int = 0 + var _i3: scala.Int = 0 + var _i4: scala.Int = 0 + var required: scala.Int = 15 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleInt_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleInt] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _i1 = `in₂`.expectInt() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _i2 = `in₂`.expectInt() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _i3 = `in₂`.expectInt() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _i4 = `in₂`.expectInt() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i4"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleInt_fields) + } + if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleInt(_i1, _i2, _i3, _i4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("i1", "i2", "i3", "i4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleInt] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleInt, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleInt = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleInt]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleLong_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("l1", "l2", "l3", "l4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleLong, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("l1") + out.value(in.l1) + out.label("l2") + out.value(in.l2) + out.label("l3") + out.value(in.l3) + out.label("l4") + out.value(in.l4) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLong = { + var _l1: scala.Long = 0L + var _l2: scala.Long = 0L + var _l3: scala.Long = 0L + var _l4: scala.Long = 0L + var required: scala.Int = 15 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleLong_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleLong] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _l1 = `in₂`.expectLong() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _l2 = `in₂`.expectLong() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _l3 = `in₂`.expectLong() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _l4 = `in₂`.expectLong() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l4"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleLong_fields) + } + if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleLong(_l1, _l2, _l3, _l4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("l1", "l2", "l3", "l4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLong] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleLong, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLong = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLong]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleShort_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("s1", "s2", "s3", "s4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleShort, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("s1") + out.value(in.s1) + out.label("s2") + out.value(in.s2) + out.label("s3") + out.value(in.s3) + out.label("s4") + out.value(in.s4) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleShort = { + var _s1: scala.Short = 0.toShort + var _s2: scala.Short = 0.toShort + var _s3: scala.Short = 0.toShort + var _s4: scala.Short = 0.toShort + var required: scala.Int = 15 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleShort_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleShort] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _s1 = `in₂`.expectInt().toShort + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _s2 = `in₂`.expectInt().toShort + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _s3 = `in₂`.expectInt().toShort + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _s4 = `in₂`.expectInt().toShort + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s4"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleShort_fields) + } + if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleShort(_s1, _s2, _s3, _s4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("s1", "s2", "s3", "s4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleShort] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleShort, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleShort = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleShort]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleString_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("s1", "s2", "s3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleString, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("s1") + out.valueEscaped(in.s1) + out.label("s2") + out.valueEscaped(in.s2) + out.label("s3") + out.valueEscaped(in.s3) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleString = { + var _s1: java.lang.String = "" + var _s2: java.lang.String = "" + var _s3: java.lang.String = "" + var required: scala.Int = 7 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleString_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleString] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _s1 = `in₂`.expectString() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _s2 = `in₂`.expectString() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _s3 = `in₂`.expectString() + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s3"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleString_fields) + } + if (required.&(7).==(0)) new co.blocke.scalajack.json.primitives.SampleString(_s1, _s2, _s3) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("s1", "s2", "s3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(7)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleString] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleString, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleString = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleString]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleDuration_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("d1", "d2", "d3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleDuration, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("d1") + out.value(in.d1) + out.label("d2") + out.value(in.d2) + out.label("d3") + out.value(in.d3) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleDuration = { + var _d1: java.time.Duration = null + var _d2: java.time.Duration = null + var _d3: java.time.Duration = null + var required: scala.Int = 7 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleDuration_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleDuration] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _d1 = `in₂`.expectString[java.time.Duration](((x$0: java.lang.CharSequence) => java.time.Duration.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _d2 = `in₂`.expectString[java.time.Duration](((`x$0₂`: java.lang.CharSequence) => java.time.Duration.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _d3 = `in₂`.expectString[java.time.Duration](((`x$0₃`: java.lang.CharSequence) => java.time.Duration.parse(`x$0₃`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d3"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleDuration_fields) + } + if (required.&(7).==(0)) new co.blocke.scalajack.json.primitives.SampleDuration(_d1, _d2, _d3) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("d1", "d2", "d3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(7)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleDuration] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleDuration, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleDuration = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleDuration]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleDuration_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("d1", "d2", "d3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleDuration, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("d1") + out.value(in.d1) + out.label("d2") + out.value(in.d2) + out.label("d3") + out.value(in.d3) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleDuration = { + var _d1: java.time.Duration = null + var _d2: java.time.Duration = null + var _d3: java.time.Duration = null + var required: scala.Int = 7 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleDuration_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleDuration] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _d1 = `in₂`.expectString[java.time.Duration](((x$0: java.lang.CharSequence) => java.time.Duration.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _d2 = `in₂`.expectString[java.time.Duration](((`x$0₂`: java.lang.CharSequence) => java.time.Duration.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _d3 = `in₂`.expectString[java.time.Duration](((`x$0₃`: java.lang.CharSequence) => java.time.Duration.parse(`x$0₃`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d3"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleDuration_fields) + } + if (required.&(7).==(0)) new co.blocke.scalajack.json.primitives.SampleDuration(_d1, _d2, _d3) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("d1", "d2", "d3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(7)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleDuration] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleDuration, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleDuration = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleDuration]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleInstant_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("i1", "i2", "i3", "i4", "i5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleInstant, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("i1") + out.value(in.i1) + out.label("i2") + out.value(in.i2) + out.label("i3") + out.value(in.i3) + out.label("i4") + out.value(in.i4) + out.label("i5") + out.value(in.i5) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleInstant = { + var _i1: java.time.Instant = null + var _i2: java.time.Instant = null + var _i3: java.time.Instant = null + var _i4: java.time.Instant = null + var _i5: java.time.Instant = null + var required: scala.Int = 31 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleInstant_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleInstant] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _i1 = `in₂`.expectString[java.time.Instant](((x$0: java.lang.CharSequence) => java.time.Instant.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _i2 = `in₂`.expectString[java.time.Instant](((`x$0₂`: java.lang.CharSequence) => java.time.Instant.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _i3 = `in₂`.expectString[java.time.Instant](((`x$0₃`: java.lang.CharSequence) => java.time.Instant.parse(`x$0₃`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _i4 = `in₂`.expectString[java.time.Instant](((`x$0₄`: java.lang.CharSequence) => java.time.Instant.parse(`x$0₄`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _i5 = `in₂`.expectString[java.time.Instant](((`x$0₅`: java.lang.CharSequence) => java.time.Instant.parse(`x$0₅`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i5"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleInstant_fields) + } + if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleInstant(_i1, _i2, _i3, _i4, _i5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("i1", "i2", "i3", "i4", "i5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleInstant] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleInstant, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleInstant = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleInstant]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleInstant_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("i1", "i2", "i3", "i4", "i5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleInstant, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("i1") + out.value(in.i1) + out.label("i2") + out.value(in.i2) + out.label("i3") + out.value(in.i3) + out.label("i4") + out.value(in.i4) + out.label("i5") + out.value(in.i5) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleInstant = { + var _i1: java.time.Instant = null + var _i2: java.time.Instant = null + var _i3: java.time.Instant = null + var _i4: java.time.Instant = null + var _i5: java.time.Instant = null + var required: scala.Int = 31 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleInstant_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleInstant] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _i1 = `in₂`.expectString[java.time.Instant](((x$0: java.lang.CharSequence) => java.time.Instant.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _i2 = `in₂`.expectString[java.time.Instant](((`x$0₂`: java.lang.CharSequence) => java.time.Instant.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _i3 = `in₂`.expectString[java.time.Instant](((`x$0₃`: java.lang.CharSequence) => java.time.Instant.parse(`x$0₃`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _i4 = `in₂`.expectString[java.time.Instant](((`x$0₄`: java.lang.CharSequence) => java.time.Instant.parse(`x$0₄`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _i5 = `in₂`.expectString[java.time.Instant](((`x$0₅`: java.lang.CharSequence) => java.time.Instant.parse(`x$0₅`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i5"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleInstant_fields) + } + if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleInstant(_i1, _i2, _i3, _i4, _i5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("i1", "i2", "i3", "i4", "i5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleInstant] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleInstant, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleInstant = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleInstant]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleLocalDateTime_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleLocalDateTime, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("d1") + out.value(in.d1) + out.label("d2") + out.value(in.d2) + out.label("d3") + out.value(in.d3) + out.label("d4") + out.value(in.d4) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalDateTime = { + var _d1: java.time.LocalDateTime = null + var _d2: java.time.LocalDateTime = null + var _d3: java.time.LocalDateTime = null + var _d4: java.time.LocalDateTime = null + var required: scala.Int = 15 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleLocalDateTime_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleLocalDateTime] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _d1 = `in₂`.expectString[java.time.LocalDateTime](((x$0: java.lang.CharSequence) => java.time.LocalDateTime.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _d2 = `in₂`.expectString[java.time.LocalDateTime](((`x$0₂`: java.lang.CharSequence) => java.time.LocalDateTime.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _d3 = `in₂`.expectString[java.time.LocalDateTime](((`x$0₃`: java.lang.CharSequence) => java.time.LocalDateTime.parse(`x$0₃`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _d4 = `in₂`.expectString[java.time.LocalDateTime](((`x$0₄`: java.lang.CharSequence) => java.time.LocalDateTime.parse(`x$0₄`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d4"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleLocalDateTime_fields) + } + if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleLocalDateTime(_d1, _d2, _d3, _d4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalDateTime] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleLocalDateTime, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalDateTime = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalDateTime]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleLocalDateTime_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleLocalDateTime, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("d1") + out.value(in.d1) + out.label("d2") + out.value(in.d2) + out.label("d3") + out.value(in.d3) + out.label("d4") + out.value(in.d4) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalDateTime = { + var _d1: java.time.LocalDateTime = null + var _d2: java.time.LocalDateTime = null + var _d3: java.time.LocalDateTime = null + var _d4: java.time.LocalDateTime = null + var required: scala.Int = 15 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleLocalDateTime_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleLocalDateTime] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _d1 = `in₂`.expectString[java.time.LocalDateTime](((x$0: java.lang.CharSequence) => java.time.LocalDateTime.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _d2 = `in₂`.expectString[java.time.LocalDateTime](((`x$0₂`: java.lang.CharSequence) => java.time.LocalDateTime.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _d3 = `in₂`.expectString[java.time.LocalDateTime](((`x$0₃`: java.lang.CharSequence) => java.time.LocalDateTime.parse(`x$0₃`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _d4 = `in₂`.expectString[java.time.LocalDateTime](((`x$0₄`: java.lang.CharSequence) => java.time.LocalDateTime.parse(`x$0₄`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d4"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleLocalDateTime_fields) + } + if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleLocalDateTime(_d1, _d2, _d3, _d4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalDateTime] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleLocalDateTime, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalDateTime = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalDateTime]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleLocalDate_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleLocalDate, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("d1") + out.value(in.d1) + out.label("d2") + out.value(in.d2) + out.label("d3") + out.value(in.d3) + out.label("d4") + out.value(in.d4) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalDate = { + var _d1: java.time.LocalDate = null + var _d2: java.time.LocalDate = null + var _d3: java.time.LocalDate = null + var _d4: java.time.LocalDate = null + var required: scala.Int = 15 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleLocalDate_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleLocalDate] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _d1 = `in₂`.expectString[java.time.LocalDate](((x$0: java.lang.CharSequence) => java.time.LocalDate.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _d2 = `in₂`.expectString[java.time.LocalDate](((`x$0₂`: java.lang.CharSequence) => java.time.LocalDate.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _d3 = `in₂`.expectString[java.time.LocalDate](((`x$0₃`: java.lang.CharSequence) => java.time.LocalDate.parse(`x$0₃`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _d4 = `in₂`.expectString[java.time.LocalDate](((`x$0₄`: java.lang.CharSequence) => java.time.LocalDate.parse(`x$0₄`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d4"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleLocalDate_fields) + } + if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleLocalDate(_d1, _d2, _d3, _d4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalDate] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleLocalDate, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalDate = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalDate]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleLocalDate_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleLocalDate, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("d1") + out.value(in.d1) + out.label("d2") + out.value(in.d2) + out.label("d3") + out.value(in.d3) + out.label("d4") + out.value(in.d4) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalDate = { + var _d1: java.time.LocalDate = null + var _d2: java.time.LocalDate = null + var _d3: java.time.LocalDate = null + var _d4: java.time.LocalDate = null + var required: scala.Int = 15 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleLocalDate_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleLocalDate] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _d1 = `in₂`.expectString[java.time.LocalDate](((x$0: java.lang.CharSequence) => java.time.LocalDate.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _d2 = `in₂`.expectString[java.time.LocalDate](((`x$0₂`: java.lang.CharSequence) => java.time.LocalDate.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _d3 = `in₂`.expectString[java.time.LocalDate](((`x$0₃`: java.lang.CharSequence) => java.time.LocalDate.parse(`x$0₃`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _d4 = `in₂`.expectString[java.time.LocalDate](((`x$0₄`: java.lang.CharSequence) => java.time.LocalDate.parse(`x$0₄`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d4"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleLocalDate_fields) + } + if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleLocalDate(_d1, _d2, _d3, _d4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalDate] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleLocalDate, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalDate = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalDate]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleLocalTime_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4", "d5", "d6")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleLocalTime, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("d1") + out.value(in.d1) + out.label("d2") + out.value(in.d2) + out.label("d3") + out.value(in.d3) + out.label("d4") + out.value(in.d4) + out.label("d5") + out.value(in.d5) + out.label("d6") + out.value(in.d6) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalTime = { + var _d1: java.time.LocalTime = null + var _d2: java.time.LocalTime = null + var _d3: java.time.LocalTime = null + var _d4: java.time.LocalTime = null + var _d5: java.time.LocalTime = null + var _d6: java.time.LocalTime = null + var required: scala.Int = 63 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleLocalTime_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleLocalTime] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _d1 = `in₂`.expectString[java.time.LocalTime](((x$0: java.lang.CharSequence) => java.time.LocalTime.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _d2 = `in₂`.expectString[java.time.LocalTime](((`x$0₂`: java.lang.CharSequence) => java.time.LocalTime.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _d3 = `in₂`.expectString[java.time.LocalTime](((`x$0₃`: java.lang.CharSequence) => java.time.LocalTime.parse(`x$0₃`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _d4 = `in₂`.expectString[java.time.LocalTime](((`x$0₄`: java.lang.CharSequence) => java.time.LocalTime.parse(`x$0₄`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _d5 = `in₂`.expectString[java.time.LocalTime](((`x$0₅`: java.lang.CharSequence) => java.time.LocalTime.parse(`x$0₅`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d5"), `in₂`) + case 5 => + if (required.&(32).!=(0)) { + required = required.^(32) + _d6 = `in₂`.expectString[java.time.LocalTime](((`x$0₆`: java.lang.CharSequence) => java.time.LocalTime.parse(`x$0₆`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d6"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleLocalTime_fields) + } + if (required.&(63).==(0)) new co.blocke.scalajack.json.primitives.SampleLocalTime(_d1, _d2, _d3, _d4, _d5, _d6) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4", "d5", "d6")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(63)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalTime] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleLocalTime, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalTime = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalTime]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleLocalTime_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4", "d5", "d6")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleLocalTime, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("d1") + out.value(in.d1) + out.label("d2") + out.value(in.d2) + out.label("d3") + out.value(in.d3) + out.label("d4") + out.value(in.d4) + out.label("d5") + out.value(in.d5) + out.label("d6") + out.value(in.d6) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalTime = { + var _d1: java.time.LocalTime = null + var _d2: java.time.LocalTime = null + var _d3: java.time.LocalTime = null + var _d4: java.time.LocalTime = null + var _d5: java.time.LocalTime = null + var _d6: java.time.LocalTime = null + var required: scala.Int = 63 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleLocalTime_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleLocalTime] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _d1 = `in₂`.expectString[java.time.LocalTime](((x$0: java.lang.CharSequence) => java.time.LocalTime.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _d2 = `in₂`.expectString[java.time.LocalTime](((`x$0₂`: java.lang.CharSequence) => java.time.LocalTime.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _d3 = `in₂`.expectString[java.time.LocalTime](((`x$0₃`: java.lang.CharSequence) => java.time.LocalTime.parse(`x$0₃`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _d4 = `in₂`.expectString[java.time.LocalTime](((`x$0₄`: java.lang.CharSequence) => java.time.LocalTime.parse(`x$0₄`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _d5 = `in₂`.expectString[java.time.LocalTime](((`x$0₅`: java.lang.CharSequence) => java.time.LocalTime.parse(`x$0₅`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d5"), `in₂`) + case 5 => + if (required.&(32).!=(0)) { + required = required.^(32) + _d6 = `in₂`.expectString[java.time.LocalTime](((`x$0₆`: java.lang.CharSequence) => java.time.LocalTime.parse(`x$0₆`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d6"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleLocalTime_fields) + } + if (required.&(63).==(0)) new co.blocke.scalajack.json.primitives.SampleLocalTime(_d1, _d2, _d3, _d4, _d5, _d6) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4", "d5", "d6")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(63)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalTime] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleLocalTime, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalTime = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalTime]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleMonthDay_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("m1", "m2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleMonthDay, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("m1") + out.value(in.m1) + out.label("m2") + out.value(in.m2) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleMonthDay = { + var _m1: java.time.MonthDay = null + var _m2: java.time.MonthDay = null + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleMonthDay_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleMonthDay] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _m1 = `in₂`.expectString[java.time.MonthDay](((x$0: java.lang.CharSequence) => java.time.MonthDay.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("m1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _m2 = `in₂`.expectString[java.time.MonthDay](((`x$0₂`: java.lang.CharSequence) => java.time.MonthDay.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("m2"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleMonthDay_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleMonthDay(_m1, _m2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("m1", "m2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleMonthDay] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleMonthDay, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleMonthDay = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleMonthDay]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleMonthDay_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("m1", "m2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleMonthDay, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("m1") + out.value(in.m1) + out.label("m2") + out.value(in.m2) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleMonthDay = { + var _m1: java.time.MonthDay = null + var _m2: java.time.MonthDay = null + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleMonthDay_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleMonthDay] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _m1 = `in₂`.expectString[java.time.MonthDay](((x$0: java.lang.CharSequence) => java.time.MonthDay.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("m1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _m2 = `in₂`.expectString[java.time.MonthDay](((`x$0₂`: java.lang.CharSequence) => java.time.MonthDay.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("m2"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleMonthDay_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleMonthDay(_m1, _m2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("m1", "m2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleMonthDay] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleMonthDay, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleMonthDay = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleMonthDay]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleOffsetDateTime_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("o1", "o2", "o3", "o4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleOffsetDateTime, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("o1") + out.value(in.o1) + out.label("o2") + out.value(in.o2) + out.label("o3") + out.value(in.o3) + out.label("o4") + out.value(in.o4) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleOffsetDateTime = { + var _o1: java.time.OffsetDateTime = null + var _o2: java.time.OffsetDateTime = null + var _o3: java.time.OffsetDateTime = null + var _o4: java.time.OffsetDateTime = null + var required: scala.Int = 15 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleOffsetDateTime_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleOffsetDateTime] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _o1 = `in₂`.expectString[java.time.OffsetDateTime](((x$0: java.lang.CharSequence) => java.time.OffsetDateTime.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _o2 = `in₂`.expectString[java.time.OffsetDateTime](((`x$0₂`: java.lang.CharSequence) => java.time.OffsetDateTime.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _o3 = `in₂`.expectString[java.time.OffsetDateTime](((`x$0₃`: java.lang.CharSequence) => java.time.OffsetDateTime.parse(`x$0₃`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _o4 = `in₂`.expectString[java.time.OffsetDateTime](((`x$0₄`: java.lang.CharSequence) => java.time.OffsetDateTime.parse(`x$0₄`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o4"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleOffsetDateTime_fields) + } + if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleOffsetDateTime(_o1, _o2, _o3, _o4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("o1", "o2", "o3", "o4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleOffsetDateTime] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleOffsetDateTime, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleOffsetDateTime = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleOffsetDateTime]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleOffsetDateTime_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("o1", "o2", "o3", "o4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleOffsetDateTime, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("o1") + out.value(in.o1) + out.label("o2") + out.value(in.o2) + out.label("o3") + out.value(in.o3) + out.label("o4") + out.value(in.o4) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleOffsetDateTime = { + var _o1: java.time.OffsetDateTime = null + var _o2: java.time.OffsetDateTime = null + var _o3: java.time.OffsetDateTime = null + var _o4: java.time.OffsetDateTime = null + var required: scala.Int = 15 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleOffsetDateTime_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleOffsetDateTime] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _o1 = `in₂`.expectString[java.time.OffsetDateTime](((x$0: java.lang.CharSequence) => java.time.OffsetDateTime.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _o2 = `in₂`.expectString[java.time.OffsetDateTime](((`x$0₂`: java.lang.CharSequence) => java.time.OffsetDateTime.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _o3 = `in₂`.expectString[java.time.OffsetDateTime](((`x$0₃`: java.lang.CharSequence) => java.time.OffsetDateTime.parse(`x$0₃`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _o4 = `in₂`.expectString[java.time.OffsetDateTime](((`x$0₄`: java.lang.CharSequence) => java.time.OffsetDateTime.parse(`x$0₄`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o4"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleOffsetDateTime_fields) + } + if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleOffsetDateTime(_o1, _o2, _o3, _o4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("o1", "o2", "o3", "o4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleOffsetDateTime] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleOffsetDateTime, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleOffsetDateTime = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleOffsetDateTime]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleOffsetTime_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("o1", "o2", "o3", "o4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleOffsetTime, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("o1") + out.value(in.o1) + out.label("o2") + out.value(in.o2) + out.label("o3") + out.value(in.o3) + out.label("o4") + out.value(in.o4) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleOffsetTime = { + var _o1: java.time.OffsetTime = null + var _o2: java.time.OffsetTime = null + var _o3: java.time.OffsetTime = null + var _o4: java.time.OffsetTime = null + var required: scala.Int = 15 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleOffsetTime_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleOffsetTime] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _o1 = `in₂`.expectString[java.time.OffsetTime](((x$0: java.lang.CharSequence) => java.time.OffsetTime.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _o2 = `in₂`.expectString[java.time.OffsetTime](((`x$0₂`: java.lang.CharSequence) => java.time.OffsetTime.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _o3 = `in₂`.expectString[java.time.OffsetTime](((`x$0₃`: java.lang.CharSequence) => java.time.OffsetTime.parse(`x$0₃`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _o4 = `in₂`.expectString[java.time.OffsetTime](((`x$0₄`: java.lang.CharSequence) => java.time.OffsetTime.parse(`x$0₄`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o4"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleOffsetTime_fields) + } + if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleOffsetTime(_o1, _o2, _o3, _o4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("o1", "o2", "o3", "o4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleOffsetTime] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleOffsetTime, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleOffsetTime = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleOffsetTime]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleOffsetTime_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("o1", "o2", "o3", "o4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleOffsetTime, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("o1") + out.value(in.o1) + out.label("o2") + out.value(in.o2) + out.label("o3") + out.value(in.o3) + out.label("o4") + out.value(in.o4) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleOffsetTime = { + var _o1: java.time.OffsetTime = null + var _o2: java.time.OffsetTime = null + var _o3: java.time.OffsetTime = null + var _o4: java.time.OffsetTime = null + var required: scala.Int = 15 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleOffsetTime_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleOffsetTime] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _o1 = `in₂`.expectString[java.time.OffsetTime](((x$0: java.lang.CharSequence) => java.time.OffsetTime.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _o2 = `in₂`.expectString[java.time.OffsetTime](((`x$0₂`: java.lang.CharSequence) => java.time.OffsetTime.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _o3 = `in₂`.expectString[java.time.OffsetTime](((`x$0₃`: java.lang.CharSequence) => java.time.OffsetTime.parse(`x$0₃`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _o4 = `in₂`.expectString[java.time.OffsetTime](((`x$0₄`: java.lang.CharSequence) => java.time.OffsetTime.parse(`x$0₄`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o4"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleOffsetTime_fields) + } + if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleOffsetTime(_o1, _o2, _o3, _o4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("o1", "o2", "o3", "o4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleOffsetTime] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleOffsetTime, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleOffsetTime = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleOffsetTime]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SamplePeriod_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("p1", "p2", "p3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SamplePeriod, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("p1") + out.value(in.p1) + out.label("p2") + out.value(in.p2) + out.label("p3") + out.value(in.p3) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SamplePeriod = { + var _p1: java.time.Period = null + var _p2: java.time.Period = null + var _p3: java.time.Period = null + var required: scala.Int = 7 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SamplePeriod_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SamplePeriod] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _p1 = `in₂`.expectString[java.time.Period](((x$0: java.lang.CharSequence) => java.time.Period.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("p1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _p2 = `in₂`.expectString[java.time.Period](((`x$0₂`: java.lang.CharSequence) => java.time.Period.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("p2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _p3 = `in₂`.expectString[java.time.Period](((`x$0₃`: java.lang.CharSequence) => java.time.Period.parse(`x$0₃`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("p3"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SamplePeriod_fields) + } + if (required.&(7).==(0)) new co.blocke.scalajack.json.primitives.SamplePeriod(_p1, _p2, _p3) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("p1", "p2", "p3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(7)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SamplePeriod] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SamplePeriod, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SamplePeriod = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SamplePeriod]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SamplePeriod_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("p1", "p2", "p3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SamplePeriod, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("p1") + out.value(in.p1) + out.label("p2") + out.value(in.p2) + out.label("p3") + out.value(in.p3) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SamplePeriod = { + var _p1: java.time.Period = null + var _p2: java.time.Period = null + var _p3: java.time.Period = null + var required: scala.Int = 7 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SamplePeriod_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SamplePeriod] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _p1 = `in₂`.expectString[java.time.Period](((x$0: java.lang.CharSequence) => java.time.Period.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("p1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _p2 = `in₂`.expectString[java.time.Period](((`x$0₂`: java.lang.CharSequence) => java.time.Period.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("p2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _p3 = `in₂`.expectString[java.time.Period](((`x$0₃`: java.lang.CharSequence) => java.time.Period.parse(`x$0₃`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("p3"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SamplePeriod_fields) + } + if (required.&(7).==(0)) new co.blocke.scalajack.json.primitives.SamplePeriod(_p1, _p2, _p3) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("p1", "p2", "p3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(7)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SamplePeriod] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SamplePeriod, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SamplePeriod = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SamplePeriod]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleYear_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("y1", "y2", "y3", "y4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleYear, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("y1") + out.value(in.y1) + out.label("y2") + out.value(in.y2) + out.label("y3") + out.value(in.y3) + out.label("y4") + out.value(in.y4) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleYear = { + var _y1: java.time.Year = null + var _y2: java.time.Year = null + var _y3: java.time.Year = null + var _y4: java.time.Year = null + var required: scala.Int = 15 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleYear_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleYear] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _y1 = `in₂`.expectString[java.time.Year](((x$0: java.lang.CharSequence) => java.time.Year.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _y2 = `in₂`.expectString[java.time.Year](((`x$0₂`: java.lang.CharSequence) => java.time.Year.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _y3 = `in₂`.expectString[java.time.Year](((`x$0₃`: java.lang.CharSequence) => java.time.Year.parse(`x$0₃`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _y4 = `in₂`.expectString[java.time.Year](((`x$0₄`: java.lang.CharSequence) => java.time.Year.parse(`x$0₄`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y4"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleYear_fields) + } + if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleYear(_y1, _y2, _y3, _y4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("y1", "y2", "y3", "y4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleYear] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleYear, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleYear = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleYear]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleYear_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("y1", "y2", "y3", "y4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleYear, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("y1") + out.value(in.y1) + out.label("y2") + out.value(in.y2) + out.label("y3") + out.value(in.y3) + out.label("y4") + out.value(in.y4) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleYear = { + var _y1: java.time.Year = null + var _y2: java.time.Year = null + var _y3: java.time.Year = null + var _y4: java.time.Year = null + var required: scala.Int = 15 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleYear_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleYear] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _y1 = `in₂`.expectString[java.time.Year](((x$0: java.lang.CharSequence) => java.time.Year.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _y2 = `in₂`.expectString[java.time.Year](((`x$0₂`: java.lang.CharSequence) => java.time.Year.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _y3 = `in₂`.expectString[java.time.Year](((`x$0₃`: java.lang.CharSequence) => java.time.Year.parse(`x$0₃`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _y4 = `in₂`.expectString[java.time.Year](((`x$0₄`: java.lang.CharSequence) => java.time.Year.parse(`x$0₄`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y4"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleYear_fields) + } + if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleYear(_y1, _y2, _y3, _y4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("y1", "y2", "y3", "y4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleYear] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleYear, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleYear = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleYear]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleYearMonth_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("y1", "y2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleYearMonth, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("y1") + out.value(in.y1) + out.label("y2") + out.value(in.y2) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleYearMonth = { + var _y1: java.time.YearMonth = null + var _y2: java.time.YearMonth = null + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleYearMonth_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleYearMonth] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _y1 = `in₂`.expectString[java.time.YearMonth](((x$0: java.lang.CharSequence) => java.time.YearMonth.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _y2 = `in₂`.expectString[java.time.YearMonth](((`x$0₂`: java.lang.CharSequence) => java.time.YearMonth.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y2"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleYearMonth_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleYearMonth(_y1, _y2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("y1", "y2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleYearMonth] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleYearMonth, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleYearMonth = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleYearMonth]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleYearMonth_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("y1", "y2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleYearMonth, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("y1") + out.value(in.y1) + out.label("y2") + out.value(in.y2) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleYearMonth = { + var _y1: java.time.YearMonth = null + var _y2: java.time.YearMonth = null + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleYearMonth_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleYearMonth] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _y1 = `in₂`.expectString[java.time.YearMonth](((x$0: java.lang.CharSequence) => java.time.YearMonth.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _y2 = `in₂`.expectString[java.time.YearMonth](((`x$0₂`: java.lang.CharSequence) => java.time.YearMonth.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y2"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleYearMonth_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleYearMonth(_y1, _y2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("y1", "y2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleYearMonth] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleYearMonth, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleYearMonth = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleYearMonth]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleZonedDateTime_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("o1", "o2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleZonedDateTime, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("o1") + out.value(in.o1) + out.label("o2") + out.value(in.o2) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleZonedDateTime = { + var _o1: java.time.ZonedDateTime = null + var _o2: java.time.ZonedDateTime = null + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleZonedDateTime_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleZonedDateTime] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _o1 = `in₂`.expectString[java.time.ZonedDateTime](((x$0: java.lang.CharSequence) => java.time.ZonedDateTime.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _o2 = `in₂`.expectString[java.time.ZonedDateTime](((`x$0₂`: java.lang.CharSequence) => java.time.ZonedDateTime.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o2"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleZonedDateTime_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleZonedDateTime(_o1, _o2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("o1", "o2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleZonedDateTime] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleZonedDateTime, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleZonedDateTime = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleZonedDateTime]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleZonedDateTime_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("o1", "o2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleZonedDateTime, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("o1") + out.value(in.o1) + out.label("o2") + out.value(in.o2) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleZonedDateTime = { + var _o1: java.time.ZonedDateTime = null + var _o2: java.time.ZonedDateTime = null + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleZonedDateTime_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleZonedDateTime] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _o1 = `in₂`.expectString[java.time.ZonedDateTime](((x$0: java.lang.CharSequence) => java.time.ZonedDateTime.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _o2 = `in₂`.expectString[java.time.ZonedDateTime](((`x$0₂`: java.lang.CharSequence) => java.time.ZonedDateTime.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o2"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleZonedDateTime_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleZonedDateTime(_o1, _o2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("o1", "o2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleZonedDateTime] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleZonedDateTime, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleZonedDateTime = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleZonedDateTime]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleZoneId_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("z1", "z2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleZoneId, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("z1") + out.value(in.z1) + out.label("z2") + out.value(in.z2) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleZoneId = { + var _z1: java.time.ZoneId = null + var _z2: java.time.ZoneId = null + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleZoneId_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleZoneId] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _z1 = `in₂`.expectString[java.time.ZoneId](((x$0: java.lang.String) => java.time.ZoneId.of(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("z1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _z2 = `in₂`.expectString[java.time.ZoneId](((`x$0₂`: java.lang.String) => java.time.ZoneId.of(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("z2"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleZoneId_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleZoneId(_z1, _z2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("z1", "z2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleZoneId] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleZoneId, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleZoneId = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleZoneId]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleZoneId_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("z1", "z2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleZoneId, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("z1") + out.value(in.z1) + out.label("z2") + out.value(in.z2) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleZoneId = { + var _z1: java.time.ZoneId = null + var _z2: java.time.ZoneId = null + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleZoneId_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleZoneId] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _z1 = `in₂`.expectString[java.time.ZoneId](((x$0: java.lang.String) => java.time.ZoneId.of(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("z1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _z2 = `in₂`.expectString[java.time.ZoneId](((`x$0₂`: java.lang.String) => java.time.ZoneId.of(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("z2"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleZoneId_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleZoneId(_z1, _z2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("z1", "z2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleZoneId] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleZoneId, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleZoneId = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleZoneId]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleZoneOffset_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("z1", "z2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleZoneOffset, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("z1") + out.value(in.z1) + out.label("z2") + out.value(in.z2) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleZoneOffset = { + var _z1: java.time.ZoneOffset = null + var _z2: java.time.ZoneOffset = null + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleZoneOffset_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleZoneOffset] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _z1 = `in₂`.expectString[java.time.ZoneOffset](((x$0: java.lang.String) => java.time.ZoneOffset.of(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("z1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _z2 = `in₂`.expectString[java.time.ZoneOffset](((`x$0₂`: java.lang.String) => java.time.ZoneOffset.of(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("z2"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleZoneOffset_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleZoneOffset(_z1, _z2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("z1", "z2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleZoneOffset] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleZoneOffset, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleZoneOffset = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleZoneOffset]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleZoneOffset_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("z1", "z2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleZoneOffset, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("z1") + out.value(in.z1) + out.label("z2") + out.value(in.z2) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleZoneOffset = { + var _z1: java.time.ZoneOffset = null + var _z2: java.time.ZoneOffset = null + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleZoneOffset_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleZoneOffset] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _z1 = `in₂`.expectString[java.time.ZoneOffset](((x$0: java.lang.String) => java.time.ZoneOffset.of(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("z1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _z2 = `in₂`.expectString[java.time.ZoneOffset](((`x$0₂`: java.lang.String) => java.time.ZoneOffset.of(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("z2"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleZoneOffset_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleZoneOffset(_z1, _z2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("z1", "z2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleZoneOffset] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleZoneOffset, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleZoneOffset = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleZoneOffset]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleNet_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("u1", "u2", "u3", "u4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleNet, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("u1") + out.value(in.u1) + out.label("u2") + out.value(in.u2) + out.label("u3") + out.value(in.u3) + out.label("u4") + out.value(in.u4) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleNet = { + var _u1: java.net.URL = null + var _u2: java.net.URL = null + var _u3: java.net.URI = null + var _u4: java.net.URI = null + var required: scala.Int = 15 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleNet_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleNet] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _u1 = `in₂`.expectString[java.net.URL](((s: java.lang.String) => new java.net.URL(s))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _u2 = `in₂`.expectString[java.net.URL](((`s₂`: java.lang.String) => new java.net.URL(`s₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _u3 = `in₂`.expectString[java.net.URI](((`s₃`: java.lang.String) => new java.net.URI(`s₃`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _u4 = `in₂`.expectString[java.net.URI](((`s₄`: java.lang.String) => new java.net.URI(`s₄`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u4"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleNet_fields) + } + if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleNet(_u1, _u2, _u3, _u4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("u1", "u2", "u3", "u4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleNet] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleNet, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleNet = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleNet]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleNet_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("u1", "u2", "u3", "u4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleNet, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("u1") + out.value(in.u1) + out.label("u2") + out.value(in.u2) + out.label("u3") + out.value(in.u3) + out.label("u4") + out.value(in.u4) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleNet = { + var _u1: java.net.URL = null + var _u2: java.net.URL = null + var _u3: java.net.URI = null + var _u4: java.net.URI = null + var required: scala.Int = 15 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleNet_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleNet] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _u1 = `in₂`.expectString[java.net.URL](((s: java.lang.String) => new java.net.URL(s))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _u2 = `in₂`.expectString[java.net.URL](((`s₂`: java.lang.String) => new java.net.URL(`s₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _u3 = `in₂`.expectString[java.net.URI](((`s₃`: java.lang.String) => new java.net.URI(`s₃`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _u4 = `in₂`.expectString[java.net.URI](((`s₄`: java.lang.String) => new java.net.URI(`s₄`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u4"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleNet_fields) + } + if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleNet(_u1, _u2, _u3, _u4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("u1", "u2", "u3", "u4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleNet] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleNet, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleNet = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleNet]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleUUID_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("u1", "u2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleUUID, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("u1") + out.value(in.u1) + out.label("u2") + out.value(in.u2) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleUUID = { + var _u1: java.util.UUID = null + var _u2: java.util.UUID = null + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleUUID_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleUUID] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _u1 = `in₂`.expectString[java.util.UUID](((x$0: java.lang.String) => java.util.UUID.fromString(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _u2 = `in₂`.expectString[java.util.UUID](((`x$0₂`: java.lang.String) => java.util.UUID.fromString(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u2"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleUUID_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleUUID(_u1, _u2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("u1", "u2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleUUID] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleUUID, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleUUID = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleUUID]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleUUID_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("u1", "u2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleUUID, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("u1") + out.value(in.u1) + out.label("u2") + out.value(in.u2) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleUUID = { + var _u1: java.util.UUID = null + var _u2: java.util.UUID = null + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleUUID_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleUUID] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _u1 = `in₂`.expectString[java.util.UUID](((x$0: java.lang.String) => java.util.UUID.fromString(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _u2 = `in₂`.expectString[java.util.UUID](((`x$0₂`: java.lang.String) => java.util.UUID.fromString(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u2"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleUUID_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleUUID(_u1, _u2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("u1", "u2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleUUID] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleUUID, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleUUID = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleUUID]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleDuration_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("d1", "d2", "d3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleDuration, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("d1") + out.value(in.d1) + out.label("d2") + out.value(in.d2) + out.label("d3") + out.value(in.d3) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleDuration = { + var _d1: java.time.Duration = null + var _d2: java.time.Duration = null + var _d3: java.time.Duration = null + var required: scala.Int = 7 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleDuration_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleDuration] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _d1 = `in₂`.expectString[java.time.Duration](((x$0: java.lang.CharSequence) => java.time.Duration.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _d2 = `in₂`.expectString[java.time.Duration](((`x$0₂`: java.lang.CharSequence) => java.time.Duration.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _d3 = `in₂`.expectString[java.time.Duration](((`x$0₃`: java.lang.CharSequence) => java.time.Duration.parse(`x$0₃`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d3"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleDuration_fields) + } + if (required.&(7).==(0)) new co.blocke.scalajack.json.primitives.SampleDuration(_d1, _d2, _d3) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("d1", "d2", "d3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(7)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleDuration] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleDuration, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleDuration = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleDuration]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleDuration_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("d1", "d2", "d3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleDuration, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("d1") + out.value(in.d1) + out.label("d2") + out.value(in.d2) + out.label("d3") + out.value(in.d3) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleDuration = { + var _d1: java.time.Duration = null + var _d2: java.time.Duration = null + var _d3: java.time.Duration = null + var required: scala.Int = 7 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleDuration_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleDuration] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _d1 = `in₂`.expectString[java.time.Duration](((x$0: java.lang.CharSequence) => java.time.Duration.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _d2 = `in₂`.expectString[java.time.Duration](((`x$0₂`: java.lang.CharSequence) => java.time.Duration.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _d3 = `in₂`.expectString[java.time.Duration](((`x$0₃`: java.lang.CharSequence) => java.time.Duration.parse(`x$0₃`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d3"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleDuration_fields) + } + if (required.&(7).==(0)) new co.blocke.scalajack.json.primitives.SampleDuration(_d1, _d2, _d3) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("d1", "d2", "d3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(7)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleDuration] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleDuration, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleDuration = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleDuration]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleInstant_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("i1", "i2", "i3", "i4", "i5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleInstant, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("i1") + out.value(in.i1) + out.label("i2") + out.value(in.i2) + out.label("i3") + out.value(in.i3) + out.label("i4") + out.value(in.i4) + out.label("i5") + out.value(in.i5) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleInstant = { + var _i1: java.time.Instant = null + var _i2: java.time.Instant = null + var _i3: java.time.Instant = null + var _i4: java.time.Instant = null + var _i5: java.time.Instant = null + var required: scala.Int = 31 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleInstant_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleInstant] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _i1 = `in₂`.expectString[java.time.Instant](((x$0: java.lang.CharSequence) => java.time.Instant.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _i2 = `in₂`.expectString[java.time.Instant](((`x$0₂`: java.lang.CharSequence) => java.time.Instant.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _i3 = `in₂`.expectString[java.time.Instant](((`x$0₃`: java.lang.CharSequence) => java.time.Instant.parse(`x$0₃`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _i4 = `in₂`.expectString[java.time.Instant](((`x$0₄`: java.lang.CharSequence) => java.time.Instant.parse(`x$0₄`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _i5 = `in₂`.expectString[java.time.Instant](((`x$0₅`: java.lang.CharSequence) => java.time.Instant.parse(`x$0₅`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i5"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleInstant_fields) + } + if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleInstant(_i1, _i2, _i3, _i4, _i5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("i1", "i2", "i3", "i4", "i5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleInstant] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleInstant, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleInstant = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleInstant]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleInstant_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("i1", "i2", "i3", "i4", "i5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleInstant, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("i1") + out.value(in.i1) + out.label("i2") + out.value(in.i2) + out.label("i3") + out.value(in.i3) + out.label("i4") + out.value(in.i4) + out.label("i5") + out.value(in.i5) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleInstant = { + var _i1: java.time.Instant = null + var _i2: java.time.Instant = null + var _i3: java.time.Instant = null + var _i4: java.time.Instant = null + var _i5: java.time.Instant = null + var required: scala.Int = 31 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleInstant_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleInstant] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _i1 = `in₂`.expectString[java.time.Instant](((x$0: java.lang.CharSequence) => java.time.Instant.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _i2 = `in₂`.expectString[java.time.Instant](((`x$0₂`: java.lang.CharSequence) => java.time.Instant.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _i3 = `in₂`.expectString[java.time.Instant](((`x$0₃`: java.lang.CharSequence) => java.time.Instant.parse(`x$0₃`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _i4 = `in₂`.expectString[java.time.Instant](((`x$0₄`: java.lang.CharSequence) => java.time.Instant.parse(`x$0₄`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _i5 = `in₂`.expectString[java.time.Instant](((`x$0₅`: java.lang.CharSequence) => java.time.Instant.parse(`x$0₅`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i5"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleInstant_fields) + } + if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleInstant(_i1, _i2, _i3, _i4, _i5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("i1", "i2", "i3", "i4", "i5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleInstant] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleInstant, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleInstant = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleInstant]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleLocalDateTime_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleLocalDateTime, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("d1") + out.value(in.d1) + out.label("d2") + out.value(in.d2) + out.label("d3") + out.value(in.d3) + out.label("d4") + out.value(in.d4) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalDateTime = { + var _d1: java.time.LocalDateTime = null + var _d2: java.time.LocalDateTime = null + var _d3: java.time.LocalDateTime = null + var _d4: java.time.LocalDateTime = null + var required: scala.Int = 15 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleLocalDateTime_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleLocalDateTime] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _d1 = `in₂`.expectString[java.time.LocalDateTime](((x$0: java.lang.CharSequence) => java.time.LocalDateTime.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _d2 = `in₂`.expectString[java.time.LocalDateTime](((`x$0₂`: java.lang.CharSequence) => java.time.LocalDateTime.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _d3 = `in₂`.expectString[java.time.LocalDateTime](((`x$0₃`: java.lang.CharSequence) => java.time.LocalDateTime.parse(`x$0₃`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _d4 = `in₂`.expectString[java.time.LocalDateTime](((`x$0₄`: java.lang.CharSequence) => java.time.LocalDateTime.parse(`x$0₄`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d4"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleLocalDateTime_fields) + } + if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleLocalDateTime(_d1, _d2, _d3, _d4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalDateTime] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleLocalDateTime, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalDateTime = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalDateTime]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleLocalDateTime_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleLocalDateTime, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("d1") + out.value(in.d1) + out.label("d2") + out.value(in.d2) + out.label("d3") + out.value(in.d3) + out.label("d4") + out.value(in.d4) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalDateTime = { + var _d1: java.time.LocalDateTime = null + var _d2: java.time.LocalDateTime = null + var _d3: java.time.LocalDateTime = null + var _d4: java.time.LocalDateTime = null + var required: scala.Int = 15 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleLocalDateTime_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleLocalDateTime] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _d1 = `in₂`.expectString[java.time.LocalDateTime](((x$0: java.lang.CharSequence) => java.time.LocalDateTime.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _d2 = `in₂`.expectString[java.time.LocalDateTime](((`x$0₂`: java.lang.CharSequence) => java.time.LocalDateTime.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _d3 = `in₂`.expectString[java.time.LocalDateTime](((`x$0₃`: java.lang.CharSequence) => java.time.LocalDateTime.parse(`x$0₃`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _d4 = `in₂`.expectString[java.time.LocalDateTime](((`x$0₄`: java.lang.CharSequence) => java.time.LocalDateTime.parse(`x$0₄`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d4"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleLocalDateTime_fields) + } + if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleLocalDateTime(_d1, _d2, _d3, _d4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalDateTime] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleLocalDateTime, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalDateTime = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalDateTime]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleLocalDate_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleLocalDate, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("d1") + out.value(in.d1) + out.label("d2") + out.value(in.d2) + out.label("d3") + out.value(in.d3) + out.label("d4") + out.value(in.d4) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalDate = { + var _d1: java.time.LocalDate = null + var _d2: java.time.LocalDate = null + var _d3: java.time.LocalDate = null + var _d4: java.time.LocalDate = null + var required: scala.Int = 15 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleLocalDate_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleLocalDate] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _d1 = `in₂`.expectString[java.time.LocalDate](((x$0: java.lang.CharSequence) => java.time.LocalDate.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _d2 = `in₂`.expectString[java.time.LocalDate](((`x$0₂`: java.lang.CharSequence) => java.time.LocalDate.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _d3 = `in₂`.expectString[java.time.LocalDate](((`x$0₃`: java.lang.CharSequence) => java.time.LocalDate.parse(`x$0₃`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _d4 = `in₂`.expectString[java.time.LocalDate](((`x$0₄`: java.lang.CharSequence) => java.time.LocalDate.parse(`x$0₄`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d4"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleLocalDate_fields) + } + if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleLocalDate(_d1, _d2, _d3, _d4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalDate] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleLocalDate, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalDate = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalDate]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleLocalDate_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleLocalDate, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("d1") + out.value(in.d1) + out.label("d2") + out.value(in.d2) + out.label("d3") + out.value(in.d3) + out.label("d4") + out.value(in.d4) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalDate = { + var _d1: java.time.LocalDate = null + var _d2: java.time.LocalDate = null + var _d3: java.time.LocalDate = null + var _d4: java.time.LocalDate = null + var required: scala.Int = 15 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleLocalDate_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleLocalDate] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _d1 = `in₂`.expectString[java.time.LocalDate](((x$0: java.lang.CharSequence) => java.time.LocalDate.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _d2 = `in₂`.expectString[java.time.LocalDate](((`x$0₂`: java.lang.CharSequence) => java.time.LocalDate.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _d3 = `in₂`.expectString[java.time.LocalDate](((`x$0₃`: java.lang.CharSequence) => java.time.LocalDate.parse(`x$0₃`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _d4 = `in₂`.expectString[java.time.LocalDate](((`x$0₄`: java.lang.CharSequence) => java.time.LocalDate.parse(`x$0₄`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d4"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleLocalDate_fields) + } + if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleLocalDate(_d1, _d2, _d3, _d4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalDate] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleLocalDate, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalDate = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalDate]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleLocalTime_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4", "d5", "d6")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleLocalTime, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("d1") + out.value(in.d1) + out.label("d2") + out.value(in.d2) + out.label("d3") + out.value(in.d3) + out.label("d4") + out.value(in.d4) + out.label("d5") + out.value(in.d5) + out.label("d6") + out.value(in.d6) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalTime = { + var _d1: java.time.LocalTime = null + var _d2: java.time.LocalTime = null + var _d3: java.time.LocalTime = null + var _d4: java.time.LocalTime = null + var _d5: java.time.LocalTime = null + var _d6: java.time.LocalTime = null + var required: scala.Int = 63 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleLocalTime_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleLocalTime] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _d1 = `in₂`.expectString[java.time.LocalTime](((x$0: java.lang.CharSequence) => java.time.LocalTime.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _d2 = `in₂`.expectString[java.time.LocalTime](((`x$0₂`: java.lang.CharSequence) => java.time.LocalTime.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _d3 = `in₂`.expectString[java.time.LocalTime](((`x$0₃`: java.lang.CharSequence) => java.time.LocalTime.parse(`x$0₃`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _d4 = `in₂`.expectString[java.time.LocalTime](((`x$0₄`: java.lang.CharSequence) => java.time.LocalTime.parse(`x$0₄`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _d5 = `in₂`.expectString[java.time.LocalTime](((`x$0₅`: java.lang.CharSequence) => java.time.LocalTime.parse(`x$0₅`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d5"), `in₂`) + case 5 => + if (required.&(32).!=(0)) { + required = required.^(32) + _d6 = `in₂`.expectString[java.time.LocalTime](((`x$0₆`: java.lang.CharSequence) => java.time.LocalTime.parse(`x$0₆`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d6"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleLocalTime_fields) + } + if (required.&(63).==(0)) new co.blocke.scalajack.json.primitives.SampleLocalTime(_d1, _d2, _d3, _d4, _d5, _d6) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4", "d5", "d6")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(63)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalTime] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleLocalTime, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalTime = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalTime]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleLocalTime_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4", "d5", "d6")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleLocalTime, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("d1") + out.value(in.d1) + out.label("d2") + out.value(in.d2) + out.label("d3") + out.value(in.d3) + out.label("d4") + out.value(in.d4) + out.label("d5") + out.value(in.d5) + out.label("d6") + out.value(in.d6) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalTime = { + var _d1: java.time.LocalTime = null + var _d2: java.time.LocalTime = null + var _d3: java.time.LocalTime = null + var _d4: java.time.LocalTime = null + var _d5: java.time.LocalTime = null + var _d6: java.time.LocalTime = null + var required: scala.Int = 63 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleLocalTime_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleLocalTime] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _d1 = `in₂`.expectString[java.time.LocalTime](((x$0: java.lang.CharSequence) => java.time.LocalTime.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _d2 = `in₂`.expectString[java.time.LocalTime](((`x$0₂`: java.lang.CharSequence) => java.time.LocalTime.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _d3 = `in₂`.expectString[java.time.LocalTime](((`x$0₃`: java.lang.CharSequence) => java.time.LocalTime.parse(`x$0₃`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _d4 = `in₂`.expectString[java.time.LocalTime](((`x$0₄`: java.lang.CharSequence) => java.time.LocalTime.parse(`x$0₄`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d4"), `in₂`) + case 4 => + if (required.&(16).!=(0)) { + required = required.^(16) + _d5 = `in₂`.expectString[java.time.LocalTime](((`x$0₅`: java.lang.CharSequence) => java.time.LocalTime.parse(`x$0₅`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d5"), `in₂`) + case 5 => + if (required.&(32).!=(0)) { + required = required.^(32) + _d6 = `in₂`.expectString[java.time.LocalTime](((`x$0₆`: java.lang.CharSequence) => java.time.LocalTime.parse(`x$0₆`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d6"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleLocalTime_fields) + } + if (required.&(63).==(0)) new co.blocke.scalajack.json.primitives.SampleLocalTime(_d1, _d2, _d3, _d4, _d5, _d6) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4", "d5", "d6")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(63)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalTime] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleLocalTime, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalTime = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalTime]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleMonthDay_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("m1", "m2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleMonthDay, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("m1") + out.value(in.m1) + out.label("m2") + out.value(in.m2) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleMonthDay = { + var _m1: java.time.MonthDay = null + var _m2: java.time.MonthDay = null + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleMonthDay_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleMonthDay] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _m1 = `in₂`.expectString[java.time.MonthDay](((x$0: java.lang.CharSequence) => java.time.MonthDay.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("m1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _m2 = `in₂`.expectString[java.time.MonthDay](((`x$0₂`: java.lang.CharSequence) => java.time.MonthDay.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("m2"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleMonthDay_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleMonthDay(_m1, _m2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("m1", "m2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleMonthDay] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleMonthDay, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleMonthDay = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleMonthDay]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleMonthDay_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("m1", "m2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleMonthDay, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("m1") + out.value(in.m1) + out.label("m2") + out.value(in.m2) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleMonthDay = { + var _m1: java.time.MonthDay = null + var _m2: java.time.MonthDay = null + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleMonthDay_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleMonthDay] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _m1 = `in₂`.expectString[java.time.MonthDay](((x$0: java.lang.CharSequence) => java.time.MonthDay.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("m1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _m2 = `in₂`.expectString[java.time.MonthDay](((`x$0₂`: java.lang.CharSequence) => java.time.MonthDay.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("m2"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleMonthDay_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleMonthDay(_m1, _m2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("m1", "m2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleMonthDay] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleMonthDay, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleMonthDay = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleMonthDay]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleOffsetDateTime_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("o1", "o2", "o3", "o4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleOffsetDateTime, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("o1") + out.value(in.o1) + out.label("o2") + out.value(in.o2) + out.label("o3") + out.value(in.o3) + out.label("o4") + out.value(in.o4) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleOffsetDateTime = { + var _o1: java.time.OffsetDateTime = null + var _o2: java.time.OffsetDateTime = null + var _o3: java.time.OffsetDateTime = null + var _o4: java.time.OffsetDateTime = null + var required: scala.Int = 15 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleOffsetDateTime_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleOffsetDateTime] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _o1 = `in₂`.expectString[java.time.OffsetDateTime](((x$0: java.lang.CharSequence) => java.time.OffsetDateTime.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _o2 = `in₂`.expectString[java.time.OffsetDateTime](((`x$0₂`: java.lang.CharSequence) => java.time.OffsetDateTime.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _o3 = `in₂`.expectString[java.time.OffsetDateTime](((`x$0₃`: java.lang.CharSequence) => java.time.OffsetDateTime.parse(`x$0₃`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _o4 = `in₂`.expectString[java.time.OffsetDateTime](((`x$0₄`: java.lang.CharSequence) => java.time.OffsetDateTime.parse(`x$0₄`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o4"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleOffsetDateTime_fields) + } + if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleOffsetDateTime(_o1, _o2, _o3, _o4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("o1", "o2", "o3", "o4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleOffsetDateTime] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleOffsetDateTime, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleOffsetDateTime = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleOffsetDateTime]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleOffsetDateTime_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("o1", "o2", "o3", "o4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleOffsetDateTime, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("o1") + out.value(in.o1) + out.label("o2") + out.value(in.o2) + out.label("o3") + out.value(in.o3) + out.label("o4") + out.value(in.o4) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleOffsetDateTime = { + var _o1: java.time.OffsetDateTime = null + var _o2: java.time.OffsetDateTime = null + var _o3: java.time.OffsetDateTime = null + var _o4: java.time.OffsetDateTime = null + var required: scala.Int = 15 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleOffsetDateTime_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleOffsetDateTime] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _o1 = `in₂`.expectString[java.time.OffsetDateTime](((x$0: java.lang.CharSequence) => java.time.OffsetDateTime.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _o2 = `in₂`.expectString[java.time.OffsetDateTime](((`x$0₂`: java.lang.CharSequence) => java.time.OffsetDateTime.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _o3 = `in₂`.expectString[java.time.OffsetDateTime](((`x$0₃`: java.lang.CharSequence) => java.time.OffsetDateTime.parse(`x$0₃`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _o4 = `in₂`.expectString[java.time.OffsetDateTime](((`x$0₄`: java.lang.CharSequence) => java.time.OffsetDateTime.parse(`x$0₄`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o4"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleOffsetDateTime_fields) + } + if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleOffsetDateTime(_o1, _o2, _o3, _o4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("o1", "o2", "o3", "o4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleOffsetDateTime] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleOffsetDateTime, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleOffsetDateTime = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleOffsetDateTime]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleOffsetTime_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("o1", "o2", "o3", "o4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleOffsetTime, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("o1") + out.value(in.o1) + out.label("o2") + out.value(in.o2) + out.label("o3") + out.value(in.o3) + out.label("o4") + out.value(in.o4) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleOffsetTime = { + var _o1: java.time.OffsetTime = null + var _o2: java.time.OffsetTime = null + var _o3: java.time.OffsetTime = null + var _o4: java.time.OffsetTime = null + var required: scala.Int = 15 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleOffsetTime_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleOffsetTime] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _o1 = `in₂`.expectString[java.time.OffsetTime](((x$0: java.lang.CharSequence) => java.time.OffsetTime.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _o2 = `in₂`.expectString[java.time.OffsetTime](((`x$0₂`: java.lang.CharSequence) => java.time.OffsetTime.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _o3 = `in₂`.expectString[java.time.OffsetTime](((`x$0₃`: java.lang.CharSequence) => java.time.OffsetTime.parse(`x$0₃`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _o4 = `in₂`.expectString[java.time.OffsetTime](((`x$0₄`: java.lang.CharSequence) => java.time.OffsetTime.parse(`x$0₄`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o4"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleOffsetTime_fields) + } + if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleOffsetTime(_o1, _o2, _o3, _o4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("o1", "o2", "o3", "o4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleOffsetTime] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleOffsetTime, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleOffsetTime = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleOffsetTime]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleOffsetTime_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("o1", "o2", "o3", "o4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleOffsetTime, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("o1") + out.value(in.o1) + out.label("o2") + out.value(in.o2) + out.label("o3") + out.value(in.o3) + out.label("o4") + out.value(in.o4) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleOffsetTime = { + var _o1: java.time.OffsetTime = null + var _o2: java.time.OffsetTime = null + var _o3: java.time.OffsetTime = null + var _o4: java.time.OffsetTime = null + var required: scala.Int = 15 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleOffsetTime_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleOffsetTime] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _o1 = `in₂`.expectString[java.time.OffsetTime](((x$0: java.lang.CharSequence) => java.time.OffsetTime.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _o2 = `in₂`.expectString[java.time.OffsetTime](((`x$0₂`: java.lang.CharSequence) => java.time.OffsetTime.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _o3 = `in₂`.expectString[java.time.OffsetTime](((`x$0₃`: java.lang.CharSequence) => java.time.OffsetTime.parse(`x$0₃`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _o4 = `in₂`.expectString[java.time.OffsetTime](((`x$0₄`: java.lang.CharSequence) => java.time.OffsetTime.parse(`x$0₄`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o4"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleOffsetTime_fields) + } + if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleOffsetTime(_o1, _o2, _o3, _o4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("o1", "o2", "o3", "o4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleOffsetTime] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleOffsetTime, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleOffsetTime = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleOffsetTime]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SamplePeriod_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("p1", "p2", "p3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SamplePeriod, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("p1") + out.value(in.p1) + out.label("p2") + out.value(in.p2) + out.label("p3") + out.value(in.p3) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SamplePeriod = { + var _p1: java.time.Period = null + var _p2: java.time.Period = null + var _p3: java.time.Period = null + var required: scala.Int = 7 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SamplePeriod_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SamplePeriod] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _p1 = `in₂`.expectString[java.time.Period](((x$0: java.lang.CharSequence) => java.time.Period.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("p1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _p2 = `in₂`.expectString[java.time.Period](((`x$0₂`: java.lang.CharSequence) => java.time.Period.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("p2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _p3 = `in₂`.expectString[java.time.Period](((`x$0₃`: java.lang.CharSequence) => java.time.Period.parse(`x$0₃`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("p3"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SamplePeriod_fields) + } + if (required.&(7).==(0)) new co.blocke.scalajack.json.primitives.SamplePeriod(_p1, _p2, _p3) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("p1", "p2", "p3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(7)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SamplePeriod] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SamplePeriod, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SamplePeriod = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SamplePeriod]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SamplePeriod_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("p1", "p2", "p3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SamplePeriod, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("p1") + out.value(in.p1) + out.label("p2") + out.value(in.p2) + out.label("p3") + out.value(in.p3) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SamplePeriod = { + var _p1: java.time.Period = null + var _p2: java.time.Period = null + var _p3: java.time.Period = null + var required: scala.Int = 7 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SamplePeriod_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SamplePeriod] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _p1 = `in₂`.expectString[java.time.Period](((x$0: java.lang.CharSequence) => java.time.Period.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("p1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _p2 = `in₂`.expectString[java.time.Period](((`x$0₂`: java.lang.CharSequence) => java.time.Period.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("p2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _p3 = `in₂`.expectString[java.time.Period](((`x$0₃`: java.lang.CharSequence) => java.time.Period.parse(`x$0₃`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("p3"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SamplePeriod_fields) + } + if (required.&(7).==(0)) new co.blocke.scalajack.json.primitives.SamplePeriod(_p1, _p2, _p3) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("p1", "p2", "p3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(7)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SamplePeriod] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SamplePeriod, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SamplePeriod = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SamplePeriod]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleYear_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("y1", "y2", "y3", "y4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleYear, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("y1") + out.value(in.y1) + out.label("y2") + out.value(in.y2) + out.label("y3") + out.value(in.y3) + out.label("y4") + out.value(in.y4) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleYear = { + var _y1: java.time.Year = null + var _y2: java.time.Year = null + var _y3: java.time.Year = null + var _y4: java.time.Year = null + var required: scala.Int = 15 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleYear_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleYear] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _y1 = `in₂`.expectString[java.time.Year](((x$0: java.lang.CharSequence) => java.time.Year.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _y2 = `in₂`.expectString[java.time.Year](((`x$0₂`: java.lang.CharSequence) => java.time.Year.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _y3 = `in₂`.expectString[java.time.Year](((`x$0₃`: java.lang.CharSequence) => java.time.Year.parse(`x$0₃`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _y4 = `in₂`.expectString[java.time.Year](((`x$0₄`: java.lang.CharSequence) => java.time.Year.parse(`x$0₄`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y4"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleYear_fields) + } + if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleYear(_y1, _y2, _y3, _y4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("y1", "y2", "y3", "y4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleYear] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleYear, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleYear = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleYear]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleYear_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("y1", "y2", "y3", "y4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleYear, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("y1") + out.value(in.y1) + out.label("y2") + out.value(in.y2) + out.label("y3") + out.value(in.y3) + out.label("y4") + out.value(in.y4) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleYear = { + var _y1: java.time.Year = null + var _y2: java.time.Year = null + var _y3: java.time.Year = null + var _y4: java.time.Year = null + var required: scala.Int = 15 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleYear_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleYear] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _y1 = `in₂`.expectString[java.time.Year](((x$0: java.lang.CharSequence) => java.time.Year.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _y2 = `in₂`.expectString[java.time.Year](((`x$0₂`: java.lang.CharSequence) => java.time.Year.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _y3 = `in₂`.expectString[java.time.Year](((`x$0₃`: java.lang.CharSequence) => java.time.Year.parse(`x$0₃`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _y4 = `in₂`.expectString[java.time.Year](((`x$0₄`: java.lang.CharSequence) => java.time.Year.parse(`x$0₄`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y4"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleYear_fields) + } + if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleYear(_y1, _y2, _y3, _y4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("y1", "y2", "y3", "y4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleYear] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleYear, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleYear = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleYear]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleYearMonth_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("y1", "y2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleYearMonth, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("y1") + out.value(in.y1) + out.label("y2") + out.value(in.y2) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleYearMonth = { + var _y1: java.time.YearMonth = null + var _y2: java.time.YearMonth = null + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleYearMonth_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleYearMonth] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _y1 = `in₂`.expectString[java.time.YearMonth](((x$0: java.lang.CharSequence) => java.time.YearMonth.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _y2 = `in₂`.expectString[java.time.YearMonth](((`x$0₂`: java.lang.CharSequence) => java.time.YearMonth.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y2"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleYearMonth_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleYearMonth(_y1, _y2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("y1", "y2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleYearMonth] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleYearMonth, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleYearMonth = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleYearMonth]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleYearMonth_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("y1", "y2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleYearMonth, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("y1") + out.value(in.y1) + out.label("y2") + out.value(in.y2) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleYearMonth = { + var _y1: java.time.YearMonth = null + var _y2: java.time.YearMonth = null + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleYearMonth_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleYearMonth] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _y1 = `in₂`.expectString[java.time.YearMonth](((x$0: java.lang.CharSequence) => java.time.YearMonth.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _y2 = `in₂`.expectString[java.time.YearMonth](((`x$0₂`: java.lang.CharSequence) => java.time.YearMonth.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y2"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleYearMonth_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleYearMonth(_y1, _y2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("y1", "y2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleYearMonth] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleYearMonth, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleYearMonth = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleYearMonth]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleZoneId_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("z1", "z2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleZoneId, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("z1") + out.value(in.z1) + out.label("z2") + out.value(in.z2) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleZoneId = { + var _z1: java.time.ZoneId = null + var _z2: java.time.ZoneId = null + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleZoneId_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleZoneId] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _z1 = `in₂`.expectString[java.time.ZoneId](((x$0: java.lang.String) => java.time.ZoneId.of(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("z1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _z2 = `in₂`.expectString[java.time.ZoneId](((`x$0₂`: java.lang.String) => java.time.ZoneId.of(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("z2"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleZoneId_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleZoneId(_z1, _z2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("z1", "z2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleZoneId] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleZoneId, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleZoneId = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleZoneId]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleZoneId_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("z1", "z2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleZoneId, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("z1") + out.value(in.z1) + out.label("z2") + out.value(in.z2) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleZoneId = { + var _z1: java.time.ZoneId = null + var _z2: java.time.ZoneId = null + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleZoneId_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleZoneId] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _z1 = `in₂`.expectString[java.time.ZoneId](((x$0: java.lang.String) => java.time.ZoneId.of(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("z1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _z2 = `in₂`.expectString[java.time.ZoneId](((`x$0₂`: java.lang.String) => java.time.ZoneId.of(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("z2"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleZoneId_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleZoneId(_z1, _z2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("z1", "z2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleZoneId] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleZoneId, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleZoneId = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleZoneId]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleZonedDateTime_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("o1", "o2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleZonedDateTime, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("o1") + out.value(in.o1) + out.label("o2") + out.value(in.o2) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleZonedDateTime = { + var _o1: java.time.ZonedDateTime = null + var _o2: java.time.ZonedDateTime = null + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleZonedDateTime_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleZonedDateTime] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _o1 = `in₂`.expectString[java.time.ZonedDateTime](((x$0: java.lang.CharSequence) => java.time.ZonedDateTime.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _o2 = `in₂`.expectString[java.time.ZonedDateTime](((`x$0₂`: java.lang.CharSequence) => java.time.ZonedDateTime.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o2"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleZonedDateTime_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleZonedDateTime(_o1, _o2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("o1", "o2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleZonedDateTime] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleZonedDateTime, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleZonedDateTime = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleZonedDateTime]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleZonedDateTime_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("o1", "o2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleZonedDateTime, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("o1") + out.value(in.o1) + out.label("o2") + out.value(in.o2) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleZonedDateTime = { + var _o1: java.time.ZonedDateTime = null + var _o2: java.time.ZonedDateTime = null + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleZonedDateTime_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleZonedDateTime] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _o1 = `in₂`.expectString[java.time.ZonedDateTime](((x$0: java.lang.CharSequence) => java.time.ZonedDateTime.parse(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _o2 = `in₂`.expectString[java.time.ZonedDateTime](((`x$0₂`: java.lang.CharSequence) => java.time.ZonedDateTime.parse(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o2"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleZonedDateTime_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleZonedDateTime(_o1, _o2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("o1", "o2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleZonedDateTime] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleZonedDateTime, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleZonedDateTime = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleZonedDateTime]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleNet_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("u1", "u2", "u3", "u4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleNet, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("u1") + out.value(in.u1) + out.label("u2") + out.value(in.u2) + out.label("u3") + out.value(in.u3) + out.label("u4") + out.value(in.u4) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleNet = { + var _u1: java.net.URL = null + var _u2: java.net.URL = null + var _u3: java.net.URI = null + var _u4: java.net.URI = null + var required: scala.Int = 15 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleNet_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleNet] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _u1 = `in₂`.expectString[java.net.URL](((s: java.lang.String) => new java.net.URL(s))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _u2 = `in₂`.expectString[java.net.URL](((`s₂`: java.lang.String) => new java.net.URL(`s₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _u3 = `in₂`.expectString[java.net.URI](((`s₃`: java.lang.String) => new java.net.URI(`s₃`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _u4 = `in₂`.expectString[java.net.URI](((`s₄`: java.lang.String) => new java.net.URI(`s₄`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u4"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleNet_fields) + } + if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleNet(_u1, _u2, _u3, _u4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("u1", "u2", "u3", "u4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleNet] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleNet, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleNet = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleNet]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleNet_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("u1", "u2", "u3", "u4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleNet, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("u1") + out.value(in.u1) + out.label("u2") + out.value(in.u2) + out.label("u3") + out.value(in.u3) + out.label("u4") + out.value(in.u4) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleNet = { + var _u1: java.net.URL = null + var _u2: java.net.URL = null + var _u3: java.net.URI = null + var _u4: java.net.URI = null + var required: scala.Int = 15 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleNet_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleNet] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _u1 = `in₂`.expectString[java.net.URL](((s: java.lang.String) => new java.net.URL(s))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _u2 = `in₂`.expectString[java.net.URL](((`s₂`: java.lang.String) => new java.net.URL(`s₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u2"), `in₂`) + case 2 => + if (required.&(4).!=(0)) { + required = required.^(4) + _u3 = `in₂`.expectString[java.net.URI](((`s₃`: java.lang.String) => new java.net.URI(`s₃`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u3"), `in₂`) + case 3 => + if (required.&(8).!=(0)) { + required = required.^(8) + _u4 = `in₂`.expectString[java.net.URI](((`s₄`: java.lang.String) => new java.net.URI(`s₄`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u4"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleNet_fields) + } + if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleNet(_u1, _u2, _u3, _u4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("u1", "u2", "u3", "u4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleNet] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleNet, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleNet = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleNet]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleUUID_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("u1", "u2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleUUID, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("u1") + out.value(in.u1) + out.label("u2") + out.value(in.u2) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleUUID = { + var _u1: java.util.UUID = null + var _u2: java.util.UUID = null + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleUUID_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleUUID] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _u1 = `in₂`.expectString[java.util.UUID](((x$0: java.lang.String) => java.util.UUID.fromString(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _u2 = `in₂`.expectString[java.util.UUID](((`x$0₂`: java.lang.String) => java.util.UUID.fromString(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u2"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleUUID_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleUUID(_u1, _u2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("u1", "u2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleUUID] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleUUID, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleUUID = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleUUID]) +} +Codec: { + val __co_blocke_scalajack_json_primitives_SampleUUID_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("u1", "u2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) + def w0(in: co.blocke.scalajack.json.primitives.SampleUUID, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { + out.startObject() + out.label("u1") + out.value(in.u1) + out.label("u2") + out.value(in.u2) + out.endObject() + } + def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleUUID = { + var _u1: java.util.UUID = null + var _u2: java.util.UUID = null + var required: scala.Int = 3 + var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleUUID_fields) + if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleUUID] else { + while (maybeFieldNum.isDefined) { + maybeFieldNum.get match { + case 0 => + if (required.&(1).!=(0)) { + required = required.^(1) + _u1 = `in₂`.expectString[java.util.UUID](((x$0: java.lang.String) => java.util.UUID.fromString(x$0))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u1"), `in₂`) + case 1 => + if (required.&(2).!=(0)) { + required = required.^(2) + _u2 = `in₂`.expectString[java.util.UUID](((`x$0₂`: java.lang.String) => java.util.UUID.fromString(`x$0₂`))) + } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u2"), `in₂`) + case _ => + `in₂`.skipValue() + } + maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleUUID_fields) + } + if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleUUID(_u1, _u2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("u1", "u2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) + } + } + final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleUUID] { + def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleUUID, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) + def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleUUID = r0(`in₄`) + } + + (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleUUID]) +} +[info] done compiling +[info] JavaCollSpec: +[info] ------------------------------- +[info] : Java Collection Tests : +[info] ------------------------------- +[info] +++ Basic functions (Set) +++ +[info] - Set is null must work +[info] - Set of numeric must work +[info] - Set of string must work +[info] - Set of boolean must work +[info] - Set of Set (nested) must work *** FAILED *** +[info] java.lang.ClassCastException: class java.util.TreeSet cannot be cast to class java.lang.Comparable (java.util.TreeSet and java.lang.Comparable are in module java.base of loader 'bootstrap') +[info] at java.base/java.util.TreeMap.compare(TreeMap.java:1569) +[info] at java.base/java.util.TreeMap.addEntryToEmptyMap(TreeMap.java:776) +[info] at java.base/java.util.TreeMap.put(TreeMap.java:785) +[info] at java.base/java.util.TreeMap.put(TreeMap.java:534) +[info] at java.base/java.util.TreeSet.add(TreeSet.java:255) +[info] at java.base/java.util.AbstractCollection.addAll(AbstractCollection.java:338) +[info] at java.base/java.util.TreeSet.addAll(TreeSet.java:309) +[info] at java.base/java.util.TreeSet.(TreeSet.java:160) +[info] at co.blocke.scalajack.json.collections.JavaCollSpec.co$blocke$scalajack$json$collections$JavaCollSpec$$_$_$r0$5(JavaCollSpec.scala:61) +[info] at co.blocke.scalajack.json.collections.JavaCollSpec$$anon$6.decodeValue(JavaCollSpec.scala:61) +[info] ... +[info] - Set of either must work *** FAILED *** +[info] java.lang.ClassCastException: class scala.util.Right cannot be cast to class java.lang.Comparable (scala.util.Right is in unnamed module of loader sbt.internal.ScalaLibraryClassLoader @34e01d38; java.lang.Comparable is in module java.base of loader 'bootstrap') +[info] at java.base/java.util.TreeMap.compare(TreeMap.java:1569) +[info] at java.base/java.util.TreeMap.addEntryToEmptyMap(TreeMap.java:776) +[info] at java.base/java.util.TreeMap.put(TreeMap.java:785) +[info] at java.base/java.util.TreeMap.put(TreeMap.java:534) +[info] at java.base/java.util.TreeSet.add(TreeSet.java:255) +[info] at java.base/java.util.AbstractCollection.addAll(AbstractCollection.java:338) +[info] at java.base/java.util.TreeSet.addAll(TreeSet.java:309) +[info] at java.base/java.util.TreeSet.(TreeSet.java:160) +[info] at co.blocke.scalajack.json.collections.JavaCollSpec.co$blocke$scalajack$json$collections$JavaCollSpec$$_$_$r0$6(JavaCollSpec.scala:68) +[info] at co.blocke.scalajack.json.collections.JavaCollSpec$$anon$7.decodeValue(JavaCollSpec.scala:68) +[info] ... +[info] - Set of union must work *** FAILED *** +[info] java.lang.ClassCastException: class java.lang.Boolean cannot be cast to class java.lang.Integer (java.lang.Boolean and java.lang.Integer are in module java.base of loader 'bootstrap') +[info] at java.base/java.lang.Integer.compareTo(Integer.java:72) +[info] at java.base/java.util.TreeMap.put(TreeMap.java:814) +[info] at java.base/java.util.TreeMap.put(TreeMap.java:534) +[info] at java.base/java.util.TreeSet.add(TreeSet.java:255) +[info] at java.base/java.util.AbstractCollection.addAll(AbstractCollection.java:338) +[info] at java.base/java.util.TreeSet.addAll(TreeSet.java:309) +[info] at java.base/java.util.TreeSet.(TreeSet.java:160) +[info] at co.blocke.scalajack.json.collections.JavaCollSpec.co$blocke$scalajack$json$collections$JavaCollSpec$$_$_$r0$7(JavaCollSpec.scala:75) +[info] at co.blocke.scalajack.json.collections.JavaCollSpec$$anon$8.decodeValue(JavaCollSpec.scala:75) +[info] at co.blocke.scalajack.json.collections.JavaCollSpec$$anon$8.decodeValue(JavaCollSpec.scala:75) +[info] ... +[info] - Set of option must work *** FAILED *** +[info] java.lang.ClassCastException: class scala.Some cannot be cast to class java.lang.Comparable (scala.Some is in unnamed module of loader sbt.internal.ScalaLibraryClassLoader @34e01d38; java.lang.Comparable is in module java.base of loader 'bootstrap') +[info] at java.base/java.util.TreeMap.compare(TreeMap.java:1569) +[info] at java.base/java.util.TreeMap.addEntryToEmptyMap(TreeMap.java:776) +[info] at java.base/java.util.TreeMap.put(TreeMap.java:785) +[info] at java.base/java.util.TreeMap.put(TreeMap.java:534) +[info] at java.base/java.util.TreeSet.add(TreeSet.java:255) +[info] at java.base/java.util.AbstractCollection.addAll(AbstractCollection.java:338) +[info] at java.base/java.util.TreeSet.addAll(TreeSet.java:309) +[info] at java.base/java.util.TreeSet.(TreeSet.java:160) +[info] at co.blocke.scalajack.json.collections.JavaCollSpec.co$blocke$scalajack$json$collections$JavaCollSpec$$_$_$r0$8(JavaCollSpec.scala:82) +[info] at co.blocke.scalajack.json.collections.JavaCollSpec$$anon$9.decodeValue(JavaCollSpec.scala:82) +[info] ... +[info] - Set of map must work *** FAILED *** +[info] java.lang.ClassCastException: class scala.collection.immutable.Map$Map2 cannot be cast to class java.lang.Comparable (scala.collection.immutable.Map$Map2 is in unnamed module of loader sbt.internal.ScalaLibraryClassLoader @34e01d38; java.lang.Comparable is in module java.base of loader 'bootstrap') +[info] at java.base/java.util.TreeMap.compare(TreeMap.java:1569) +[info] at java.base/java.util.TreeMap.addEntryToEmptyMap(TreeMap.java:776) +[info] at java.base/java.util.TreeMap.put(TreeMap.java:785) +[info] at java.base/java.util.TreeMap.put(TreeMap.java:534) +[info] at java.base/java.util.TreeSet.add(TreeSet.java:255) +[info] at java.base/java.util.AbstractCollection.addAll(AbstractCollection.java:338) +[info] at java.base/java.util.TreeSet.addAll(TreeSet.java:309) +[info] at java.base/java.util.TreeSet.(TreeSet.java:160) +[info] at co.blocke.scalajack.json.collections.JavaCollSpec.co$blocke$scalajack$json$collections$JavaCollSpec$$_$_$r0$9(JavaCollSpec.scala:89) +[info] at co.blocke.scalajack.json.collections.JavaCollSpec$$anon$10.decodeValue(JavaCollSpec.scala:89) +[info] ... +[info] - Set of class must work *** FAILED *** +[info] java.lang.ClassCastException: class co.blocke.scalajack.json.collections.Person cannot be cast to class java.lang.Comparable (co.blocke.scalajack.json.collections.Person is in unnamed module of loader sbt.internal.LayeredClassLoader @2aa03814; java.lang.Comparable is in module java.base of loader 'bootstrap') +[info] at java.base/java.util.TreeMap.compare(TreeMap.java:1569) +[info] at java.base/java.util.TreeMap.addEntryToEmptyMap(TreeMap.java:776) +[info] at java.base/java.util.TreeMap.put(TreeMap.java:785) +[info] at java.base/java.util.TreeMap.put(TreeMap.java:534) +[info] at java.base/java.util.TreeSet.add(TreeSet.java:255) +[info] at java.base/java.util.AbstractCollection.addAll(AbstractCollection.java:338) +[info] at java.base/java.util.TreeSet.addAll(TreeSet.java:309) +[info] at java.base/java.util.TreeSet.(TreeSet.java:160) +[info] at co.blocke.scalajack.json.collections.JavaCollSpec.co$blocke$scalajack$json$collections$JavaCollSpec$$_$_$r0$10(JavaCollSpec.scala:96) +[info] at co.blocke.scalajack.json.collections.JavaCollSpec$$anon$11.decodeValue(JavaCollSpec.scala:96) +[info] ... +[info] +++ Basic functions (ArrayList) +++ +[info] - ArrayList is null must work +[info] - ArrayList of numeric must work +[info] - ArrayList of string must work +[info] - ArrayList of boolean must work +[info] - ArrayList of ArrayList (nested) must work +[info] - ArrayList of either must work +[info] - ArrayList of union must work +[info] - ArrayList of option must work +[info] - ArrayList of map must work +[info] - ArrayList of class must work +[info] +++ Coersions (special cases, traits, etc) +++ +[info] - ArrayBlockingQueue +[info] - TreeSet +[info] - Stack (pending) +[info] - List (pending) +[info] - Iterable (pending) +[info] - Queue (pending) +[info] - SortedSet (pending) +[info] - Deque (pending) +[info] - BlockingQueue (pending) +[info] - Vector (generic Collection example) (pending) +[info] Run completed in 180 milliseconds. +[info] Total number of tests run: 22 +[info] Suites: completed 1, aborted 0 +[info] Tests: succeeded 16, failed 6, canceled 0, ignored 0, pending 8 +[info] *** 6 TESTS FAILED *** +[error] Failed tests: +[error] co.blocke.scalajack.json.collections.JavaCollSpec +[error] (Test / testOnly) sbt.TestsFailedException: Tests unsuccessful +[error] Total time: 22 s, completed Apr 6, 2024, 11:44:32 PM diff --git a/build.sbt b/build.sbt index 12a6de43..71c3d1dd 100644 --- a/build.sbt +++ b/build.sbt @@ -78,7 +78,7 @@ lazy val compilerOptions = Seq( "-feature", "-language:implicitConversions", "-deprecation", - "-explain", + // "-explain", "-encoding", "utf8" ) diff --git a/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala b/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala index 3507fd54..1be08334 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala @@ -460,7 +460,7 @@ object JsonCodecMaker: makeWriteFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => t.elementRef.refType match case '[e] => - val tin = in.asExprOf[java.util.Collection[_]] + val tin = '{ $in.asInstanceOf[java.util.Collection[_]] } '{ if $tin == null then $out.burpNull() else @@ -1505,64 +1505,93 @@ syntheticTA.write(t.asInstanceOf[L], writer, out) else null }.asExprOf[T] - /* - - ArrayBlockingQueue, - + ArrayDeque, - + ArrayList, - + ConcurrentLinkedDeque, - + ConcurrentLinkedQueue, - + ConcurrentSkipListSet, - + CopyOnWriteArrayList, - + CopyOnWriteArraySet, - + DelayQueue, - + HashSet, - + LinkedBlockingDeque, - + LinkedBlockingQueue, - + LinkedHashSet, - + LinkedList, - + LinkedTransferQueue, - + PriorityBlockingQueue, - + PriorityQueue, - - Stack, - + TreeSet, - + Vector - So... 2 special cases. The rest follow a Collection constructor pattern - */ // -------------------- // Java Collections... // -------------------- case t: JavaCollectionRef[?] => - ref.refType match - case '[java.util.concurrent.ArrayBlockingQueue[?]] => - t.elementRef.refType match - case '[e] => - val rtypeRef = t.elementRef.asInstanceOf[RTypeRef[e]] + t.elementRef.refType match + case '[e] => + val rtypeRef = t.elementRef.asInstanceOf[RTypeRef[e]] + ref.name match + // Non-standard concrete Java Collecitons + case "java.util.concurrent.ArrayBlockingQueue" => + '{ + val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in, inTuple) }) + if parsedArray == null then null + else new java.util.concurrent.ArrayBlockingQueue(parsedArray.length, true, parsedArray.toList.asJava) + }.asExprOf[T] + // java.util.Collection interfaces + case "java.util.Stack" => + '{ + val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in, inTuple) }) + if parsedArray == null then null + else + val s = new java.util.Stack[e]() + parsedArray.map(j => s.push(j)) + s + }.asExprOf[T] + case "java.util.concurrent.TransferQueue" => '{ val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in, inTuple) }) - if parsedArray != null then new java.util.concurrent.ArrayBlockingQueue(parsedArray.length, true, parsedArray.toList.asJava) + if parsedArray != null then new java.util.concurrent.LinkedTransferQueue(parsedArray.toList.asJava) else null }.asExprOf[T] - case '[java.util.Stack[?]] => - t.elementRef.refType match - case '[e] => - val rtypeRef = t.elementRef.asInstanceOf[RTypeRef[e]] + case "java.util.concurrent.BlockingQueue" => '{ val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in, inTuple) }) - if parsedArray != null then - val stack = new java.util.Stack[e]() - parsedArray.map(stack.push(_)) - stack + if parsedArray != null then new java.util.concurrent.LinkedBlockingQueue(parsedArray.toList.asJava) else null }.asExprOf[T] - case _ => - t.elementRef.refType match - case '[e] => - val arg = '{ - val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](t.elementRef.asInstanceOf[RTypeRef[e]], in, inTuple) }) - if parsedArray == null then null - else parsedArray.toList.asJava - }.asTerm - Select.overloaded(New(Inferred(TypeRepr.of[T])), "", List(TypeRepr.of[e]), List(arg)).asExprOf[T] + case "java.util.TreeSet" | "java.util.NavigableSet" | "java.util.SortedSet" => + t.elementRef match + case _: PrimitiveRef => + t.elementRef.refType match + case '[z] => + '{ + val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in, inTuple) }) + if parsedArray != null then new java.util.TreeSet(parsedArray.toList.asJava) + else null + }.asExprOf[T] + case x => throw JsonTypeError("Only primitive types supported for TreeSet, NavigableSet, or SortedSet. You've specified " + x.name) + case "java.util.Queue" | "java.util.Deque" => + '{ + val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in, inTuple) }) + if parsedArray != null then new java.util.LinkedList(parsedArray.toList.asJava) + else null + }.asExprOf[T] + case "java.util.Set" => + '{ + val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in, inTuple) }) + if parsedArray != null then new java.util.HashSet(parsedArray.toList.asJava) + else null + }.asExprOf[T] + case "java.util.List" | "java.lang.Iterable" => + println(ref) + '{ + val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in, inTuple) }) + if parsedArray != null then new java.util.ArrayList(parsedArray.toList.asJava) + else null + }.asExprOf[T] + // Normal concrete Java Collecitons + case _ => + '{ + if $in.expectNull() then null + else + ${ + val arg = '{ + val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](t.elementRef.asInstanceOf[RTypeRef[e]], in, inTuple) }) + parsedArray.toList.asJava.asInstanceOf[java.util.Collection[e]] + }.asTerm + Select + .overloaded( + New(Inferred(TypeRepr.of[T])), + "", + List(TypeRepr.of[e]), + List(arg) + ) + .asExprOf[T] + } + }.asExprOf[T] // -------------------- // Maps... @@ -1684,7 +1713,6 @@ syntheticTA.write(t.asInstanceOf[L], writer, out) ) }.asExprOf[T] case '[Map[?, ?]] => // all other immutable Maps - println(ref) t.elementRef.refType match case '[k] => t.elementRef2.refType match @@ -1704,28 +1732,110 @@ syntheticTA.write(t.asInstanceOf[L], writer, out) .to(${ Expr.summon[Factory[(k, v), T]].get }) }.asExprOf[T] - // TODO: Find out why SeqMap fails with this summon here. Create a minimal project w/macro to test/show behavior. - // Note: All other Map subtypes seem to work ok. SortedMap, etc. No issues. Just SeqMap. - - /* - case _ => - t.elementRef.refType match - case '[k] => - t.elementRef2.refType match - case '[v] => + // -------------------- + // Java Maps... + // -------------------- + case t: JavaMapRef[?] => + t.elementRef.refType match + case '[k] => + t.elementRef2.refType match + case '[v] => + ref.name match + case "java.util.NavigableMap" | "java.util.SortedMap" | "java.util.TreeMap" => testValidMapKey(t.elementRef) '{ if $in.expectNull() then null else $in.expectToken('{') - $in.parseMap[k, v]( - () => ${ genReadVal[k](t.elementRef.asInstanceOf[RTypeRef[k]], in, inTuple, true) }, - () => ${ genReadVal[v](t.elementRef2.asInstanceOf[RTypeRef[v]], in, inTuple) }, - Map.empty[k, v], - true + new java.util.TreeMap( + $in + .parseMap[k, v]( + () => ${ genReadVal[k](t.elementRef.asInstanceOf[RTypeRef[k]], in, inTuple, true) }, + () => ${ genReadVal[v](t.elementRef2.asInstanceOf[RTypeRef[v]], in, inTuple) }, + Map.empty[k, v], + true + ) + .asJava ) }.asExprOf[T] - */ + case "java.util.concurrent.ConcurrentMap" => + testValidMapKey(t.elementRef) + '{ + if $in.expectNull() then null + else + $in.expectToken('{') + new java.util.concurrent.ConcurrentHashMap( + $in + .parseMap[k, v]( + () => ${ genReadVal[k](t.elementRef.asInstanceOf[RTypeRef[k]], in, inTuple, true) }, + () => ${ genReadVal[v](t.elementRef2.asInstanceOf[RTypeRef[v]], in, inTuple) }, + Map.empty[k, v], + true + ) + .asJava + ) + }.asExprOf[T] + case "java.util.concurrent.ConcurrentNavigableMap" => + testValidMapKey(t.elementRef) + '{ + if $in.expectNull() then null + else + $in.expectToken('{') + new java.util.concurrent.ConcurrentSkipListMap( + $in + .parseMap[k, v]( + () => ${ genReadVal[k](t.elementRef.asInstanceOf[RTypeRef[k]], in, inTuple, true) }, + () => ${ genReadVal[v](t.elementRef2.asInstanceOf[RTypeRef[v]], in, inTuple) }, + Map.empty[k, v], + true + ) + .asJava + ) + }.asExprOf[T] + case "java.util.Map" => + testValidMapKey(t.elementRef) + '{ + if $in.expectNull() then null + else + $in.expectToken('{') + new java.util.HashMap( + $in + .parseMap[k, v]( + () => ${ genReadVal[k](t.elementRef.asInstanceOf[RTypeRef[k]], in, inTuple, true) }, + () => ${ genReadVal[v](t.elementRef2.asInstanceOf[RTypeRef[v]], in, inTuple) }, + Map.empty[k, v], + true + ) + .asJava + ) + }.asExprOf[T] + case _ => + testValidMapKey(t.elementRef) + '{ + if $in.expectNull() then null + else + ${ + val arg = '{ + $in.expectToken('{') + $in + .parseMap[k, v]( + () => ${ genReadVal[k](t.elementRef.asInstanceOf[RTypeRef[k]], in, inTuple, true) }, + () => ${ genReadVal[v](t.elementRef2.asInstanceOf[RTypeRef[v]], in, inTuple) }, + Map.empty[k, v], + true + ) + .asJava + }.asTerm + Select + .overloaded( + New(Inferred(TypeRepr.of[T])), + "", + List(TypeRepr.of[k], TypeRepr.of[v]), + List(arg) + ) + .asExprOf[T] + } + }.asExprOf[T] // -------------------- // Tuples... diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index 6c6c612d..88174d3f 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -47,10 +47,14 @@ object RunMe extends App: import co.blocke.scalajack.json.run.Record println("\n") + val a: java.util.Set[Int] = new java.util.TreeSet[Int]() + val b: java.util.Set[Int] = new java.util.TreeSet[Int]() + val tt = new java.util.TreeSet[java.util.Set[Int]](java.util.Arrays.asList(a, b).asInstanceOf[java.util.Collection[java.util.Set[Int]]]) + println(tt) //case class JJ(a: java.util.Stack[Int]) - given blah: ScalaJack[JJ] = sjCodecOf[JJ] - val js = """{"a":[1,2,3]}""" - println(blah.fromJson(js)) + // given blah: ScalaJack[JJ] = sjCodecOf[JJ] + // val js = """{"a":[1,2,3]}""" + // println(blah.fromJson(js)) // println(RType.of[Person].pretty) diff --git a/src/test/scala/co.blocke.scalajack/json/collections/JavaCollSpec.scala b/src/test/scala/co.blocke.scalajack/json/collections/JavaCollSpec.scala index 2ffb4346..363b1e8b 100644 --- a/src/test/scala/co.blocke.scalajack/json/collections/JavaCollSpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/collections/JavaCollSpec.scala @@ -14,107 +14,262 @@ import java.util.{ArrayList, Arrays, HashSet} class JavaCollSpec() extends AnyFunSpec with JsonMatchers: describe(colorString("-------------------------------\n: Java Collection Tests :\n-------------------------------", Console.YELLOW)) { - describe(colorString("+++ Positive Tests +++")) { + describe(colorString("+++ Basic functions (Set) +++")) { it("Set is null must work") { val inst = JSetHolder[Int](null) - val js = sjCodecOf[JSetHolder[Int]].toJson(inst) + val sj = sjCodecOf[JSetHolder[Int]] + val js = sj.toJson(inst) js should matchJson("""{"a":null}""") + sj.fromJson(js) shouldEqual (inst) } it("Set of numeric must work") { val inst = JSetHolder[Int](HashSet(Arrays.asList(1, 2, 3))) - val js = sjCodecOf[JSetHolder[Int]].toJson(inst) + val sj = sjCodecOf[JSetHolder[Int]] + val js = sj.toJson(inst) js should matchJson("""{"a":[1,2,3]}""") + sj.fromJson(js) shouldEqual (inst) } it("Set of string must work") { val inst = JSetHolder[String](HashSet(Arrays.asList("a", "b", "c"))) - val js = sjCodecOf[JSetHolder[String]].toJson(inst) + val sj = sjCodecOf[JSetHolder[String]] + val js = sj.toJson(inst) js should matchJson("""{"a":["a","b","c"]}""") + sj.fromJson(js) shouldEqual (inst) } it("Set of boolean must work") { val inst = JSetHolder[Boolean](HashSet(Arrays.asList(true, false, true))) - val js = sjCodecOf[JSetHolder[Boolean]].toJson(inst) + val sj = sjCodecOf[JSetHolder[Boolean]] + val js = sj.toJson(inst) js should matchJson("""{"a":[false,true]}""") + sj.fromJson(js) shouldEqual (inst) } it("Set of Set (nested) must work") { - val inst = JSetHolder[List[Int]](HashSet(Arrays.asList(List(1, 2), List(3, 4)))) - val js = sjCodecOf[JSetHolder[List[Int]]].toJson(inst) - js should matchJson("""{"a":[[3,4],[1,2]]}""") + import java.util.TreeSet as JTreeSet + import java.util.Comparator + val setOfSets: java.util.Set[java.util.Set[Int]] = new JTreeSet[java.util.Set[Int]](new Comparator[java.util.Set[Int]] { + override def compare(o1: java.util.Set[Int], o2: java.util.Set[Int]): Int = -1 + }) + val a: java.util.Set[Int] = new JTreeSet() + a.add(1) + a.add(2) + val b: java.util.Set[Int] = new JTreeSet() + b.add(3) + b.add(4) + setOfSets.add(b) + setOfSets.add(a) + val inst = JSetHolder[java.util.Set[Int]](setOfSets) + val sj = sjCodecOf[JSetHolder[java.util.Set[Int]]] + val js = sj.toJson(inst) + js should (matchJson("""{"a":[[1,2],[3,4]]}""") or matchJson("""{"a":[[3,4],[1,2]]}""")) + sj.fromJson(js) shouldEqual (inst) } it("Set of either must work") { val inst = JSetHolder[Either[Int, Boolean]](HashSet(Arrays.asList(Right(true), Left(15), Right(false)))) - val js = sjCodecOf[JSetHolder[Either[Int, Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)).toJson(inst) + val sj = sjCodecOf[JSetHolder[Either[Int, Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)) + val js = sj.toJson(inst) js should matchJson("""{"a":[true,15,false]}""") + sj.fromJson(js) shouldEqual (inst) } it("Set of union must work") { val inst = JSetHolder[Int | Boolean](HashSet(Arrays.asList(true, 15, false))) - val js = sjCodecOf[JSetHolder[Int | Boolean]].toJson(inst) + val sj = sjCodecOf[JSetHolder[Int | Boolean]] + val js = sj.toJson(inst) js should matchJson("""{"a":[false,true,15]}""") + sj.fromJson(js) shouldEqual (inst) } it("Set of option must work") { val inst = JSetHolder[Option[Int]](HashSet(Arrays.asList(Some(1), None, Some(3)))) - val js = sjCodecOf[JSetHolder[Option[Int]]].toJson(inst) + val sj = sjCodecOf[JSetHolder[Option[Int]]] + val js = sj.toJson(inst) js should matchJson("""{"a":[1,3]}""") + sj.fromJson(js) shouldEqual (JSetHolder[Option[Int]](HashSet(Arrays.asList(Some(1), Some(3))))) } it("Set of map must work") { val inst = JSetHolder[Map[String, Int]](HashSet(Arrays.asList(Map("a" -> 1, "b" -> 2), Map("c" -> 3, "d" -> 4)))) - val js = sjCodecOf[JSetHolder[Map[String, Int]]].toJson(inst) + val sj = sjCodecOf[JSetHolder[Map[String, Int]]] + val js = sj.toJson(inst) js should matchJson("""{"a":[{"a":1,"b":2},{"c":3,"d":4}]}""") + sj.fromJson(js) shouldEqual (inst) } it("Set of class must work") { val inst = JSetHolder[Person](HashSet(Arrays.asList(Person("Bob", 35), Person("Sally", 54)))) - val js = sjCodecOf[JSetHolder[Person]].toJson(inst) + val sj = sjCodecOf[JSetHolder[Person]] + val js = sj.toJson(inst) js should matchJson("""{"a":[{"name":"Bob","age",35},{"name":"Sally","age",54}]}""") + sj.fromJson(js) shouldEqual (inst) } + } + describe(colorString("+++ Basic functions (ArrayList) +++")) { it("ArrayList is null must work") { val inst = ArrayListHolder[Int](null) - val js = sjCodecOf[ArrayListHolder[Int]].toJson(inst) + val sj = sjCodecOf[ArrayListHolder[Int]] + val js = sj.toJson(inst) js should matchJson("""{"a":null}""") + sj.fromJson(js) shouldEqual (inst) } it("ArrayList of numeric must work") { val inst = ArrayListHolder[Int](ArrayList[Int](Arrays.asList(1, 2, 3))) - val js = sjCodecOf[ArrayListHolder[Int]].toJson(inst) + val sj = sjCodecOf[ArrayListHolder[Int]] + val js = sj.toJson(inst) js should matchJson("""{"a":[1,2,3]}""") + sj.fromJson(js) shouldEqual (inst) } it("ArrayList of string must work") { val inst = ArrayListHolder[String](ArrayList[String](Arrays.asList("a", "b", "c"))) - val js = sjCodecOf[ArrayListHolder[String]].toJson(inst) + val sj = sjCodecOf[ArrayListHolder[String]] + val js = sj.toJson(inst) js should matchJson("""{"a":["a","b","c"]}""") + sj.fromJson(js) shouldEqual (inst) } it("ArrayList of boolean must work") { val inst = ArrayListHolder[Boolean](ArrayList[Boolean](Arrays.asList(true, false, true))) - val js = sjCodecOf[ArrayListHolder[Boolean]].toJson(inst) + val sj = sjCodecOf[ArrayListHolder[Boolean]] + val js = sj.toJson(inst) js should matchJson("""{"a":[true,false,true]}""") + sj.fromJson(js) shouldEqual (inst) } it("ArrayList of ArrayList (nested) must work") { val inst = ArrayListHolder[ArrayList[Int]](ArrayList[ArrayList[Int]](Arrays.asList(ArrayList(Arrays.asList(1, 2)), ArrayList(Arrays.asList(3, 4))))) - val js = sjCodecOf[ArrayListHolder[ArrayList[Int]]].toJson(inst) + val sj = sjCodecOf[ArrayListHolder[ArrayList[Int]]] + val js = sj.toJson(inst) js should matchJson("""{"a":[[1,2],[3,4]]}""") + sj.fromJson(js) shouldEqual (inst) } it("ArrayList of either must work") { val inst = ArrayListHolder[Either[Int, Boolean]](ArrayList[Either[Int, Boolean]](Arrays.asList(Right(true), Left(15), Right(false)))) - val js = sjCodecOf[ArrayListHolder[Either[Int, Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)).toJson(inst) + val sj = sjCodecOf[ArrayListHolder[Either[Int, Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)) + val js = sj.toJson(inst) js should matchJson("""{"a":[true,15,false]}""") + sj.fromJson(js) shouldEqual (inst) } it("ArrayList of union must work") { val inst = ArrayListHolder[Int | Boolean](ArrayList[Int | Boolean](Arrays.asList(true, 15, false))) - val js = sjCodecOf[ArrayListHolder[Int | Boolean]].toJson(inst) + val sj = sjCodecOf[ArrayListHolder[Int | Boolean]] + val js = sj.toJson(inst) js should matchJson("""{"a":[true,15,false]}""") + sj.fromJson(js) shouldEqual (inst) } it("ArrayList of option must work") { val inst = ArrayListHolder[Option[Int]](ArrayList[Option[Int]](Arrays.asList(Some(1), None, Some(3)))) - val js = sjCodecOf[ArrayListHolder[Option[Int]]].toJson(inst) + val sj = sjCodecOf[ArrayListHolder[Option[Int]]] + val js = sj.toJson(inst) js should matchJson("""{"a":[1,3]}""") + sj.fromJson(js) shouldEqual (ArrayListHolder[Option[Int]](ArrayList[Option[Int]](Arrays.asList(Some(1), Some(3))))) } it("ArrayList of map must work") { val inst = ArrayListHolder[Map[String, Int]](ArrayList[Map[String, Int]](Arrays.asList(Map("a" -> 1, "b" -> 2), Map("c" -> 3, "d" -> 4)))) - val js = sjCodecOf[ArrayListHolder[Map[String, Int]]].toJson(inst) + val sj = sjCodecOf[ArrayListHolder[Map[String, Int]]] + val js = sj.toJson(inst) js should matchJson("""{"a":[{"a":1,"b":2},{"c":3,"d":4}]}""") + sj.fromJson(js) shouldEqual (inst) } it("ArrayList of class must work") { val inst = ArrayListHolder[Person](ArrayList[Person](Arrays.asList(Person("Bob", 35), Person("Sally", 54)))) - val js = sjCodecOf[ArrayListHolder[Person]].toJson(inst) + val sj = sjCodecOf[ArrayListHolder[Person]] + val js = sj.toJson(inst) js should matchJson("""{"a":[{"name":"Bob","age",35},{"name":"Sally","age",54}]}""") + sj.fromJson(js) shouldEqual (inst) + } + } + + describe(colorString("+++ Coersions (special cases, traits, etc) +++")) { + it("ArrayBlockingQueue") { + val q = new java.util.concurrent.ArrayBlockingQueue[Int](2, true, Arrays.asList(1, 2)) + val inst = Holder[java.util.concurrent.ArrayBlockingQueue[Int]](q) + val sj = sjCodecOf[Holder[java.util.concurrent.ArrayBlockingQueue[Int]]] + val js = sj.toJson(inst) + js should matchJson("""{"a":[1,2]}""") + val x = sj.fromJson(js) + x.a.getClass.getName shouldEqual (inst.a.getClass.getName) + x.a should contain(1) + x.a should contain(2) + x.a.size shouldEqual (2) + } + it("TreeSet") { + val ts = new java.util.TreeSet[Int](Arrays.asList(1, 2, 3)) + val inst = Holder(ts) + val sj = sjCodecOf[Holder[java.util.TreeSet[Int]]] + val js = sj.toJson(inst) + js should matchJson("""{"a":[1,2,3]}""") + sj.fromJson(js) shouldEqual (inst) + } + it("Stack") { + val ts = new java.util.Stack[String]() + ts.push("x") + ts.push("y") + ts.push("z") + val inst = Holder(ts) + val sj = sjCodecOf[Holder[java.util.Stack[String]]] + val js = sj.toJson(inst) + js should matchJson("""{"a":["x","y","z"]}""") + sj.fromJson(js) shouldEqual (inst) + } + it("List") { + val ts: java.util.List[Int] = new java.util.ArrayList[Int](Arrays.asList(1, 2, 3)) + val inst = Holder(ts) + val sj = sjCodecOf[Holder[java.util.List[Int]]] + val js = sj.toJson(inst) + js should matchJson("""{"a":[1,2,3]}""") + sj.fromJson(js) shouldEqual (inst) + } + it("Iterable") { + val ts: java.lang.Iterable[Int] = new java.util.ArrayList[Int](Arrays.asList(1, 2, 3)) + val inst = Holder(ts) + val sj = sjCodecOf[Holder[java.lang.Iterable[Int]]] + val js = sj.toJson(inst) + js should matchJson("""{"a":[1,2,3]}""") + sj.fromJson(js) shouldEqual (inst) + } + it("Queue") { + val ts: java.util.Queue[Int] = new java.util.LinkedList[Int](Arrays.asList(1, 2, 3)) + val inst = Holder(ts) + val sj = sjCodecOf[Holder[java.util.Queue[Int]]] + val js = sj.toJson(inst) + js should matchJson("""{"a":[1,2,3]}""") + sj.fromJson(js) shouldEqual (inst) + } + it("Deque") { + val ts: java.util.Deque[Int] = new java.util.LinkedList[Int](Arrays.asList(1, 2, 3)) + val inst = Holder(ts) + val sj = sjCodecOf[Holder[java.util.Deque[Int]]] + val js = sj.toJson(inst) + js should matchJson("""{"a":[1,2,3]}""") + sj.fromJson(js) shouldEqual (inst) + } + it("BlockingQueue") { + val ts: java.util.concurrent.BlockingQueue[Int] = new java.util.concurrent.LinkedBlockingQueue[Int](Arrays.asList(1, 2, 3)) + val inst = Holder(ts) + val sj = sjCodecOf[Holder[java.util.concurrent.BlockingQueue[Int]]] + val js = sj.toJson(inst) + js should matchJson("""{"a":[1,2,3]}""") + val x = sj.fromJson(js) + x.a.getClass.getName shouldEqual (inst.a.getClass.getName) + x.a should contain(1) + x.a should contain(2) + x.a should contain(3) + x.a.size shouldEqual (3) + } + it("TransferQueue") { + val ts: java.util.concurrent.TransferQueue[Int] = new java.util.concurrent.LinkedTransferQueue[Int](Arrays.asList(1, 2, 3)) + val inst = Holder(ts) + val sj = sjCodecOf[Holder[java.util.concurrent.TransferQueue[Int]]] + val js = sj.toJson(inst) + js should matchJson("""{"a":[1,2,3]}""") + val x = sj.fromJson(js) + x.a.getClass.getName shouldEqual (inst.a.getClass.getName) + x.a should contain(1) + x.a should contain(2) + x.a should contain(3) + x.a.size shouldEqual (3) + } + it("Vector (generic Collection example)") { + val ts = new java.util.Vector[Int](Arrays.asList(1, 2, 3)) + val inst = Holder(ts) + val sj = sjCodecOf[Holder[java.util.Vector[Int]]] + val js = sj.toJson(inst) + js should matchJson("""{"a":[1,2,3]}""") + sj.fromJson(js) shouldEqual (inst) } } } diff --git a/src/test/scala/co.blocke.scalajack/json/collections/JavaMapSpec.scalax b/src/test/scala/co.blocke.scalajack/json/collections/JavaMapSpec.scala similarity index 50% rename from src/test/scala/co.blocke.scalajack/json/collections/JavaMapSpec.scalax rename to src/test/scala/co.blocke.scalajack/json/collections/JavaMapSpec.scala index cbf0a57d..924f6057 100644 --- a/src/test/scala/co.blocke.scalajack/json/collections/JavaMapSpec.scalax +++ b/src/test/scala/co.blocke.scalajack/json/collections/JavaMapSpec.scala @@ -14,116 +14,216 @@ import java.util.UUID class JavaMapSpec() extends AnyFunSpec with JsonMatchers: describe(colorString("-------------------------------\n: Java Map Tests :\n-------------------------------", Console.YELLOW)) { - describe(colorString("+++ Positive Tests +++")) { + describe(colorString("+++ General Map Interface Tests +++")) { it("Map is null must work") { val inst = JMapHolder[Int, Int](null) - val js = sj[JMapHolder[Int, Int]].toJson(inst) + val sj = sjCodecOf[JMapHolder[Int, Int]] + val js = sj.toJson(inst) js should matchJson("""{"a":null}""") + sj.fromJson(js) shouldEqual(inst) } it("Map key of string must work") { val m: java.util.Map[String, Int] = new java.util.HashMap[String, Int]() m.put("x", 1) m.put("y", 2) val inst = JMapHolder[String, Int](new java.util.HashMap(m)) - val js = sj[JMapHolder[String, Int]].toJson(inst) + val sj = sjCodecOf[JMapHolder[String, Int]] + val js = sj.toJson(inst) js should matchJson("""{"a":{"x":1,"y":2}}""") + sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual(inst) } it("Map key of long must work") { val m: java.util.Map[Long, Int] = new java.util.HashMap[Long, Int]() m.put(15L, 1) m.put(25L, 2) val inst = JMapHolder[Long, Int](new java.util.HashMap(m)) - val js = sj[JMapHolder[Long, Int]].toJson(inst) + val sj = sjCodecOf[JMapHolder[Long, Int]] + val js = sj.toJson(inst) js should matchJson("""{"a":{"15":1,"25":2}}""") + sj.fromJson(js) shouldEqual(inst) } it("Map key of boolean must work") { val m: java.util.Map[Boolean, Int] = new java.util.HashMap[Boolean, Int]() m.put(true, 1) m.put(false, 2) val inst = JMapHolder[Boolean, Int](new java.util.HashMap(m)) - val js = sj[JMapHolder[Boolean, Int]].toJson(inst) + val sj = sjCodecOf[JMapHolder[Boolean, Int]] + val js = sj.toJson(inst) js should matchJson("""{"a":{"true":1,"false":2}}""") + sj.fromJson(js) shouldEqual(inst) } it("Map key of uuid must work") { val m: java.util.Map[UUID, String] = new java.util.HashMap[UUID, String]() m.put(UUID.fromString("1b9ab03f-26a3-4ec5-a8dd-d5122ff86b03"), "x") m.put(UUID.fromString("09abdeb1-8b07-4683-8f97-1f5621696008"), "y") val inst = JMapHolder[UUID, String](new java.util.HashMap(m)) - val js = sj[JMapHolder[UUID, String]].toJson(inst) + val sj = sjCodecOf[JMapHolder[UUID, String]] + val js = sj.toJson(inst) js should matchJson("""{"a":{"1b9ab03f-26a3-4ec5-a8dd-d5122ff86b03":"x","09abdeb1-8b07-4683-8f97-1f5621696008":"y"}}""") + sj.fromJson(js) shouldEqual(inst) + } + it("Map key of value class must work") { + val m: java.util.Map[Distance, String] = new java.util.HashMap[Distance, String]() + m.put(Distance(1.23), "w") + m.put(Distance(4.56), "y") + val inst = JMapHolder[Distance, String](new java.util.HashMap(m)) + val sj = sjCodecOf[JMapHolder[Distance, String]] + val js = sj.toJson(inst) + js should matchJson("""{"a":{"1.23":"w","4.56":"y"}}""") + sj.fromJson(js) shouldEqual(inst) } - it("Map value of string must work") { val m: java.util.Map[String, String] = new java.util.HashMap[String, String]() m.put("w", "x") m.put("y", "z") val inst = JMapHolder[String, String](new java.util.HashMap(m)) - val js = sj[JMapHolder[String, String]].toJson(inst) + val sj = sjCodecOf[JMapHolder[String, String]] + val js = sj.toJson(inst) js should matchJson("""{"a":{"w":"x","y":"z"}}""") + sj.fromJson(js) shouldEqual(inst) } it("Map value of long must work") { val m: java.util.Map[String, Long] = new java.util.HashMap[String, Long]() m.put("w", 3L) m.put("y", 4L) val inst = JMapHolder[String, Long](new java.util.HashMap(m)) - val js = sj[JMapHolder[String, Long]].toJson(inst) + val sj = sjCodecOf[JMapHolder[String, Long]] + val js = sj.toJson(inst) js should matchJson("""{"a":{"w":3,"y":4}}""") + sj.fromJson(js) shouldEqual(inst) } it("Map value of boolean must work") { val m: java.util.Map[String, Boolean] = new java.util.HashMap[String, Boolean]() m.put("w", true) m.put("y", false) val inst = JMapHolder[String, Boolean](new java.util.HashMap(m)) - val js = sj[JMapHolder[String, Boolean]].toJson(inst) + val sj = sjCodecOf[JMapHolder[String, Boolean]] + val js = sj.toJson(inst) js should matchJson("""{"a":{"w":true,"y":false}}""") + sj.fromJson(js) shouldEqual(inst) } it("Map value of uuid must work") { val m: java.util.Map[String, UUID] = new java.util.HashMap[String, UUID]() m.put("x", UUID.fromString("1b9ab03f-26a3-4ec5-a8dd-d5122ff86b03")) m.put("y", UUID.fromString("09abdeb1-8b07-4683-8f97-1f5621696008")) val inst = JMapHolder[String, UUID](new java.util.HashMap(m)) - val js = sj[JMapHolder[String, UUID]].toJson(inst) + val sj = sjCodecOf[JMapHolder[String, UUID]] + val js = sj.toJson(inst) js should matchJson("""{"a":{"x":"1b9ab03f-26a3-4ec5-a8dd-d5122ff86b03","y":"09abdeb1-8b07-4683-8f97-1f5621696008"}}""") + sj.fromJson(js) shouldEqual(inst) } it("Map value of Seq must work") { val m: java.util.Map[String, List[Int]] = new java.util.HashMap[String, List[Int]]() m.put("w", List(1, 2)) m.put("y", List(3, 4)) val inst = JMapHolder[String, List[Int]](new java.util.HashMap(m)) - val js = sj[JMapHolder[String, List[Int]]].toJson(inst) + val sj = sjCodecOf[JMapHolder[String, List[Int]]] + val js = sj.toJson(inst) js should matchJson("""{"a":{"w":[1,2],"y":[3,4]}}""") + sj.fromJson(js) shouldEqual(inst) } it("Map value of Map (nested) must work") { val m: java.util.Map[String, Map[String, Int]] = new java.util.HashMap[String, Map[String, Int]]() m.put("w", Map("r" -> 3, "t" -> 4)) m.put("y", Map("s" -> 7, "q" -> 9)) val inst = JMapHolder[String, Map[String, Int]](new java.util.HashMap(m)) - val js = sj[JMapHolder[String, Map[String, Int]]].toJson(inst) + val sj = sjCodecOf[JMapHolder[String, Map[String, Int]]] + val js = sj.toJson(inst) js should matchJson("""{"a":{"w":{"r":3,"t":4},"y":{"s":7,"q":9}}}""") + sj.fromJson(js) shouldEqual(inst) } it("Map value of class must work") { val m: java.util.Map[String, Person] = new java.util.HashMap[String, Person]() m.put("w", Person("Bob", 34)) m.put("y", Person("Sally", 25)) val inst = JMapHolder[String, Person](new java.util.HashMap(m)) - val js = sj[JMapHolder[String, Person]].toJson(inst) + val sj = sjCodecOf[JMapHolder[String, Person]] + val js = sj.toJson(inst) js should matchJson("""{"a":{"w":{"name":"Bob","age":34},"y":{"name":"Sally","age":25}}}""") + sj.fromJson(js) shouldEqual(inst) } it("Map value of union type must work") { val m: java.util.Map[String, Int | List[String]] = new java.util.HashMap[String, Int | List[String]]() m.put("w", 3) m.put("y", List("wow", "blah")) val inst = JMapHolder[String, Int | List[String]](new java.util.HashMap(m)) - val js = sj[JMapHolder[String, Int | List[String]]].toJson(inst) + val sj = sjCodecOf[JMapHolder[String, Int | List[String]]] + val js = sj.toJson(inst) js should matchJson("""{"a":{"w":3,"y":["wow","blah"]}}""") + sj.fromJson(js) shouldEqual(inst) } it("Map value of value class must work") { val m: java.util.Map[String, Distance] = new java.util.HashMap[String, Distance]() m.put("w", new Distance(1.23)) m.put("y", Distance(4.56)) val inst = JMapHolder[String, Distance](new java.util.HashMap(m)) - val js = sj[JMapHolder[String, Distance]].toJson(inst) + val sj = sjCodecOf[JMapHolder[String, Distance]] + val js = sj.toJson(inst) js should matchJson("""{"a":{"w":1.23,"y":4.56}}""") + sj.fromJson(js) shouldEqual(inst) + } + } + describe(colorString("+++ Special/Specific Map Tests +++")) { + it("NavigableMap must work") { + val m: java.util.NavigableMap[String, Int] = new java.util.TreeMap[String, Int]() + m.put("x", 1) + m.put("y", 2) + val inst = Holder[java.util.NavigableMap[String, Int]](m) + val sj = sjCodecOf[Holder[java.util.NavigableMap[String, Int]]] + val js = sj.toJson(inst) + js should matchJson("""{"a":{"x":1,"y":2}}""") + sj.fromJson(js) shouldEqual(inst) + } + it("SortedMap must work") { + val m: java.util.SortedMap[String, Int] = new java.util.TreeMap[String, Int]() + m.put("x", 1) + m.put("y", 2) + val inst = Holder[java.util.SortedMap[String, Int]](m) + val sj = sjCodecOf[Holder[java.util.SortedMap[String, Int]]] + val js = sj.toJson(inst) + js should matchJson("""{"a":{"x":1,"y":2}}""") + sj.fromJson(js) shouldEqual(inst) + } + it("TreeMap must work") { + val m = new java.util.TreeMap[String, Int]() + m.put("x", 1) + m.put("y", 2) + val inst = Holder[java.util.TreeMap[String, Int]](m) + val sj = sjCodecOf[Holder[java.util.TreeMap[String, Int]]] + val js = sj.toJson(inst) + js should matchJson("""{"a":{"x":1,"y":2}}""") + sj.fromJson(js) shouldEqual(inst) + } + it("ConcurrentMap must work") { + val m = new java.util.concurrent.ConcurrentHashMap[String, Int]() + m.put("x", 1) + m.put("y", 2) + val inst = Holder[java.util.concurrent.ConcurrentMap[String, Int]](m) + val sj = sjCodecOf[Holder[java.util.concurrent.ConcurrentMap[String, Int]]] + val js = sj.toJson(inst) + js should matchJson("""{"a":{"x":1,"y":2}}""") + sj.fromJson(js) shouldEqual(inst) + } + it("ConcurrentNavigableMap must work") { + val m = new java.util.concurrent.ConcurrentSkipListMap[String, Int]() + m.put("x", 1) + m.put("y", 2) + val inst = Holder[java.util.concurrent.ConcurrentNavigableMap[String, Int]](m) + val sj = sjCodecOf[Holder[java.util.concurrent.ConcurrentNavigableMap[String, Int]]] + val js = sj.toJson(inst) + js should matchJson("""{"a":{"x":1,"y":2}}""") + sj.fromJson(js) shouldEqual(inst) + } + it("Specific must work (eg HashMap)") { + val m = new java.util.HashMap[String, Int]() + m.put("x", 1) + m.put("y", 2) + val inst = Holder[java.util.HashMap[String, Int]](m) + val sj = sjCodecOf[Holder[java.util.HashMap[String, Int]]] + val js = sj.toJson(inst) + js should matchJson("""{"a":{"x":1,"y":2}}""") + sj.fromJson(js) shouldEqual(inst) } } } diff --git a/src/test/scala/co.blocke.scalajack/json/collections/Model.scala b/src/test/scala/co.blocke.scalajack/json/collections/Model.scala index 012b1007..b097c90c 100644 --- a/src/test/scala/co.blocke.scalajack/json/collections/Model.scala +++ b/src/test/scala/co.blocke.scalajack/json/collections/Model.scala @@ -12,6 +12,7 @@ opaque type Counter = Short case class SeqHolder[T](a: Seq[T]) case class SetHolder[T](a: Set[T]) case class ArrayHolder[T](a: Array[T]) +case class Holder[T](a: T) case class MapHolder[T, V](a: Map[T, V]) case class MapHolder2[T, V](a: scala.collection.immutable.HashMap[T, V]) // specific diff --git a/src/test/scala/co.blocke.scalajack/json/collections/TupleSpec.scala b/src/test/scala/co.blocke.scalajack/json/collections/TupleSpec.scala index 25017c8d..b74f8045 100644 --- a/src/test/scala/co.blocke.scalajack/json/collections/TupleSpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/collections/TupleSpec.scala @@ -29,11 +29,13 @@ class TupleSpec() extends AnyFunSpec with JsonMatchers: js should matchJson("""{"a":[15,"wow",true]}""") sj.fromJson(js) shouldEqual inst } - // it("Tuple of collecitons (including another tuple) must work") { - // val inst = TupleHolder[Seq[Int], Map[String, Long], (Double, Char, Boolean)]((List(1, 2), Map("a" -> 3L, "b" -> 4L), (1.23d, 'X', true))) - // val js = sjCodecOf[TupleHolder[Seq[Int], Map[String, Long], (Double, Char, Boolean)]].toJson(inst) - // js should matchJson("""{"a":[[1,2],{"a":3,"b":4},[1.23,"X",true]]}""") - // } + it("Tuple of collecitons (including another tuple) must work") { + val inst = TupleHolder[Seq[Int], Map[String, Long], (Double, Char, Boolean)]((List(1, 2), Map("a" -> 3L, "b" -> 4L), (1.23d, 'X', true))) + val sj = sjCodecOf[TupleHolder[Seq[Int], Map[String, Long], (Double, Char, Boolean)]] + val js = sj.toJson(inst) + js should matchJson("""{"a":[[1,2],{"a":3,"b":4},[1.23,"X",true]]}""") + sj.fromJson(js) shouldEqual inst + } } describe(colorString("--- Negative Tests ---")) { diff --git a/src/test/scala/co.blocke.scalajack/json/misc/AliasSpec.scala b/src/test/scala/co.blocke.scalajack/json/misc/AliasSpec.scala index 398ebb17..e3db4cb1 100644 --- a/src/test/scala/co.blocke.scalajack/json/misc/AliasSpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/misc/AliasSpec.scala @@ -25,35 +25,35 @@ class AliasSpec() extends AnyFunSpec with JsonMatchers: val sj = sjCodecOf[AliasHolder[Count]] val js = sj.toJson(inst) js should matchJson("""{"a":5,"b":[1,2,3],"c":{"1":"wow"},"d":{"wow":2}}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Type aliases (opaque types) must be dereferenced (with Option)") { val inst = AliasHolder2[CountX](Some(5), List(Some(1), None, Some(3)), Map("wow" -> None)) val sj = sjCodecOf[AliasHolder2[CountX]] val js = sj.toJson(inst) js should matchJson("""{"a":5,"b":[1,3],"c":{}}""") - sj.fromJson(js) shouldEqual(AliasHolder2[CountX](Some(5), List(Some(1),Some(3)), Map.empty[String,CountX])) + sj.fromJson(js) shouldEqual (AliasHolder2[CountX](Some(5), List(Some(1), Some(3)), Map.empty[String, CountX])) } it("Type aliases (opaque types) must be dereferenced (with Option, noneAsNull)") { val inst = AliasHolder2[CountX](Some(5), List(Some(1), None, Some(3)), Map("wow" -> None)) val sj = sjCodecOf[AliasHolder2[CountX]](JsonConfig.withNoneAsNull()) val js = sj.toJson(inst) js should matchJson("""{"a":5,"b":[1,null,3],"c":{"wow":null}}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Type aliases (non-opaque types) must be dereferenced") { val inst = AliasHolder[CountY]("q", List("r", "s", "t"), Map("u" -> "wow"), Map("wow" -> "v")) val sj = sjCodecOf[AliasHolder[CountY]] val js = sj.toJson(inst) js should matchJson("""{"a":"q","b":["r","s","t"],"c":{"u":"wow"},"d":{"wow":"v"}}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Type aliases (non-opaque types) must be dereferenced (with Option)") { val inst = AliasHolder2[CountZ](Some("q"), List(Some("r"), None, Some("t")), Map("wow" -> None)) val sj = sjCodecOf[AliasHolder2[CountZ]] val js = sj.toJson(inst) js should matchJson("""{"a":"q","b":["r","t"],"c":{}}""") - sj.fromJson(js) shouldEqual(AliasHolder2[CountZ](Some("q"), List(Some("r"),Some("t")), Map.empty[String,CountZ])) + sj.fromJson(js) shouldEqual (AliasHolder2[CountZ](Some("q"), List(Some("r"), Some("t")), Map.empty[String, CountZ])) } } } diff --git a/src/test/scala/co.blocke.scalajack/json/misc/TrySpec.scala b/src/test/scala/co.blocke.scalajack/json/misc/TrySpec.scala index 7b77bb15..baf08677 100644 --- a/src/test/scala/co.blocke.scalajack/json/misc/TrySpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/misc/TrySpec.scala @@ -22,42 +22,42 @@ class TrySpec() extends AnyFunSpec with JsonMatchers: val sj = sjCodecOf[TryHolder[Int]] val js = sj.toJson(inst) js should matchJson("""{"a":15}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Try of Option (non-None) must work (Success)") { val inst = TryHolder[Option[Int]](Success(Some(15))) val sj = sjCodecOf[TryHolder[Option[Int]]] val js = sj.toJson(inst) js should matchJson("""{"a":15}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Try of Option (None) must work (Success)") { val inst = TryHolder[Option[Int]](Success(None)) val sj = sjCodecOf[TryHolder[Option[Int]]] val js = sj.toJson(inst) js should matchJson("""{}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Try of Optional (non-None) must work (Success)") { val inst = TryHolder[Optional[Int]](Success(Optional.of(15))) val sj = sjCodecOf[TryHolder[Optional[Int]]] val js = sj.toJson(inst) js should matchJson("""{"a":15}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Try of Optional (None) must work (Success)") { val inst = TryHolder[Optional[Int]](Success(Optional.empty)) val sj = sjCodecOf[TryHolder[Optional[Int]]] val js = sj.toJson(inst) js should matchJson("""{}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Try w/policy AS_NULL must work (Failure)") { val inst = TryHolder[Int](Failure(new Exception("boom"))) val sj = sjCodecOf[TryHolder[Int]](JsonConfig.withTryFailureHandling(TryPolicy.AS_NULL)) val js = sj.toJson(inst) js should matchJson("""{"a":null}""") - sj.fromJson(js) shouldEqual(TryHolder[Int](null)) + sj.fromJson(js) shouldEqual (TryHolder[Int](null)) } it("Try w/policy ERR_MSG_STRING must work (Failure)") { val inst = TryHolder[Int](Failure(new Exception("boom"))) @@ -67,7 +67,7 @@ class TrySpec() extends AnyFunSpec with JsonMatchers: val msg = """Unsuccessful attempt to read Try type with failure: Non-numeric character found when integer value expected at position 5 at position [5] |{"a":"Try Failure with msg: boom"} |-----^""".stripMargin - val err = intercept[JsonParseError] { sj.fromJson(js) } + val err = intercept[JsonParseError](sj.fromJson(js)) err.show shouldEqual msg } it("Try w/policy ATHROW_EXCEPTIONS_NULL must work (Failure)") { @@ -83,7 +83,7 @@ class TrySpec() extends AnyFunSpec with JsonMatchers: val sj = sjCodecOf[TryHolder2[Int]](JsonConfig.withTryFailureHandling(TryPolicy.AS_NULL)) val js = sj.toJson(inst) js should matchJson("""{"a":[1,null,3],"b":[null,0]}""") - sj.fromJson(js) shouldEqual(TryHolder2[Int](List(Success(1), null, Success(3)), (null, Success(0)))) + sj.fromJson(js) shouldEqual (TryHolder2[Int](List(Success(1), null, Success(3)), (null, Success(0)))) } } - } \ No newline at end of file + } From d802efcdbeb90c849c299190c17503eb92e4979f Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Sat, 13 Apr 2024 23:23:53 -0500 Subject: [PATCH 55/65] Many fixes for classes --- Z | 17685 ---------------- .../src/main/scala/co.blocke/ScalaJack.scala | 2 - .../json/JsonCodecMaker.scala | 211 +- .../co.blocke.scalajack/json/JsonConfig.scala | 43 +- .../co.blocke.scalajack/json/package.scala | 6 +- .../json/reading/JsonSource.scala | 28 +- .../json/classes/ClassSpec.scala | 143 + .../json/classes/ClassSpec.scalax | 109 - .../json/classes/Model.scala | 19 +- .../json/classes/TraitSpec.scalax | 47 + .../json/collections/JavaMapSpec.scala | 44 +- 11 files changed, 464 insertions(+), 17873 deletions(-) delete mode 100644 Z create mode 100644 src/test/scala/co.blocke.scalajack/json/classes/ClassSpec.scala delete mode 100644 src/test/scala/co.blocke.scalajack/json/classes/ClassSpec.scalax create mode 100644 src/test/scala/co.blocke.scalajack/json/classes/TraitSpec.scalax diff --git a/Z b/Z deleted file mode 100644 index 128b233f..00000000 --- a/Z +++ /dev/null @@ -1,17685 +0,0 @@ -[info] welcome to sbt 1.9.6 (Homebrew Java 19.0.2) -[info] loading global plugins from /Users/gzoller/.sbt/1.0/plugins -[info] loading settings for project scalajack-build-build-build from metals.sbt ... -[info] loading project definition from /Users/gzoller/me/git/ScalaJack/project/project/project -[info] loading settings for project scalajack-build-build from metals.sbt ... -[info] loading project definition from /Users/gzoller/me/git/ScalaJack/project/project -[success] Generated .bloop/scalajack-build-build.json -[success] Total time: 1 s, completed Apr 6, 2024, 11:44:09 PM -[info] loading settings for project scalajack-build from metals.sbt,plugins.sbt ... -[info] loading project definition from /Users/gzoller/me/git/ScalaJack/project -[success] Generated .bloop/scalajack-build.json -[success] Total time: 0 s, completed Apr 6, 2024, 11:44:09 PM -[info] loading settings for project root from build.sbt ... ->>> Unknown gitflow branch: newworld ->>> Unknown gitflow branch: newworld -Packaged artifact name: scalajack_unknown-tests.jar -Packaged artifact name: scalajack_unknown.jar ->>> Unknown gitflow branch: newworld -Packaged artifact name: scalajack_unknown.jar ->>> Unknown gitflow branch: newworld -Packaged artifact name: scalajack_unknown-sources.jar ->>> Unknown gitflow branch: newworld -Packaged artifact name: scalajack_unknown-tests-sources.jar ->>> Unknown gitflow branch: newworld -Packaged artifact name: scalajack_unknown.jar ->>> Unknown gitflow branch: newworld ->>> Unknown gitflow branch: newworld -Packaged artifact name: scalajack_unknown.pom -Packaged artifact name: scalajack_unknown.pom ->>> Unknown gitflow branch: newworld -Packaged artifact name: scalajack_unknown-javadoc.jar ->>> Unknown gitflow branch: newworld -Packaged artifact name: scalajack_unknown-tests-javadoc.jar ->>> Unknown gitflow branch: newworld -Packaged artifact name: scalajack_unknown-cached-compile.jar ->>> Unknown gitflow branch: newworld -Packaged artifact name: scalajack_unknown-cached-test.jar -[info] set current project to scalajack (in build file:/Users/gzoller/me/git/ScalaJack/) -[info] scalafmt: Formatting 17 Scala sources (/Users/gzoller/me/git/ScalaJack)... -[info] scalafmt: Formatting 17 Scala sources (/Users/gzoller/me/git/ScalaJack)... -[info] compiling 17 Scala sources and 20 Java sources to /Users/gzoller/me/git/ScalaJack/target/scala-3.3.1/classes ... -Codec: { - val __co_blocke_scalajack_json_run_JJ_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: java.util.ArrayList[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startArray() - scala.Predef.refArrayOps[java.lang.Object](in.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => out.value(elem.asInstanceOf[scala.Int]))) - out.endArray() - } - def w0(`in₂`: co.blocke.scalajack.json.run.JJ, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `out₂`.label("a") - w1(`in₂`.a, `out₂`) - `out₂`.endObject() - } - def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.run.JJ = { - var _a: java.util.ArrayList[scala.Int] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_run_JJ_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.run.JJ] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₃`.expectNull()) null else new java.util.ArrayList[scala.Int][scala.Int]({ - val parsedArray: scala.collection.mutable.ListBuffer[scala.Int] = `in₃`.expectArray[scala.Int]((() => `in₃`.expectInt())) - scala.jdk.CollectionConverters.SeqHasAsJava[scala.Int](parsedArray.toList).asJava.asInstanceOf[java.util.Collection[scala.Int]] - }) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) - case _ => - `in₃`.skipValue() - } - maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_run_JJ_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.run.JJ(_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.run.JJ] { - def encodeValue(`in₄`: co.blocke.scalajack.json.run.JJ, `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) - def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.run.JJ = r0(`in₅`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.run.JJ]) -} -[info] done compiling -[info] compiling 17 Scala sources and 2 Java sources to /Users/gzoller/me/git/ScalaJack/target/scala-3.3.1/test-classes ... -Codec: { - val __co_blocke_scalajack_json_collections_JSetHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: java.util.Set[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startArray() - scala.Predef.refArrayOps[java.lang.Object](in.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => out.value(elem.asInstanceOf[scala.Int]))) - out.endArray() - } - def w0(`in₂`: co.blocke.scalajack.json.collections.JSetHolder[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `out₂`.label("a") - w1(`in₂`.a, `out₂`) - `out₂`.endObject() - } - def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.JSetHolder[scala.Int] = { - var _a: java.util.Set[scala.Int] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_JSetHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.JSetHolder[scala.Int]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = { - val parsedArray: scala.collection.mutable.ListBuffer[scala.Int] = `in₃`.expectArray[scala.Int]((() => `in₃`.expectInt())) - if (parsedArray.==(null)) null else new java.util.TreeSet[scala.Int](scala.jdk.CollectionConverters.SeqHasAsJava[scala.Int](parsedArray.toList).asJava) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) - case _ => - `in₃`.skipValue() - } - maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_JSetHolder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.JSetHolder[scala.Int][scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.JSetHolder[scala.Int]] { - def encodeValue(`in₄`: co.blocke.scalajack.json.collections.JSetHolder[scala.Int], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) - def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.JSetHolder[scala.Int] = r0(`in₅`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.JSetHolder[scala.Int]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_JSetHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: java.util.Set[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startArray() - scala.Predef.refArrayOps[java.lang.Object](in.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => out.value(elem.asInstanceOf[scala.Int]))) - out.endArray() - } - def w0(`in₂`: co.blocke.scalajack.json.collections.JSetHolder[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `out₂`.label("a") - w1(`in₂`.a, `out₂`) - `out₂`.endObject() - } - def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.JSetHolder[scala.Int] = { - var _a: java.util.Set[scala.Int] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_JSetHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.JSetHolder[scala.Int]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = { - val parsedArray: scala.collection.mutable.ListBuffer[scala.Int] = `in₃`.expectArray[scala.Int]((() => `in₃`.expectInt())) - if (parsedArray.==(null)) null else new java.util.TreeSet[scala.Int](scala.jdk.CollectionConverters.SeqHasAsJava[scala.Int](parsedArray.toList).asJava) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) - case _ => - `in₃`.skipValue() - } - maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_JSetHolder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.JSetHolder[scala.Int][scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.JSetHolder[scala.Int]] { - def encodeValue(`in₄`: co.blocke.scalajack.json.collections.JSetHolder[scala.Int], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) - def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.JSetHolder[scala.Int] = r0(`in₅`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.JSetHolder[scala.Int]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_JSetHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: java.util.Set[scala.Predef.String], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startArray() - scala.Predef.refArrayOps[java.lang.Object](in.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => out.valueEscaped(elem.asInstanceOf[java.lang.String]))) - out.endArray() - } - def w0(`in₂`: co.blocke.scalajack.json.collections.JSetHolder[scala.Predef.String], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `out₂`.label("a") - w1(`in₂`.a, `out₂`) - `out₂`.endObject() - } - def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.JSetHolder[scala.Predef.String] = { - var _a: java.util.Set[scala.Predef.String] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_JSetHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.JSetHolder[scala.Predef.String]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = { - val parsedArray: scala.collection.mutable.ListBuffer[java.lang.String] = `in₃`.expectArray[java.lang.String]((() => `in₃`.expectString())) - if (parsedArray.==(null)) null else new java.util.TreeSet[java.lang.String](scala.jdk.CollectionConverters.SeqHasAsJava[java.lang.String](parsedArray.toList).asJava) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) - case _ => - `in₃`.skipValue() - } - maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_JSetHolder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.JSetHolder[scala.Predef.String][java.lang.String](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.JSetHolder[scala.Predef.String]] { - def encodeValue(`in₄`: co.blocke.scalajack.json.collections.JSetHolder[scala.Predef.String], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) - def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.JSetHolder[scala.Predef.String] = r0(`in₅`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.JSetHolder[scala.Predef.String]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_JSetHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: java.util.Set[scala.Boolean], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startArray() - scala.Predef.refArrayOps[java.lang.Object](in.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => out.value(elem.asInstanceOf[scala.Boolean]))) - out.endArray() - } - def w0(`in₂`: co.blocke.scalajack.json.collections.JSetHolder[scala.Boolean], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `out₂`.label("a") - w1(`in₂`.a, `out₂`) - `out₂`.endObject() - } - def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.JSetHolder[scala.Boolean] = { - var _a: java.util.Set[scala.Boolean] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_JSetHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.JSetHolder[scala.Boolean]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = { - val parsedArray: scala.collection.mutable.ListBuffer[scala.Boolean] = `in₃`.expectArray[scala.Boolean]((() => `in₃`.expectBoolean())) - if (parsedArray.==(null)) null else new java.util.TreeSet[scala.Boolean](scala.jdk.CollectionConverters.SeqHasAsJava[scala.Boolean](parsedArray.toList).asJava) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) - case _ => - `in₃`.skipValue() - } - maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_JSetHolder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.JSetHolder[scala.Boolean][scala.Boolean](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.JSetHolder[scala.Boolean]] { - def encodeValue(`in₄`: co.blocke.scalajack.json.collections.JSetHolder[scala.Boolean], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) - def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.JSetHolder[scala.Boolean] = r0(`in₅`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.JSetHolder[scala.Boolean]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_JSetHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w2(in: java.util.Set[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startArray() - scala.Predef.refArrayOps[java.lang.Object](in.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => out.value(elem.asInstanceOf[scala.Int]))) - out.endArray() - } - def w1(`in₂`: java.util.Set[java.util.Set[scala.Int]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startArray() - scala.Predef.refArrayOps[java.lang.Object](`in₂`.toArray()).foreach[scala.Unit](((`elem₂`: java.lang.Object) => w2(`elem₂`.asInstanceOf[java.util.Set[scala.Int]], `out₂`))) - `out₂`.endArray() - } - def w0(`in₃`: co.blocke.scalajack.json.collections.JSetHolder[java.util.Set[scala.Int]], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { - `out₃`.startObject() - `out₃`.label("a") - w1(`in₃`.a, `out₃`) - `out₃`.endObject() - } - def r0(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.JSetHolder[java.util.Set[scala.Int]] = { - var _a: java.util.Set[java.util.Set[scala.Int]] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₄`.expectFirstObjectField(__co_blocke_scalajack_json_collections_JSetHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.JSetHolder[java.util.Set[scala.Int]]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = { - val parsedArray: scala.collection.mutable.ListBuffer[java.util.Set[scala.Int]] = `in₄`.expectArray[java.util.Set[scala.Int]]((() => { - val `parsedArray₂`: scala.collection.mutable.ListBuffer[scala.Int] = `in₄`.expectArray[scala.Int]((() => `in₄`.expectInt())) - if (`parsedArray₂`.==(null)) null else new java.util.TreeSet[scala.Int](scala.jdk.CollectionConverters.SeqHasAsJava[scala.Int](`parsedArray₂`.toList).asJava) - })) - if (parsedArray.==(null)) null else new java.util.TreeSet[java.util.Set[scala.Int]](scala.jdk.CollectionConverters.SeqHasAsJava[java.util.Set[scala.Int]](parsedArray.toList).asJava) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₄`) - case _ => - `in₄`.skipValue() - } - maybeFieldNum = `in₄`.expectObjectField(__co_blocke_scalajack_json_collections_JSetHolder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.JSetHolder[java.util.Set[scala.Int]][java.util.Set[scala.Int]](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₄`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.JSetHolder[java.util.Set[scala.Int]]] { - def encodeValue(`in₅`: co.blocke.scalajack.json.collections.JSetHolder[java.util.Set[scala.Int]], `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₅`, `out₄`) - def decodeValue(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.JSetHolder[java.util.Set[scala.Int]] = r0(`in₆`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.JSetHolder[java.util.Set[scala.Int]]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_JSetHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: java.util.Set[scala.util.Either[scala.Int, scala.Boolean]], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startArray() - scala.Predef.refArrayOps[java.lang.Object](in.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => if (elem.asInstanceOf[scala.util.Either[scala.Int, scala.Boolean]].==(null)) out.burpNull() else elem.asInstanceOf[scala.util.Either[scala.Int, scala.Boolean]] match { - case scala.Left(v) => - out.value(v.asInstanceOf[scala.Int]) - case scala.Right(v) => - out.value(`v₂`.asInstanceOf[scala.Boolean]) - })) - out.endArray() - } - def w0(`in₂`: co.blocke.scalajack.json.collections.JSetHolder[scala.util.Either[scala.Int, scala.Boolean]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `out₂`.label("a") - w1(`in₂`.a, `out₂`) - `out₂`.endObject() - } - def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.JSetHolder[scala.util.Either[scala.Int, scala.Boolean]] = { - var _a: java.util.Set[scala.util.Either[scala.Int, scala.Boolean]] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_JSetHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.JSetHolder[scala.util.Either[scala.Int, scala.Boolean]]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = { - val parsedArray: scala.collection.mutable.ListBuffer[scala.util.Either[scala.Int, scala.Boolean]] = `in₃`.expectArray[scala.util.Either[scala.Int, scala.Boolean]]((() => { - val mark: scala.Int = `in₃`.pos - if (`in₃`.expectNull()) null else scala.util.Try.apply[scala.Boolean](`in₃`.expectBoolean()) match { - case scala.util.Success(rval) => - scala.Right.apply[scala.Nothing, scala.Boolean](rval) - case scala.util.Failure(f) => - `in₃`.revertToPos(mark) - scala.util.Try.apply[scala.Int](`in₃`.expectInt()) match { - case scala.util.Success(lval) => - scala.Left.apply[scala.Int, scala.Nothing](lval) - case scala.util.Failure(_) => - `in₃`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₃`) - } - } - })) - if (parsedArray.==(null)) null else new java.util.TreeSet[scala.util.Either[scala.Int, scala.Boolean]](scala.jdk.CollectionConverters.SeqHasAsJava[scala.util.Either[scala.Int, scala.Boolean]](parsedArray.toList).asJava) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) - case _ => - `in₃`.skipValue() - } - maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_JSetHolder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.JSetHolder[scala.util.Either[scala.Int, scala.Boolean]][scala.util.Either[scala.Int, scala.Boolean]](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.JSetHolder[scala.util.Either[scala.Int, scala.Boolean]]] { - def encodeValue(`in₄`: co.blocke.scalajack.json.collections.JSetHolder[scala.util.Either[scala.Int, scala.Boolean]], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) - def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.JSetHolder[scala.util.Either[scala.Int, scala.Boolean]] = r0(`in₅`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.JSetHolder[scala.util.Either[scala.Int, scala.Boolean]]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_JSetHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: java.util.Set[scala.Int | scala.Boolean], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startArray() - scala.Predef.refArrayOps[java.lang.Object](in.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => { - out.mark() - scala.util.Try.apply[scala.Unit](out.value(elem.asInstanceOf[scala.Int | scala.Boolean].asInstanceOf[scala.Boolean])) match { - case scala.util.Success(_) => - () - case scala.util.Failure(_) => - out.revert() - out.value(elem.asInstanceOf[scala.Int | scala.Boolean].asInstanceOf[scala.Int]) - } - })) - out.endArray() - } - def w0(`in₂`: co.blocke.scalajack.json.collections.JSetHolder[scala.Int | scala.Boolean], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `out₂`.label("a") - w1(`in₂`.a, `out₂`) - `out₂`.endObject() - } - def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.JSetHolder[scala.Int | scala.Boolean] = { - var _a: java.util.Set[scala.Int | scala.Boolean] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_JSetHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.JSetHolder[scala.Int | scala.Boolean]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = { - val parsedArray: scala.collection.mutable.ListBuffer[scala.Int | scala.Boolean] = `in₃`.expectArray[scala.Int | scala.Boolean]((() => { - val mark: scala.Int = `in₃`.pos - scala.util.Try.apply[scala.Int](`in₃`.expectInt()) match { - case scala.util.Success(lval) => - (lval: scala.Int) - case scala.util.Failure(f) => - `in₃`.revertToPos(mark) - scala.util.Try.apply[scala.Boolean](`in₃`.expectBoolean()) match { - case scala.util.Success(rval) => - (rval: scala.Boolean) - case scala.util.Failure(_) => - `in₃`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₃`) - } - } - })) - if (parsedArray.==(null)) null else new java.util.TreeSet[scala.Int | scala.Boolean](scala.jdk.CollectionConverters.SeqHasAsJava[scala.Int | scala.Boolean](parsedArray.toList).asJava) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) - case _ => - `in₃`.skipValue() - } - maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_JSetHolder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.JSetHolder[scala.Int | scala.Boolean][scala.Int | scala.Boolean](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.JSetHolder[scala.Int | scala.Boolean]] { - def encodeValue(`in₄`: co.blocke.scalajack.json.collections.JSetHolder[scala.Int | scala.Boolean], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) - def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.JSetHolder[scala.Int | scala.Boolean] = r0(`in₅`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.JSetHolder[scala.Int | scala.Boolean]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_JSetHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: java.util.Set[scala.Option[scala.Int]], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startArray() - scala.Predef.refArrayOps[java.lang.Object](in.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => elem.asInstanceOf[scala.Option[scala.Int]] match { - case null => - out.burpNull() - case scala.None => - () - case scala.Some(v) => - val vv: scala.Int = v.asInstanceOf[scala.Int] - out.value(vv) - })) - out.endArray() - } - def w0(`in₂`: co.blocke.scalajack.json.collections.JSetHolder[scala.Option[scala.Int]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `out₂`.label("a") - w1(`in₂`.a, `out₂`) - `out₂`.endObject() - } - def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.JSetHolder[scala.Option[scala.Int]] = { - var _a: java.util.Set[scala.Option[scala.Int]] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_JSetHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.JSetHolder[scala.Option[scala.Int]]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = { - val parsedArray: scala.collection.mutable.ListBuffer[scala.Option[scala.Int]] = `in₃`.expectArray[scala.Option[scala.Int]]((() => if (`in₃`.expectNull()) null else scala.Some.apply[scala.Int](`in₃`.expectInt()))) - if (parsedArray.==(null)) null else new java.util.TreeSet[scala.Option[scala.Int]](scala.jdk.CollectionConverters.SeqHasAsJava[scala.Option[scala.Int]](parsedArray.toList).asJava) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) - case _ => - `in₃`.skipValue() - } - maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_JSetHolder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.JSetHolder[scala.Option[scala.Int]][scala.Option[scala.Int]](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.JSetHolder[scala.Option[scala.Int]]] { - def encodeValue(`in₄`: co.blocke.scalajack.json.collections.JSetHolder[scala.Option[scala.Int]], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) - def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.JSetHolder[scala.Option[scala.Int]] = r0(`in₅`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.JSetHolder[scala.Option[scala.Int]]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_JSetHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w2(in: scala.collection.immutable.Map[scala.Predef.String, scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - in.foreach[scala.Unit](((x$1: scala.Tuple2[java.lang.String, scala.Int]) => x$1 match { - case scala.Tuple2(key, value) => - { - out.maybeComma() - out.valueEscaped(key) - out.colon() - out.value(value) - } - })) - out.endObject() - } - def w1(`in₂`: java.util.Set[scala.collection.immutable.Map[scala.Predef.String, scala.Int]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startArray() - scala.Predef.refArrayOps[java.lang.Object](`in₂`.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => w2(elem.asInstanceOf[scala.collection.immutable.Map[scala.Predef.String, scala.Int]], `out₂`))) - `out₂`.endArray() - } - def w0(`in₃`: co.blocke.scalajack.json.collections.JSetHolder[scala.collection.immutable.Map[scala.Predef.String, scala.Int]], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { - `out₃`.startObject() - `out₃`.label("a") - w1(`in₃`.a, `out₃`) - `out₃`.endObject() - } - def r0(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.JSetHolder[scala.collection.immutable.Map[scala.Predef.String, scala.Int]] = { - var _a: java.util.Set[scala.collection.immutable.Map[scala.Predef.String, scala.Int]] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₄`.expectFirstObjectField(__co_blocke_scalajack_json_collections_JSetHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.JSetHolder[scala.collection.immutable.Map[scala.Predef.String, scala.Int]]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = { - val parsedArray: scala.collection.mutable.ListBuffer[scala.collection.immutable.Map[scala.Predef.String, scala.Int]] = `in₄`.expectArray[scala.collection.immutable.Map[scala.Predef.String, scala.Int]]((() => if (`in₄`.expectNull()) null else { - `in₄`.expectToken('{') - `in₄`.parseMap[java.lang.String, scala.Int]((() => `in₄`.expectString()), (() => `in₄`.expectInt()), scala.Predef.Map.empty[java.lang.String, scala.Int], true) - })) - if (parsedArray.==(null)) null else new java.util.TreeSet[scala.collection.immutable.Map[scala.Predef.String, scala.Int]](scala.jdk.CollectionConverters.SeqHasAsJava[scala.collection.immutable.Map[scala.Predef.String, scala.Int]](parsedArray.toList).asJava) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₄`) - case _ => - `in₄`.skipValue() - } - maybeFieldNum = `in₄`.expectObjectField(__co_blocke_scalajack_json_collections_JSetHolder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.JSetHolder[scala.collection.immutable.Map[scala.Predef.String, scala.Int]][scala.collection.immutable.Map[scala.Predef.String, scala.Int]](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₄`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.JSetHolder[scala.collection.immutable.Map[scala.Predef.String, scala.Int]]] { - def encodeValue(`in₅`: co.blocke.scalajack.json.collections.JSetHolder[scala.collection.immutable.Map[scala.Predef.String, scala.Int]], `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₅`, `out₄`) - def decodeValue(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.JSetHolder[scala.collection.immutable.Map[scala.Predef.String, scala.Int]] = r0(`in₆`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.JSetHolder[scala.collection.immutable.Map[scala.Predef.String, scala.Int]]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_Person_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("name", "age")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - val __co_blocke_scalajack_json_collections_JSetHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w2(in: co.blocke.scalajack.json.collections.Person, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("name") - out.valueEscaped(in.name) - out.label("age") - out.value(in.age) - out.endObject() - } - def w1(`in₂`: java.util.Set[co.blocke.scalajack.json.collections.Person], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startArray() - scala.Predef.refArrayOps[java.lang.Object](`in₂`.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => w2(elem.asInstanceOf[co.blocke.scalajack.json.collections.Person], `out₂`))) - `out₂`.endArray() - } - def w0(`in₃`: co.blocke.scalajack.json.collections.JSetHolder[co.blocke.scalajack.json.collections.Person], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { - `out₃`.startObject() - `out₃`.label("a") - w1(`in₃`.a, `out₃`) - `out₃`.endObject() - } - def r1(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.Person = { - var _name: java.lang.String = "" - var _age: scala.Int = 0 - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₄`.expectFirstObjectField(__co_blocke_scalajack_json_collections_Person_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.Person] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _name = `in₄`.expectString() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("name"), `in₄`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _age = `in₄`.expectInt() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("age"), `in₄`) - case _ => - `in₄`.skipValue() - } - maybeFieldNum = `in₄`.expectObjectField(__co_blocke_scalajack_json_collections_Person_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.collections.Person(_name, _age) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("name", "age")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₄`) - } - } - def r0(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.JSetHolder[co.blocke.scalajack.json.collections.Person] = { - var _a: java.util.Set[co.blocke.scalajack.json.collections.Person] = null - var `required₂`: scala.Int = 1 - var `maybeFieldNum₂`: scala.Option[scala.Int] = `in₅`.expectFirstObjectField(__co_blocke_scalajack_json_collections_JSetHolder_fields) - if (`maybeFieldNum₂`.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.JSetHolder[co.blocke.scalajack.json.collections.Person]] else { - while (`maybeFieldNum₂`.isDefined) { - `maybeFieldNum₂`.get match { - case 0 => - if (`required₂`.&(1).!=(0)) { - `required₂` = `required₂`.^(1) - _a = { - val parsedArray: scala.collection.mutable.ListBuffer[co.blocke.scalajack.json.collections.Person] = `in₅`.expectArray[co.blocke.scalajack.json.collections.Person]((() => r1(`in₅`))) - if (parsedArray.==(null)) null else new java.util.TreeSet[co.blocke.scalajack.json.collections.Person](scala.jdk.CollectionConverters.SeqHasAsJava[co.blocke.scalajack.json.collections.Person](parsedArray.toList).asJava) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₅`) - case _ => - `in₅`.skipValue() - } - `maybeFieldNum₂` = `in₅`.expectObjectField(__co_blocke_scalajack_json_collections_JSetHolder_fields) - } - if (`required₂`.&(1).==(0)) new co.blocke.scalajack.json.collections.JSetHolder[co.blocke.scalajack.json.collections.Person][co.blocke.scalajack.json.collections.Person](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(`required₂`.&(1)))), `in₅`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.JSetHolder[co.blocke.scalajack.json.collections.Person]] { - def encodeValue(`in₆`: co.blocke.scalajack.json.collections.JSetHolder[co.blocke.scalajack.json.collections.Person], `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₆`, `out₄`) - def decodeValue(`in₇`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.JSetHolder[co.blocke.scalajack.json.collections.Person] = r0(`in₇`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.JSetHolder[co.blocke.scalajack.json.collections.Person]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_ArrayListHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: java.util.ArrayList[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startArray() - scala.Predef.refArrayOps[java.lang.Object](in.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => out.value(elem.asInstanceOf[scala.Int]))) - out.endArray() - } - def w0(`in₂`: co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `out₂`.label("a") - w1(`in₂`.a, `out₂`) - `out₂`.endObject() - } - def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int] = { - var _a: java.util.ArrayList[scala.Int] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_ArrayListHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₃`.expectNull()) null else new java.util.ArrayList[scala.Int][scala.Int]({ - val parsedArray: scala.collection.mutable.ListBuffer[scala.Int] = `in₃`.expectArray[scala.Int]((() => `in₃`.expectInt())) - scala.jdk.CollectionConverters.SeqHasAsJava[scala.Int](parsedArray.toList).asJava.asInstanceOf[java.util.Collection[scala.Int]] - }) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) - case _ => - `in₃`.skipValue() - } - maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_ArrayListHolder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int][scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int]] { - def encodeValue(`in₄`: co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) - def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int] = r0(`in₅`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_ArrayListHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: java.util.ArrayList[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startArray() - scala.Predef.refArrayOps[java.lang.Object](in.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => out.value(elem.asInstanceOf[scala.Int]))) - out.endArray() - } - def w0(`in₂`: co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `out₂`.label("a") - w1(`in₂`.a, `out₂`) - `out₂`.endObject() - } - def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int] = { - var _a: java.util.ArrayList[scala.Int] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_ArrayListHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₃`.expectNull()) null else new java.util.ArrayList[scala.Int][scala.Int]({ - val parsedArray: scala.collection.mutable.ListBuffer[scala.Int] = `in₃`.expectArray[scala.Int]((() => `in₃`.expectInt())) - scala.jdk.CollectionConverters.SeqHasAsJava[scala.Int](parsedArray.toList).asJava.asInstanceOf[java.util.Collection[scala.Int]] - }) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) - case _ => - `in₃`.skipValue() - } - maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_ArrayListHolder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int][scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int]] { - def encodeValue(`in₄`: co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) - def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int] = r0(`in₅`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_ArrayListHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: java.util.ArrayList[scala.Predef.String], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startArray() - scala.Predef.refArrayOps[java.lang.Object](in.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => out.valueEscaped(elem.asInstanceOf[java.lang.String]))) - out.endArray() - } - def w0(`in₂`: co.blocke.scalajack.json.collections.ArrayListHolder[scala.Predef.String], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `out₂`.label("a") - w1(`in₂`.a, `out₂`) - `out₂`.endObject() - } - def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.ArrayListHolder[scala.Predef.String] = { - var _a: java.util.ArrayList[scala.Predef.String] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_ArrayListHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.ArrayListHolder[scala.Predef.String]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₃`.expectNull()) null else new java.util.ArrayList[scala.Predef.String][java.lang.String]({ - val parsedArray: scala.collection.mutable.ListBuffer[java.lang.String] = `in₃`.expectArray[java.lang.String]((() => `in₃`.expectString())) - scala.jdk.CollectionConverters.SeqHasAsJava[java.lang.String](parsedArray.toList).asJava.asInstanceOf[java.util.Collection[java.lang.String]] - }) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) - case _ => - `in₃`.skipValue() - } - maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_ArrayListHolder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.ArrayListHolder[scala.Predef.String][java.lang.String](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.ArrayListHolder[scala.Predef.String]] { - def encodeValue(`in₄`: co.blocke.scalajack.json.collections.ArrayListHolder[scala.Predef.String], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) - def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.ArrayListHolder[scala.Predef.String] = r0(`in₅`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.ArrayListHolder[scala.Predef.String]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_ArrayListHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: java.util.ArrayList[scala.Boolean], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startArray() - scala.Predef.refArrayOps[java.lang.Object](in.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => out.value(elem.asInstanceOf[scala.Boolean]))) - out.endArray() - } - def w0(`in₂`: co.blocke.scalajack.json.collections.ArrayListHolder[scala.Boolean], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `out₂`.label("a") - w1(`in₂`.a, `out₂`) - `out₂`.endObject() - } - def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.ArrayListHolder[scala.Boolean] = { - var _a: java.util.ArrayList[scala.Boolean] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_ArrayListHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.ArrayListHolder[scala.Boolean]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₃`.expectNull()) null else new java.util.ArrayList[scala.Boolean][scala.Boolean]({ - val parsedArray: scala.collection.mutable.ListBuffer[scala.Boolean] = `in₃`.expectArray[scala.Boolean]((() => `in₃`.expectBoolean())) - scala.jdk.CollectionConverters.SeqHasAsJava[scala.Boolean](parsedArray.toList).asJava.asInstanceOf[java.util.Collection[scala.Boolean]] - }) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) - case _ => - `in₃`.skipValue() - } - maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_ArrayListHolder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.ArrayListHolder[scala.Boolean][scala.Boolean](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.ArrayListHolder[scala.Boolean]] { - def encodeValue(`in₄`: co.blocke.scalajack.json.collections.ArrayListHolder[scala.Boolean], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) - def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.ArrayListHolder[scala.Boolean] = r0(`in₅`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.ArrayListHolder[scala.Boolean]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_ArrayListHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w2(in: java.util.ArrayList[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startArray() - scala.Predef.refArrayOps[java.lang.Object](in.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => out.value(elem.asInstanceOf[scala.Int]))) - out.endArray() - } - def w1(`in₂`: java.util.ArrayList[java.util.ArrayList[scala.Int]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startArray() - scala.Predef.refArrayOps[java.lang.Object](`in₂`.toArray()).foreach[scala.Unit](((`elem₂`: java.lang.Object) => w2(`elem₂`.asInstanceOf[java.util.ArrayList[scala.Int]], `out₂`))) - `out₂`.endArray() - } - def w0(`in₃`: co.blocke.scalajack.json.collections.ArrayListHolder[java.util.ArrayList[scala.Int]], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { - `out₃`.startObject() - `out₃`.label("a") - w1(`in₃`.a, `out₃`) - `out₃`.endObject() - } - def r0(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.ArrayListHolder[java.util.ArrayList[scala.Int]] = { - var _a: java.util.ArrayList[java.util.ArrayList[scala.Int]] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₄`.expectFirstObjectField(__co_blocke_scalajack_json_collections_ArrayListHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.ArrayListHolder[java.util.ArrayList[scala.Int]]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₄`.expectNull()) null else new java.util.ArrayList[java.util.ArrayList[scala.Int]][java.util.ArrayList[scala.Int]]({ - val parsedArray: scala.collection.mutable.ListBuffer[java.util.ArrayList[scala.Int]] = `in₄`.expectArray[java.util.ArrayList[scala.Int]]((() => if (`in₄`.expectNull()) null else new java.util.ArrayList[scala.Int][scala.Int]({ - val `parsedArray₂`: scala.collection.mutable.ListBuffer[scala.Int] = `in₄`.expectArray[scala.Int]((() => `in₄`.expectInt())) - scala.jdk.CollectionConverters.SeqHasAsJava[scala.Int](`parsedArray₂`.toList).asJava.asInstanceOf[java.util.Collection[scala.Int]] - }))) - scala.jdk.CollectionConverters.SeqHasAsJava[java.util.ArrayList[scala.Int]](parsedArray.toList).asJava.asInstanceOf[java.util.Collection[java.util.ArrayList[scala.Int]]] - }) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₄`) - case _ => - `in₄`.skipValue() - } - maybeFieldNum = `in₄`.expectObjectField(__co_blocke_scalajack_json_collections_ArrayListHolder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.ArrayListHolder[java.util.ArrayList[scala.Int]][java.util.ArrayList[scala.Int]](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₄`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.ArrayListHolder[java.util.ArrayList[scala.Int]]] { - def encodeValue(`in₅`: co.blocke.scalajack.json.collections.ArrayListHolder[java.util.ArrayList[scala.Int]], `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₅`, `out₄`) - def decodeValue(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.ArrayListHolder[java.util.ArrayList[scala.Int]] = r0(`in₆`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.ArrayListHolder[java.util.ArrayList[scala.Int]]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_ArrayListHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: java.util.ArrayList[scala.util.Either[scala.Int, scala.Boolean]], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startArray() - scala.Predef.refArrayOps[java.lang.Object](in.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => if (elem.asInstanceOf[scala.util.Either[scala.Int, scala.Boolean]].==(null)) out.burpNull() else elem.asInstanceOf[scala.util.Either[scala.Int, scala.Boolean]] match { - case scala.Left(v) => - out.value(v.asInstanceOf[scala.Int]) - case scala.Right(v) => - out.value(`v₂`.asInstanceOf[scala.Boolean]) - })) - out.endArray() - } - def w0(`in₂`: co.blocke.scalajack.json.collections.ArrayListHolder[scala.util.Either[scala.Int, scala.Boolean]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `out₂`.label("a") - w1(`in₂`.a, `out₂`) - `out₂`.endObject() - } - def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.ArrayListHolder[scala.util.Either[scala.Int, scala.Boolean]] = { - var _a: java.util.ArrayList[scala.util.Either[scala.Int, scala.Boolean]] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_ArrayListHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.ArrayListHolder[scala.util.Either[scala.Int, scala.Boolean]]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₃`.expectNull()) null else new java.util.ArrayList[scala.util.Either[scala.Int, scala.Boolean]][scala.util.Either[scala.Int, scala.Boolean]]({ - val parsedArray: scala.collection.mutable.ListBuffer[scala.util.Either[scala.Int, scala.Boolean]] = `in₃`.expectArray[scala.util.Either[scala.Int, scala.Boolean]]((() => { - val mark: scala.Int = `in₃`.pos - if (`in₃`.expectNull()) null else scala.util.Try.apply[scala.Boolean](`in₃`.expectBoolean()) match { - case scala.util.Success(rval) => - scala.Right.apply[scala.Nothing, scala.Boolean](rval) - case scala.util.Failure(f) => - `in₃`.revertToPos(mark) - scala.util.Try.apply[scala.Int](`in₃`.expectInt()) match { - case scala.util.Success(lval) => - scala.Left.apply[scala.Int, scala.Nothing](lval) - case scala.util.Failure(_) => - `in₃`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₃`) - } - } - })) - scala.jdk.CollectionConverters.SeqHasAsJava[scala.util.Either[scala.Int, scala.Boolean]](parsedArray.toList).asJava.asInstanceOf[java.util.Collection[scala.util.Either[scala.Int, scala.Boolean]]] - }) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) - case _ => - `in₃`.skipValue() - } - maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_ArrayListHolder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.ArrayListHolder[scala.util.Either[scala.Int, scala.Boolean]][scala.util.Either[scala.Int, scala.Boolean]](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.ArrayListHolder[scala.util.Either[scala.Int, scala.Boolean]]] { - def encodeValue(`in₄`: co.blocke.scalajack.json.collections.ArrayListHolder[scala.util.Either[scala.Int, scala.Boolean]], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) - def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.ArrayListHolder[scala.util.Either[scala.Int, scala.Boolean]] = r0(`in₅`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.ArrayListHolder[scala.util.Either[scala.Int, scala.Boolean]]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_ArrayListHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: java.util.ArrayList[scala.Int | scala.Boolean], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startArray() - scala.Predef.refArrayOps[java.lang.Object](in.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => { - out.mark() - scala.util.Try.apply[scala.Unit](out.value(elem.asInstanceOf[scala.Int | scala.Boolean].asInstanceOf[scala.Boolean])) match { - case scala.util.Success(_) => - () - case scala.util.Failure(_) => - out.revert() - out.value(elem.asInstanceOf[scala.Int | scala.Boolean].asInstanceOf[scala.Int]) - } - })) - out.endArray() - } - def w0(`in₂`: co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int | scala.Boolean], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `out₂`.label("a") - w1(`in₂`.a, `out₂`) - `out₂`.endObject() - } - def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int | scala.Boolean] = { - var _a: java.util.ArrayList[scala.Int | scala.Boolean] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_ArrayListHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int | scala.Boolean]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₃`.expectNull()) null else new java.util.ArrayList[scala.Int | scala.Boolean][scala.Int | scala.Boolean]({ - val parsedArray: scala.collection.mutable.ListBuffer[scala.Int | scala.Boolean] = `in₃`.expectArray[scala.Int | scala.Boolean]((() => { - val mark: scala.Int = `in₃`.pos - scala.util.Try.apply[scala.Int](`in₃`.expectInt()) match { - case scala.util.Success(lval) => - (lval: scala.Int) - case scala.util.Failure(f) => - `in₃`.revertToPos(mark) - scala.util.Try.apply[scala.Boolean](`in₃`.expectBoolean()) match { - case scala.util.Success(rval) => - (rval: scala.Boolean) - case scala.util.Failure(_) => - `in₃`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₃`) - } - } - })) - scala.jdk.CollectionConverters.SeqHasAsJava[scala.Int | scala.Boolean](parsedArray.toList).asJava.asInstanceOf[java.util.Collection[scala.Int | scala.Boolean]] - }) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) - case _ => - `in₃`.skipValue() - } - maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_ArrayListHolder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int | scala.Boolean][scala.Int | scala.Boolean](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int | scala.Boolean]] { - def encodeValue(`in₄`: co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int | scala.Boolean], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) - def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int | scala.Boolean] = r0(`in₅`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.ArrayListHolder[scala.Int | scala.Boolean]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_ArrayListHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: java.util.ArrayList[scala.Option[scala.Int]], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startArray() - scala.Predef.refArrayOps[java.lang.Object](in.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => elem.asInstanceOf[scala.Option[scala.Int]] match { - case null => - out.burpNull() - case scala.None => - () - case scala.Some(v) => - val vv: scala.Int = v.asInstanceOf[scala.Int] - out.value(vv) - })) - out.endArray() - } - def w0(`in₂`: co.blocke.scalajack.json.collections.ArrayListHolder[scala.Option[scala.Int]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `out₂`.label("a") - w1(`in₂`.a, `out₂`) - `out₂`.endObject() - } - def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.ArrayListHolder[scala.Option[scala.Int]] = { - var _a: java.util.ArrayList[scala.Option[scala.Int]] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_ArrayListHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.ArrayListHolder[scala.Option[scala.Int]]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₃`.expectNull()) null else new java.util.ArrayList[scala.Option[scala.Int]][scala.Option[scala.Int]]({ - val parsedArray: scala.collection.mutable.ListBuffer[scala.Option[scala.Int]] = `in₃`.expectArray[scala.Option[scala.Int]]((() => if (`in₃`.expectNull()) null else scala.Some.apply[scala.Int](`in₃`.expectInt()))) - scala.jdk.CollectionConverters.SeqHasAsJava[scala.Option[scala.Int]](parsedArray.toList).asJava.asInstanceOf[java.util.Collection[scala.Option[scala.Int]]] - }) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) - case _ => - `in₃`.skipValue() - } - maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_ArrayListHolder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.ArrayListHolder[scala.Option[scala.Int]][scala.Option[scala.Int]](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.ArrayListHolder[scala.Option[scala.Int]]] { - def encodeValue(`in₄`: co.blocke.scalajack.json.collections.ArrayListHolder[scala.Option[scala.Int]], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) - def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.ArrayListHolder[scala.Option[scala.Int]] = r0(`in₅`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.ArrayListHolder[scala.Option[scala.Int]]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_ArrayListHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w2(in: scala.collection.immutable.Map[scala.Predef.String, scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - in.foreach[scala.Unit](((x$1: scala.Tuple2[java.lang.String, scala.Int]) => x$1 match { - case scala.Tuple2(key, value) => - { - out.maybeComma() - out.valueEscaped(key) - out.colon() - out.value(value) - } - })) - out.endObject() - } - def w1(`in₂`: java.util.ArrayList[scala.collection.immutable.Map[scala.Predef.String, scala.Int]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startArray() - scala.Predef.refArrayOps[java.lang.Object](`in₂`.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => w2(elem.asInstanceOf[scala.collection.immutable.Map[scala.Predef.String, scala.Int]], `out₂`))) - `out₂`.endArray() - } - def w0(`in₃`: co.blocke.scalajack.json.collections.ArrayListHolder[scala.collection.immutable.Map[scala.Predef.String, scala.Int]], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { - `out₃`.startObject() - `out₃`.label("a") - w1(`in₃`.a, `out₃`) - `out₃`.endObject() - } - def r0(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.ArrayListHolder[scala.collection.immutable.Map[scala.Predef.String, scala.Int]] = { - var _a: java.util.ArrayList[scala.collection.immutable.Map[scala.Predef.String, scala.Int]] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₄`.expectFirstObjectField(__co_blocke_scalajack_json_collections_ArrayListHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.ArrayListHolder[scala.collection.immutable.Map[scala.Predef.String, scala.Int]]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₄`.expectNull()) null else new java.util.ArrayList[scala.collection.immutable.Map[scala.Predef.String, scala.Int]][scala.collection.immutable.Map[scala.Predef.String, scala.Int]]({ - val parsedArray: scala.collection.mutable.ListBuffer[scala.collection.immutable.Map[scala.Predef.String, scala.Int]] = `in₄`.expectArray[scala.collection.immutable.Map[scala.Predef.String, scala.Int]]((() => if (`in₄`.expectNull()) null else { - `in₄`.expectToken('{') - `in₄`.parseMap[java.lang.String, scala.Int]((() => `in₄`.expectString()), (() => `in₄`.expectInt()), scala.Predef.Map.empty[java.lang.String, scala.Int], true) - })) - scala.jdk.CollectionConverters.SeqHasAsJava[scala.collection.immutable.Map[scala.Predef.String, scala.Int]](parsedArray.toList).asJava.asInstanceOf[java.util.Collection[scala.collection.immutable.Map[scala.Predef.String, scala.Int]]] - }) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₄`) - case _ => - `in₄`.skipValue() - } - maybeFieldNum = `in₄`.expectObjectField(__co_blocke_scalajack_json_collections_ArrayListHolder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.ArrayListHolder[scala.collection.immutable.Map[scala.Predef.String, scala.Int]][scala.collection.immutable.Map[scala.Predef.String, scala.Int]](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₄`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.ArrayListHolder[scala.collection.immutable.Map[scala.Predef.String, scala.Int]]] { - def encodeValue(`in₅`: co.blocke.scalajack.json.collections.ArrayListHolder[scala.collection.immutable.Map[scala.Predef.String, scala.Int]], `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₅`, `out₄`) - def decodeValue(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.ArrayListHolder[scala.collection.immutable.Map[scala.Predef.String, scala.Int]] = r0(`in₆`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.ArrayListHolder[scala.collection.immutable.Map[scala.Predef.String, scala.Int]]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_Person_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("name", "age")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - val __co_blocke_scalajack_json_collections_ArrayListHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w2(in: co.blocke.scalajack.json.collections.Person, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("name") - out.valueEscaped(in.name) - out.label("age") - out.value(in.age) - out.endObject() - } - def w1(`in₂`: java.util.ArrayList[co.blocke.scalajack.json.collections.Person], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startArray() - scala.Predef.refArrayOps[java.lang.Object](`in₂`.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => w2(elem.asInstanceOf[co.blocke.scalajack.json.collections.Person], `out₂`))) - `out₂`.endArray() - } - def w0(`in₃`: co.blocke.scalajack.json.collections.ArrayListHolder[co.blocke.scalajack.json.collections.Person], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { - `out₃`.startObject() - `out₃`.label("a") - w1(`in₃`.a, `out₃`) - `out₃`.endObject() - } - def r1(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.Person = { - var _name: java.lang.String = "" - var _age: scala.Int = 0 - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₄`.expectFirstObjectField(__co_blocke_scalajack_json_collections_Person_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.Person] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _name = `in₄`.expectString() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("name"), `in₄`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _age = `in₄`.expectInt() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("age"), `in₄`) - case _ => - `in₄`.skipValue() - } - maybeFieldNum = `in₄`.expectObjectField(__co_blocke_scalajack_json_collections_Person_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.collections.Person(_name, _age) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("name", "age")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₄`) - } - } - def r0(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.ArrayListHolder[co.blocke.scalajack.json.collections.Person] = { - var _a: java.util.ArrayList[co.blocke.scalajack.json.collections.Person] = null - var `required₂`: scala.Int = 1 - var `maybeFieldNum₂`: scala.Option[scala.Int] = `in₅`.expectFirstObjectField(__co_blocke_scalajack_json_collections_ArrayListHolder_fields) - if (`maybeFieldNum₂`.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.ArrayListHolder[co.blocke.scalajack.json.collections.Person]] else { - while (`maybeFieldNum₂`.isDefined) { - `maybeFieldNum₂`.get match { - case 0 => - if (`required₂`.&(1).!=(0)) { - `required₂` = `required₂`.^(1) - _a = if (`in₅`.expectNull()) null else new java.util.ArrayList[co.blocke.scalajack.json.collections.Person][co.blocke.scalajack.json.collections.Person]({ - val parsedArray: scala.collection.mutable.ListBuffer[co.blocke.scalajack.json.collections.Person] = `in₅`.expectArray[co.blocke.scalajack.json.collections.Person]((() => r1(`in₅`))) - scala.jdk.CollectionConverters.SeqHasAsJava[co.blocke.scalajack.json.collections.Person](parsedArray.toList).asJava.asInstanceOf[java.util.Collection[co.blocke.scalajack.json.collections.Person]] - }) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₅`) - case _ => - `in₅`.skipValue() - } - `maybeFieldNum₂` = `in₅`.expectObjectField(__co_blocke_scalajack_json_collections_ArrayListHolder_fields) - } - if (`required₂`.&(1).==(0)) new co.blocke.scalajack.json.collections.ArrayListHolder[co.blocke.scalajack.json.collections.Person][co.blocke.scalajack.json.collections.Person](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(`required₂`.&(1)))), `in₅`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.ArrayListHolder[co.blocke.scalajack.json.collections.Person]] { - def encodeValue(`in₆`: co.blocke.scalajack.json.collections.ArrayListHolder[co.blocke.scalajack.json.collections.Person], `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₆`, `out₄`) - def decodeValue(`in₇`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.ArrayListHolder[co.blocke.scalajack.json.collections.Person] = r0(`in₇`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.ArrayListHolder[co.blocke.scalajack.json.collections.Person]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_Holder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: java.util.concurrent.ArrayBlockingQueue[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startArray() - scala.Predef.refArrayOps[java.lang.Object](in.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => out.value(elem.asInstanceOf[scala.Int]))) - out.endArray() - } - def w0(`in₂`: co.blocke.scalajack.json.collections.Holder[java.util.concurrent.ArrayBlockingQueue[scala.Int]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `out₂`.label("a") - w1(`in₂`.a, `out₂`) - `out₂`.endObject() - } - def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.Holder[java.util.concurrent.ArrayBlockingQueue[scala.Int]] = { - var _a: java.util.concurrent.ArrayBlockingQueue[scala.Int] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_Holder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.Holder[java.util.concurrent.ArrayBlockingQueue[scala.Int]]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = { - val parsedArray: scala.collection.mutable.ListBuffer[scala.Int] = `in₃`.expectArray[scala.Int]((() => `in₃`.expectInt())) - if (parsedArray.==(null)) null else new java.util.concurrent.ArrayBlockingQueue[scala.Int](parsedArray.length, true, scala.jdk.CollectionConverters.SeqHasAsJava[scala.Int](parsedArray.toList).asJava) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) - case _ => - `in₃`.skipValue() - } - maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_Holder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.Holder[java.util.concurrent.ArrayBlockingQueue[scala.Int]][java.util.concurrent.ArrayBlockingQueue[scala.Int]](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.Holder[java.util.concurrent.ArrayBlockingQueue[scala.Int]]] { - def encodeValue(`in₄`: co.blocke.scalajack.json.collections.Holder[java.util.concurrent.ArrayBlockingQueue[scala.Int]], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) - def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.Holder[java.util.concurrent.ArrayBlockingQueue[scala.Int]] = r0(`in₅`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.Holder[java.util.concurrent.ArrayBlockingQueue[scala.Int]]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_Holder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: java.util.TreeSet[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startArray() - scala.Predef.refArrayOps[java.lang.Object](in.toArray()).foreach[scala.Unit](((elem: java.lang.Object) => out.value(elem.asInstanceOf[scala.Int]))) - out.endArray() - } - def w0(`in₂`: co.blocke.scalajack.json.collections.Holder[java.util.TreeSet[scala.Int]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `out₂`.label("a") - w1(`in₂`.a, `out₂`) - `out₂`.endObject() - } - def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.Holder[java.util.TreeSet[scala.Int]] = { - var _a: java.util.TreeSet[scala.Int] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_Holder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.Holder[java.util.TreeSet[scala.Int]]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = { - val parsedArray: scala.collection.mutable.ListBuffer[scala.Int] = `in₃`.expectArray[scala.Int]((() => `in₃`.expectInt())) - if (parsedArray.==(null)) null else new java.util.TreeSet[scala.Int](scala.jdk.CollectionConverters.SeqHasAsJava[scala.Int](parsedArray.toList).asJava) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) - case _ => - `in₃`.skipValue() - } - maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_Holder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.Holder[java.util.TreeSet[scala.Int]][java.util.TreeSet[scala.Int]](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.Holder[java.util.TreeSet[scala.Int]]] { - def encodeValue(`in₄`: co.blocke.scalajack.json.collections.Holder[java.util.TreeSet[scala.Int]], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) - def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.Holder[java.util.TreeSet[scala.Int]] = r0(`in₅`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.Holder[java.util.TreeSet[scala.Int]]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_MapHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: scala.collection.immutable.Map[scala.Int, scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - in.foreach[scala.Unit](((x$1: scala.Tuple2[scala.Int, scala.Int]) => x$1 match { - case scala.Tuple2(key, value) => - { - out.maybeComma() - out.valueStringified(scala.Int.int2long(key)) - out.colon() - out.value(value) - } - })) - out.endObject() - } - def w0(`in₂`: co.blocke.scalajack.json.collections.MapHolder[scala.Int, scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `out₂`.label("a") - w1(`in₂`.a, `out₂`) - `out₂`.endObject() - } - def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Int, scala.Int] = { - var _a: scala.collection.immutable.Map[scala.Int, scala.Int] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.MapHolder[scala.Int, scala.Int]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₃`.expectNull()) null else { - `in₃`.expectToken('{') - `in₃`.parseMap[scala.Int, scala.Int]((() => scala.Predef.augmentString(`in₃`.expectString()).toInt), (() => `in₃`.expectInt()), scala.Predef.Map.empty[scala.Int, scala.Int], true) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) - case _ => - `in₃`.skipValue() - } - maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.MapHolder[scala.Int, scala.Int][scala.Int, scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Int, scala.Int]] { - def encodeValue(`in₄`: co.blocke.scalajack.json.collections.MapHolder[scala.Int, scala.Int], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) - def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Int, scala.Int] = r0(`in₅`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Int, scala.Int]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_MapHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: scala.collection.immutable.Map[scala.Predef.String, scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - in.foreach[scala.Unit](((x$1: scala.Tuple2[java.lang.String, scala.Int]) => x$1 match { - case scala.Tuple2(key, value) => - { - out.maybeComma() - out.valueEscaped(key) - out.colon() - out.value(value) - } - })) - out.endObject() - } - def w0(`in₂`: co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `out₂`.label("a") - w1(`in₂`.a, `out₂`) - `out₂`.endObject() - } - def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Int] = { - var _a: scala.collection.immutable.Map[scala.Predef.String, scala.Int] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Int]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₃`.expectNull()) null else { - `in₃`.expectToken('{') - `in₃`.parseMap[java.lang.String, scala.Int]((() => `in₃`.expectString()), (() => `in₃`.expectInt()), scala.Predef.Map.empty[java.lang.String, scala.Int], true) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) - case _ => - `in₃`.skipValue() - } - maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Int][java.lang.String, scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Int]] { - def encodeValue(`in₄`: co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Int], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) - def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Int] = r0(`in₅`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Int]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_MapHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: scala.collection.immutable.Map[scala.Long, scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - in.foreach[scala.Unit](((x$1: scala.Tuple2[scala.Long, scala.Int]) => x$1 match { - case scala.Tuple2(key, value) => - { - out.maybeComma() - out.valueStringified(key) - out.colon() - out.value(value) - } - })) - out.endObject() - } - def w0(`in₂`: co.blocke.scalajack.json.collections.MapHolder[scala.Long, scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `out₂`.label("a") - w1(`in₂`.a, `out₂`) - `out₂`.endObject() - } - def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Long, scala.Int] = { - var _a: scala.collection.immutable.Map[scala.Long, scala.Int] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.MapHolder[scala.Long, scala.Int]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₃`.expectNull()) null else { - `in₃`.expectToken('{') - `in₃`.parseMap[scala.Long, scala.Int]((() => scala.Predef.augmentString(`in₃`.expectString()).toLong), (() => `in₃`.expectInt()), scala.Predef.Map.empty[scala.Long, scala.Int], true) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) - case _ => - `in₃`.skipValue() - } - maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.MapHolder[scala.Long, scala.Int][scala.Long, scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Long, scala.Int]] { - def encodeValue(`in₄`: co.blocke.scalajack.json.collections.MapHolder[scala.Long, scala.Int], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) - def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Long, scala.Int] = r0(`in₅`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Long, scala.Int]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_MapHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: scala.collection.immutable.Map[scala.Boolean, scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - in.foreach[scala.Unit](((x$1: scala.Tuple2[scala.Boolean, scala.Int]) => x$1 match { - case scala.Tuple2(key, value) => - { - out.maybeComma() - out.valueStringified(key) - out.colon() - out.value(value) - } - })) - out.endObject() - } - def w0(`in₂`: co.blocke.scalajack.json.collections.MapHolder[scala.Boolean, scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `out₂`.label("a") - w1(`in₂`.a, `out₂`) - `out₂`.endObject() - } - def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Boolean, scala.Int] = { - var _a: scala.collection.immutable.Map[scala.Boolean, scala.Int] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.MapHolder[scala.Boolean, scala.Int]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₃`.expectNull()) null else { - `in₃`.expectToken('{') - `in₃`.parseMap[scala.Boolean, scala.Int]((() => scala.Predef.augmentString(`in₃`.expectString()).toBoolean), (() => `in₃`.expectInt()), scala.Predef.Map.empty[scala.Boolean, scala.Int], true) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) - case _ => - `in₃`.skipValue() - } - maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.MapHolder[scala.Boolean, scala.Int][scala.Boolean, scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Boolean, scala.Int]] { - def encodeValue(`in₄`: co.blocke.scalajack.json.collections.MapHolder[scala.Boolean, scala.Int], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) - def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Boolean, scala.Int] = r0(`in₅`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Boolean, scala.Int]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_MapHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: scala.collection.immutable.Map[java.util.UUID, scala.Predef.String], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - in.foreach[scala.Unit](((x$1: scala.Tuple2[java.util.UUID, java.lang.String]) => x$1 match { - case scala.Tuple2(key, value) => - { - out.maybeComma() - out.value(key) - out.colon() - out.valueEscaped(value) - } - })) - out.endObject() - } - def w0(`in₂`: co.blocke.scalajack.json.collections.MapHolder[java.util.UUID, scala.Predef.String], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `out₂`.label("a") - w1(`in₂`.a, `out₂`) - `out₂`.endObject() - } - def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[java.util.UUID, scala.Predef.String] = { - var _a: scala.collection.immutable.Map[java.util.UUID, scala.Predef.String] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.MapHolder[java.util.UUID, scala.Predef.String]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₃`.expectNull()) null else { - `in₃`.expectToken('{') - `in₃`.parseMap[java.util.UUID, java.lang.String]((() => `in₃`.expectString[java.util.UUID](((x$0: java.lang.String) => java.util.UUID.fromString(x$0)))), (() => `in₃`.expectString()), scala.Predef.Map.empty[java.util.UUID, java.lang.String], true) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) - case _ => - `in₃`.skipValue() - } - maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.MapHolder[java.util.UUID, scala.Predef.String][java.util.UUID, java.lang.String](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[java.util.UUID, scala.Predef.String]] { - def encodeValue(`in₄`: co.blocke.scalajack.json.collections.MapHolder[java.util.UUID, scala.Predef.String], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) - def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[java.util.UUID, scala.Predef.String] = r0(`in₅`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[java.util.UUID, scala.Predef.String]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_MapHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: scala.collection.immutable.Map[scala.Predef.String, scala.Predef.String], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - in.foreach[scala.Unit](((x$1: scala.Tuple2[java.lang.String, java.lang.String]) => x$1 match { - case scala.Tuple2(key, value) => - { - out.maybeComma() - out.valueEscaped(key) - out.colon() - out.valueEscaped(value) - } - })) - out.endObject() - } - def w0(`in₂`: co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Predef.String], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `out₂`.label("a") - w1(`in₂`.a, `out₂`) - `out₂`.endObject() - } - def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Predef.String] = { - var _a: scala.collection.immutable.Map[scala.Predef.String, scala.Predef.String] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Predef.String]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₃`.expectNull()) null else { - `in₃`.expectToken('{') - `in₃`.parseMap[java.lang.String, java.lang.String]((() => `in₃`.expectString()), (() => `in₃`.expectString()), scala.Predef.Map.empty[java.lang.String, java.lang.String], true) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) - case _ => - `in₃`.skipValue() - } - maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Predef.String][java.lang.String, java.lang.String](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Predef.String]] { - def encodeValue(`in₄`: co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Predef.String], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) - def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Predef.String] = r0(`in₅`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Predef.String]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_MapHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: scala.collection.immutable.Map[scala.Predef.String, scala.Long], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - in.foreach[scala.Unit](((x$1: scala.Tuple2[java.lang.String, scala.Long]) => x$1 match { - case scala.Tuple2(key, value) => - { - out.maybeComma() - out.valueEscaped(key) - out.colon() - out.value(value) - } - })) - out.endObject() - } - def w0(`in₂`: co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Long], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `out₂`.label("a") - w1(`in₂`.a, `out₂`) - `out₂`.endObject() - } - def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Long] = { - var _a: scala.collection.immutable.Map[scala.Predef.String, scala.Long] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Long]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₃`.expectNull()) null else { - `in₃`.expectToken('{') - `in₃`.parseMap[java.lang.String, scala.Long]((() => `in₃`.expectString()), (() => `in₃`.expectLong()), scala.Predef.Map.empty[java.lang.String, scala.Long], true) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) - case _ => - `in₃`.skipValue() - } - maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Long][java.lang.String, scala.Long](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Long]] { - def encodeValue(`in₄`: co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Long], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) - def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Long] = r0(`in₅`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Long]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_MapHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: scala.collection.immutable.Map[scala.Predef.String, scala.Boolean], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - in.foreach[scala.Unit](((x$1: scala.Tuple2[java.lang.String, scala.Boolean]) => x$1 match { - case scala.Tuple2(key, value) => - { - out.maybeComma() - out.valueEscaped(key) - out.colon() - out.value(value) - } - })) - out.endObject() - } - def w0(`in₂`: co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Boolean], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `out₂`.label("a") - w1(`in₂`.a, `out₂`) - `out₂`.endObject() - } - def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Boolean] = { - var _a: scala.collection.immutable.Map[scala.Predef.String, scala.Boolean] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Boolean]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₃`.expectNull()) null else { - `in₃`.expectToken('{') - `in₃`.parseMap[java.lang.String, scala.Boolean]((() => `in₃`.expectString()), (() => `in₃`.expectBoolean()), scala.Predef.Map.empty[java.lang.String, scala.Boolean], true) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) - case _ => - `in₃`.skipValue() - } - maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Boolean][java.lang.String, scala.Boolean](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Boolean]] { - def encodeValue(`in₄`: co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Boolean], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) - def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Boolean] = r0(`in₅`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Boolean]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_MapHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: scala.collection.immutable.Map[co.blocke.scalajack.json.collections.Model$package.OnOff, co.blocke.scalajack.json.collections.Model$package.Counter], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - in.foreach[scala.Unit](((x$1: scala.Tuple2[co.blocke.scalajack.json.collections.Model$package.OnOff, co.blocke.scalajack.json.collections.Model$package.Counter]) => x$1 match { - case scala.Tuple2(key, value) => - { - out.maybeComma() - out.valueStringified(key.asInstanceOf[scala.Boolean]) - out.colon() - out.value(value.asInstanceOf[scala.Short]) - } - })) - out.endObject() - } - def w0(`in₂`: co.blocke.scalajack.json.collections.MapHolder[co.blocke.scalajack.json.collections.Model$package.OnOff, co.blocke.scalajack.json.collections.Model$package.Counter], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `out₂`.label("a") - w1(`in₂`.a, `out₂`) - `out₂`.endObject() - } - def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[co.blocke.scalajack.json.collections.Model$package.OnOff, co.blocke.scalajack.json.collections.Model$package.Counter] = { - var _a: scala.collection.immutable.Map[co.blocke.scalajack.json.collections.Model$package.OnOff, co.blocke.scalajack.json.collections.Model$package.Counter] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.MapHolder[co.blocke.scalajack.json.collections.Model$package.OnOff, co.blocke.scalajack.json.collections.Model$package.Counter]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₃`.expectNull()) null else { - `in₃`.expectToken('{') - `in₃`.parseMap[co.blocke.scalajack.json.collections.Model$package.OnOff, co.blocke.scalajack.json.collections.Model$package.Counter]((() => scala.Predef.augmentString(`in₃`.expectString()).toBoolean.asInstanceOf[co.blocke.scalajack.json.collections.Model$package.OnOff]), (() => `in₃`.expectInt().toShort.asInstanceOf[co.blocke.scalajack.json.collections.Model$package.Counter]), scala.Predef.Map.empty[co.blocke.scalajack.json.collections.Model$package.OnOff, co.blocke.scalajack.json.collections.Model$package.Counter], true) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) - case _ => - `in₃`.skipValue() - } - maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.MapHolder[co.blocke.scalajack.json.collections.Model$package.OnOff, co.blocke.scalajack.json.collections.Model$package.Counter][co.blocke.scalajack.json.collections.Model$package.OnOff, co.blocke.scalajack.json.collections.Model$package.Counter](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[co.blocke.scalajack.json.collections.Model$package.OnOff, co.blocke.scalajack.json.collections.Model$package.Counter]] { - def encodeValue(`in₄`: co.blocke.scalajack.json.collections.MapHolder[co.blocke.scalajack.json.collections.Model$package.OnOff, co.blocke.scalajack.json.collections.Model$package.Counter], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) - def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[co.blocke.scalajack.json.collections.Model$package.OnOff, co.blocke.scalajack.json.collections.Model$package.Counter] = r0(`in₅`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[co.blocke.scalajack.json.collections.Model$package.OnOff, co.blocke.scalajack.json.collections.Model$package.Counter]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_MapHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: scala.collection.immutable.Map[scala.Predef.String, java.util.UUID], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - in.foreach[scala.Unit](((x$1: scala.Tuple2[java.lang.String, java.util.UUID]) => x$1 match { - case scala.Tuple2(key, value) => - { - out.maybeComma() - out.valueEscaped(key) - out.colon() - out.value(value) - } - })) - out.endObject() - } - def w0(`in₂`: co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, java.util.UUID], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `out₂`.label("a") - w1(`in₂`.a, `out₂`) - `out₂`.endObject() - } - def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, java.util.UUID] = { - var _a: scala.collection.immutable.Map[scala.Predef.String, java.util.UUID] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, java.util.UUID]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₃`.expectNull()) null else { - `in₃`.expectToken('{') - `in₃`.parseMap[java.lang.String, java.util.UUID]((() => `in₃`.expectString()), (() => `in₃`.expectString[java.util.UUID](((x$0: java.lang.String) => java.util.UUID.fromString(x$0)))), scala.Predef.Map.empty[java.lang.String, java.util.UUID], true) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) - case _ => - `in₃`.skipValue() - } - maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, java.util.UUID][java.lang.String, java.util.UUID](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, java.util.UUID]] { - def encodeValue(`in₄`: co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, java.util.UUID], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) - def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, java.util.UUID] = r0(`in₅`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, java.util.UUID]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_MapHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w2(in: scala.collection.immutable.List[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startArray() - in.foreach[scala.Unit](((i: scala.Int) => out.value(i))) - out.endArray() - } - def w1(`in₂`: scala.collection.immutable.Map[scala.Predef.String, scala.collection.immutable.List[scala.Int]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `in₂`.foreach[scala.Unit](((x$1: scala.Tuple2[java.lang.String, scala.collection.immutable.List[scala.Int]]) => x$1 match { - case scala.Tuple2(key, value) => - { - `out₂`.maybeComma() - `out₂`.valueEscaped(key) - `out₂`.colon() - w2(value, `out₂`) - } - })) - `out₂`.endObject() - } - def w0(`in₃`: co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.collection.immutable.List[scala.Int]], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { - `out₃`.startObject() - `out₃`.label("a") - w1(`in₃`.a, `out₃`) - `out₃`.endObject() - } - def r0(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.collection.immutable.List[scala.Int]] = { - var _a: scala.collection.immutable.Map[scala.Predef.String, scala.collection.immutable.List[scala.Int]] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₄`.expectFirstObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.collection.immutable.List[scala.Int]]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₄`.expectNull()) null else { - `in₄`.expectToken('{') - `in₄`.parseMap[java.lang.String, scala.collection.immutable.List[scala.Int]]((() => `in₄`.expectString()), (() => { - val parsedArray: scala.collection.mutable.ListBuffer[scala.Int] = `in₄`.expectArray[scala.Int]((() => `in₄`.expectInt())) - - (if (parsedArray.!=(null)) parsedArray.toList else null: scala.collection.immutable.List[scala.Int]) - }), scala.Predef.Map.empty[java.lang.String, scala.collection.immutable.List[scala.Int]], true) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₄`) - case _ => - `in₄`.skipValue() - } - maybeFieldNum = `in₄`.expectObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.collection.immutable.List[scala.Int]][java.lang.String, scala.collection.immutable.List[scala.Int]](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₄`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.collection.immutable.List[scala.Int]]] { - def encodeValue(`in₅`: co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.collection.immutable.List[scala.Int]], `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₅`, `out₄`) - def decodeValue(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.collection.immutable.List[scala.Int]] = r0(`in₆`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.collection.immutable.List[scala.Int]]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_MapHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w2(in: scala.collection.immutable.Map[scala.Predef.String, scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - in.foreach[scala.Unit](((x$1: scala.Tuple2[java.lang.String, scala.Int]) => x$1 match { - case scala.Tuple2(key, value) => - { - out.maybeComma() - out.valueEscaped(key) - out.colon() - out.value(value) - } - })) - out.endObject() - } - def w1(`in₂`: scala.collection.immutable.Map[scala.Predef.String, scala.collection.immutable.Map[scala.Predef.String, scala.Int]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `in₂`.foreach[scala.Unit](((`x$1₂`: scala.Tuple2[java.lang.String, scala.collection.immutable.Map[scala.Predef.String, scala.Int]]) => `x$1₂` match { - case scala.Tuple2(key, value) => - { - `out₂`.maybeComma() - `out₂`.valueEscaped(`key₂`) - `out₂`.colon() - w2(`value₂`, `out₂`) - } - })) - `out₂`.endObject() - } - def w0(`in₃`: co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.collection.immutable.Map[scala.Predef.String, scala.Int]], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { - `out₃`.startObject() - `out₃`.label("a") - w1(`in₃`.a, `out₃`) - `out₃`.endObject() - } - def r0(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.collection.immutable.Map[scala.Predef.String, scala.Int]] = { - var _a: scala.collection.immutable.Map[scala.Predef.String, scala.collection.immutable.Map[scala.Predef.String, scala.Int]] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₄`.expectFirstObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.collection.immutable.Map[scala.Predef.String, scala.Int]]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₄`.expectNull()) null else { - `in₄`.expectToken('{') - `in₄`.parseMap[java.lang.String, scala.collection.immutable.Map[scala.Predef.String, scala.Int]]((() => `in₄`.expectString()), (() => if (`in₄`.expectNull()) null else { - `in₄`.expectToken('{') - `in₄`.parseMap[java.lang.String, scala.Int]((() => `in₄`.expectString()), (() => `in₄`.expectInt()), scala.Predef.Map.empty[java.lang.String, scala.Int], true) - }), scala.Predef.Map.empty[java.lang.String, scala.collection.immutable.Map[scala.Predef.String, scala.Int]], true) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₄`) - case _ => - `in₄`.skipValue() - } - maybeFieldNum = `in₄`.expectObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.collection.immutable.Map[scala.Predef.String, scala.Int]][java.lang.String, scala.collection.immutable.Map[scala.Predef.String, scala.Int]](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₄`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.collection.immutable.Map[scala.Predef.String, scala.Int]]] { - def encodeValue(`in₅`: co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.collection.immutable.Map[scala.Predef.String, scala.Int]], `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₅`, `out₄`) - def decodeValue(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.collection.immutable.Map[scala.Predef.String, scala.Int]] = r0(`in₆`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.collection.immutable.Map[scala.Predef.String, scala.Int]]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_MapHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w2(in: scala.collection.immutable.List[scala.Predef.String], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startArray() - in.foreach[scala.Unit](((i: java.lang.String) => out.valueEscaped(i))) - out.endArray() - } - def w1(`in₂`: scala.collection.immutable.Map[scala.Predef.String, scala.Int | scala.collection.immutable.List[scala.Predef.String]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `in₂`.foreach[scala.Unit](((x$1: scala.Tuple2[java.lang.String, scala.Int | scala.collection.immutable.List[scala.Predef.String]]) => x$1 match { - case scala.Tuple2(key, value) => - if (value.==(null)) `out₂`.burpNull() else { - `out₂`.mark() - scala.util.Try.apply[scala.Unit]({ - `out₂`.maybeComma() - `out₂`.valueEscaped(key) - `out₂`.colon() - w2(value.asInstanceOf[scala.collection.immutable.List[scala.Predef.String]], `out₂`) - }) match { - case scala.util.Success(_) => - () - case scala.util.Failure(f) => - `out₂`.revert() - { - `out₂`.maybeComma() - `out₂`.valueEscaped(key) - `out₂`.colon() - `out₂`.value(value.asInstanceOf[scala.Int]) - } - } - } - })) - `out₂`.endObject() - } - def w0(`in₃`: co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Int | scala.collection.immutable.List[scala.Predef.String]], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { - `out₃`.startObject() - `out₃`.label("a") - w1(`in₃`.a, `out₃`) - `out₃`.endObject() - } - def r0(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Int | scala.collection.immutable.List[scala.Predef.String]] = { - var _a: scala.collection.immutable.Map[scala.Predef.String, scala.Int | scala.collection.immutable.List[scala.Predef.String]] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₄`.expectFirstObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Int | scala.collection.immutable.List[scala.Predef.String]]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₄`.expectNull()) null else { - `in₄`.expectToken('{') - `in₄`.parseMap[java.lang.String, scala.Int | scala.collection.immutable.List[scala.Predef.String]]((() => `in₄`.expectString()), (() => { - val mark: scala.Int = `in₄`.pos - scala.util.Try.apply[scala.Int](`in₄`.expectInt()) match { - case scala.util.Success(lval) => - (lval: scala.Int) - case scala.util.Failure(f) => - `in₄`.revertToPos(mark) - scala.util.Try.apply[scala.collection.immutable.List[scala.Predef.String]]({ - val parsedArray: scala.collection.mutable.ListBuffer[java.lang.String] = `in₄`.expectArray[java.lang.String]((() => `in₄`.expectString())) - - (if (parsedArray.!=(null)) parsedArray.toList else null: scala.collection.immutable.List[java.lang.String]) - }) match { - case scala.util.Success(rval) => - (rval: scala.collection.immutable.List[scala.Predef.String]) - case scala.util.Failure(_) => - `in₄`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₄`) - } - } - }), scala.Predef.Map.empty[java.lang.String, scala.Int | scala.collection.immutable.List[scala.Predef.String]], true) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₄`) - case _ => - `in₄`.skipValue() - } - maybeFieldNum = `in₄`.expectObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Int | scala.collection.immutable.List[scala.Predef.String]][java.lang.String, scala.Int | scala.collection.immutable.List[scala.Predef.String]](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₄`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Int | scala.collection.immutable.List[scala.Predef.String]]] { - def encodeValue(`in₅`: co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Int | scala.collection.immutable.List[scala.Predef.String]], `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₅`, `out₄`) - def decodeValue(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Int | scala.collection.immutable.List[scala.Predef.String]] = r0(`in₆`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, scala.Int | scala.collection.immutable.List[scala.Predef.String]]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_Person_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("name", "age")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - val __co_blocke_scalajack_json_collections_MapHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w2(in: co.blocke.scalajack.json.collections.Person, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("name") - out.valueEscaped(in.name) - out.label("age") - out.value(in.age) - out.endObject() - } - def w1(`in₂`: scala.collection.immutable.Map[scala.Predef.String, co.blocke.scalajack.json.collections.Person], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `in₂`.foreach[scala.Unit](((x$1: scala.Tuple2[java.lang.String, co.blocke.scalajack.json.collections.Person]) => x$1 match { - case scala.Tuple2(key, value) => - { - `out₂`.maybeComma() - `out₂`.valueEscaped(key) - `out₂`.colon() - w2(value, `out₂`) - } - })) - `out₂`.endObject() - } - def w0(`in₃`: co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Person], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { - `out₃`.startObject() - `out₃`.label("a") - w1(`in₃`.a, `out₃`) - `out₃`.endObject() - } - def r1(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.Person = { - var _name: java.lang.String = "" - var _age: scala.Int = 0 - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₄`.expectFirstObjectField(__co_blocke_scalajack_json_collections_Person_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.Person] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _name = `in₄`.expectString() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("name"), `in₄`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _age = `in₄`.expectInt() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("age"), `in₄`) - case _ => - `in₄`.skipValue() - } - maybeFieldNum = `in₄`.expectObjectField(__co_blocke_scalajack_json_collections_Person_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.collections.Person(_name, _age) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("name", "age")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₄`) - } - } - def r0(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Person] = { - var _a: scala.collection.immutable.Map[scala.Predef.String, co.blocke.scalajack.json.collections.Person] = null - var `required₂`: scala.Int = 1 - var `maybeFieldNum₂`: scala.Option[scala.Int] = `in₅`.expectFirstObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) - if (`maybeFieldNum₂`.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Person]] else { - while (`maybeFieldNum₂`.isDefined) { - `maybeFieldNum₂`.get match { - case 0 => - if (`required₂`.&(1).!=(0)) { - `required₂` = `required₂`.^(1) - _a = if (`in₅`.expectNull()) null else { - `in₅`.expectToken('{') - `in₅`.parseMap[java.lang.String, co.blocke.scalajack.json.collections.Person]((() => `in₅`.expectString()), (() => r1(`in₅`)), scala.Predef.Map.empty[java.lang.String, co.blocke.scalajack.json.collections.Person], true) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₅`) - case _ => - `in₅`.skipValue() - } - `maybeFieldNum₂` = `in₅`.expectObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) - } - if (`required₂`.&(1).==(0)) new co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Person][java.lang.String, co.blocke.scalajack.json.collections.Person](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(`required₂`.&(1)))), `in₅`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Person]] { - def encodeValue(`in₆`: co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Person], `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₆`, `out₄`) - def decodeValue(`in₇`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Person] = r0(`in₇`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Person]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_MapHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: scala.collection.immutable.Map[co.blocke.scalajack.json.collections.Distance, scala.Predef.String], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - in.foreach[scala.Unit](((x$1: scala.Tuple2[co.blocke.scalajack.json.collections.Distance, java.lang.String]) => x$1 match { - case scala.Tuple2(key, value) => - { - out.maybeComma() - out.valueStringified(key.meters) - out.colon() - out.valueEscaped(value) - } - })) - out.endObject() - } - def w0(`in₂`: co.blocke.scalajack.json.collections.MapHolder[co.blocke.scalajack.json.collections.Distance, scala.Predef.String], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `out₂`.label("a") - w1(`in₂`.a, `out₂`) - `out₂`.endObject() - } - def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[co.blocke.scalajack.json.collections.Distance, scala.Predef.String] = { - var _a: scala.collection.immutable.Map[co.blocke.scalajack.json.collections.Distance, scala.Predef.String] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.MapHolder[co.blocke.scalajack.json.collections.Distance, scala.Predef.String]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₃`.expectNull()) null else { - `in₃`.expectToken('{') - `in₃`.parseMap[co.blocke.scalajack.json.collections.Distance, java.lang.String]((() => new co.blocke.scalajack.json.collections.Distance(scala.Predef.augmentString(`in₃`.expectString()).toDouble)), (() => `in₃`.expectString()), scala.Predef.Map.empty[co.blocke.scalajack.json.collections.Distance, java.lang.String], true) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) - case _ => - `in₃`.skipValue() - } - maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.MapHolder[co.blocke.scalajack.json.collections.Distance, scala.Predef.String][co.blocke.scalajack.json.collections.Distance, java.lang.String](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[co.blocke.scalajack.json.collections.Distance, scala.Predef.String]] { - def encodeValue(`in₄`: co.blocke.scalajack.json.collections.MapHolder[co.blocke.scalajack.json.collections.Distance, scala.Predef.String], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) - def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[co.blocke.scalajack.json.collections.Distance, scala.Predef.String] = r0(`in₅`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[co.blocke.scalajack.json.collections.Distance, scala.Predef.String]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_MapHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: scala.collection.immutable.Map[scala.Predef.String, co.blocke.scalajack.json.collections.Distance], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - in.foreach[scala.Unit](((x$1: scala.Tuple2[java.lang.String, co.blocke.scalajack.json.collections.Distance]) => x$1 match { - case scala.Tuple2(key, value) => - { - out.maybeComma() - out.valueEscaped(key) - out.colon() - out.value(value.meters) - } - })) - out.endObject() - } - def w0(`in₂`: co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Distance], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `out₂`.label("a") - w1(`in₂`.a, `out₂`) - `out₂`.endObject() - } - def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Distance] = { - var _a: scala.collection.immutable.Map[scala.Predef.String, co.blocke.scalajack.json.collections.Distance] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Distance]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₃`.expectNull()) null else { - `in₃`.expectToken('{') - `in₃`.parseMap[java.lang.String, co.blocke.scalajack.json.collections.Distance]((() => `in₃`.expectString()), (() => new co.blocke.scalajack.json.collections.Distance(`in₃`.expectDouble())), scala.Predef.Map.empty[java.lang.String, co.blocke.scalajack.json.collections.Distance], true) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) - case _ => - `in₃`.skipValue() - } - maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_MapHolder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Distance][java.lang.String, co.blocke.scalajack.json.collections.Distance](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Distance]] { - def encodeValue(`in₄`: co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Distance], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) - def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Distance] = r0(`in₅`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Distance]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_MMapHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: scala.collection.mutable.Map[scala.Predef.String, co.blocke.scalajack.json.collections.Distance], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - in.foreach[scala.Unit](((x$1: scala.Tuple2[java.lang.String, co.blocke.scalajack.json.collections.Distance]) => x$1 match { - case scala.Tuple2(key, value) => - { - out.maybeComma() - out.valueEscaped(key) - out.colon() - out.value(value.meters) - } - })) - out.endObject() - } - def w0(`in₂`: co.blocke.scalajack.json.collections.MMapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Distance], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `out₂`.label("a") - w1(`in₂`.a, `out₂`) - `out₂`.endObject() - } - def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MMapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Distance] = { - var _a: scala.collection.mutable.Map[scala.Predef.String, co.blocke.scalajack.json.collections.Distance] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_MMapHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.MMapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Distance]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₃`.expectNull()) null else { - `in₃`.expectToken('{') - scala.collection.mutable.Map.from[java.lang.String, co.blocke.scalajack.json.collections.Distance](`in₃`.parseMap[java.lang.String, co.blocke.scalajack.json.collections.Distance]((() => `in₃`.expectString()), (() => new co.blocke.scalajack.json.collections.Distance(`in₃`.expectDouble())), scala.Predef.Map.empty[java.lang.String, co.blocke.scalajack.json.collections.Distance], true)) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) - case _ => - `in₃`.skipValue() - } - maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_MMapHolder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.MMapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Distance][java.lang.String, co.blocke.scalajack.json.collections.Distance](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MMapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Distance]] { - def encodeValue(`in₄`: co.blocke.scalajack.json.collections.MMapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Distance], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) - def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MMapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Distance] = r0(`in₅`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MMapHolder[scala.Predef.String, co.blocke.scalajack.json.collections.Distance]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_MMapHolder2_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: scala.collection.mutable.HashMap[scala.Predef.String, co.blocke.scalajack.json.collections.Distance], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - in.foreach[scala.Unit](((x$1: scala.Tuple2[java.lang.String, co.blocke.scalajack.json.collections.Distance]) => x$1 match { - case scala.Tuple2(key, value) => - { - out.maybeComma() - out.valueEscaped(key) - out.colon() - out.value(value.meters) - } - })) - out.endObject() - } - def w0(`in₂`: co.blocke.scalajack.json.collections.MMapHolder2[scala.Predef.String, co.blocke.scalajack.json.collections.Distance], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `out₂`.label("a") - w1(`in₂`.a, `out₂`) - `out₂`.endObject() - } - def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MMapHolder2[scala.Predef.String, co.blocke.scalajack.json.collections.Distance] = { - var _a: scala.collection.mutable.HashMap[scala.Predef.String, co.blocke.scalajack.json.collections.Distance] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_MMapHolder2_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.MMapHolder2[scala.Predef.String, co.blocke.scalajack.json.collections.Distance]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₃`.expectNull()) null else { - `in₃`.expectToken('{') - scala.collection.mutable.HashMap.from[java.lang.String, co.blocke.scalajack.json.collections.Distance](`in₃`.parseMap[java.lang.String, co.blocke.scalajack.json.collections.Distance]((() => `in₃`.expectString()), (() => new co.blocke.scalajack.json.collections.Distance(`in₃`.expectDouble())), scala.Predef.Map.empty[java.lang.String, co.blocke.scalajack.json.collections.Distance], true)) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) - case _ => - `in₃`.skipValue() - } - maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_MMapHolder2_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.MMapHolder2[scala.Predef.String, co.blocke.scalajack.json.collections.Distance][java.lang.String, co.blocke.scalajack.json.collections.Distance](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MMapHolder2[scala.Predef.String, co.blocke.scalajack.json.collections.Distance]] { - def encodeValue(`in₄`: co.blocke.scalajack.json.collections.MMapHolder2[scala.Predef.String, co.blocke.scalajack.json.collections.Distance], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) - def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MMapHolder2[scala.Predef.String, co.blocke.scalajack.json.collections.Distance] = r0(`in₅`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MMapHolder2[scala.Predef.String, co.blocke.scalajack.json.collections.Distance]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_MMapHolder3_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: scala.collection.mutable.SeqMap[scala.Predef.String, co.blocke.scalajack.json.collections.Distance], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - in.foreach[scala.Unit](((x$1: scala.Tuple2[java.lang.String, co.blocke.scalajack.json.collections.Distance]) => x$1 match { - case scala.Tuple2(key, value) => - { - out.maybeComma() - out.valueEscaped(key) - out.colon() - out.value(value.meters) - } - })) - out.endObject() - } - def w0(`in₂`: co.blocke.scalajack.json.collections.MMapHolder3[scala.Predef.String, co.blocke.scalajack.json.collections.Distance], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `out₂`.label("a") - w1(`in₂`.a, `out₂`) - `out₂`.endObject() - } - def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MMapHolder3[scala.Predef.String, co.blocke.scalajack.json.collections.Distance] = { - var _a: scala.collection.mutable.SeqMap[scala.Predef.String, co.blocke.scalajack.json.collections.Distance] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_MMapHolder3_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.MMapHolder3[scala.Predef.String, co.blocke.scalajack.json.collections.Distance]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₃`.expectNull()) null else { - `in₃`.expectToken('{') - scala.collection.mutable.Map.from[java.lang.String, co.blocke.scalajack.json.collections.Distance](`in₃`.parseMap[java.lang.String, co.blocke.scalajack.json.collections.Distance]((() => `in₃`.expectString()), (() => new co.blocke.scalajack.json.collections.Distance(`in₃`.expectDouble())), scala.Predef.Map.empty[java.lang.String, co.blocke.scalajack.json.collections.Distance], true)).to[scala.collection.mutable.SeqMap[scala.Predef.String, co.blocke.scalajack.json.collections.Distance]](scala.collection.mutable.SeqMap.mapFactory[java.lang.String, co.blocke.scalajack.json.collections.Distance]) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) - case _ => - `in₃`.skipValue() - } - maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_MMapHolder3_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.MMapHolder3[scala.Predef.String, co.blocke.scalajack.json.collections.Distance][java.lang.String, co.blocke.scalajack.json.collections.Distance](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MMapHolder3[scala.Predef.String, co.blocke.scalajack.json.collections.Distance]] { - def encodeValue(`in₄`: co.blocke.scalajack.json.collections.MMapHolder3[scala.Predef.String, co.blocke.scalajack.json.collections.Distance], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) - def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MMapHolder3[scala.Predef.String, co.blocke.scalajack.json.collections.Distance] = r0(`in₅`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MMapHolder3[scala.Predef.String, co.blocke.scalajack.json.collections.Distance]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_MapHolder2_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: scala.collection.immutable.HashMap[scala.Predef.String, co.blocke.scalajack.json.collections.Distance], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - in.foreach[scala.Unit](((x$1: scala.Tuple2[java.lang.String, co.blocke.scalajack.json.collections.Distance]) => x$1 match { - case scala.Tuple2(key, value) => - { - out.maybeComma() - out.valueEscaped(key) - out.colon() - out.value(value.meters) - } - })) - out.endObject() - } - def w0(`in₂`: co.blocke.scalajack.json.collections.MapHolder2[scala.Predef.String, co.blocke.scalajack.json.collections.Distance], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `out₂`.label("a") - w1(`in₂`.a, `out₂`) - `out₂`.endObject() - } - def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder2[scala.Predef.String, co.blocke.scalajack.json.collections.Distance] = { - var _a: scala.collection.immutable.HashMap[scala.Predef.String, co.blocke.scalajack.json.collections.Distance] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_MapHolder2_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.MapHolder2[scala.Predef.String, co.blocke.scalajack.json.collections.Distance]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₃`.expectNull()) null else { - `in₃`.expectToken('{') - scala.collection.immutable.HashMap.from[java.lang.String, co.blocke.scalajack.json.collections.Distance](`in₃`.parseMap[java.lang.String, co.blocke.scalajack.json.collections.Distance]((() => `in₃`.expectString()), (() => new co.blocke.scalajack.json.collections.Distance(`in₃`.expectDouble())), scala.Predef.Map.empty[java.lang.String, co.blocke.scalajack.json.collections.Distance], true)) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) - case _ => - `in₃`.skipValue() - } - maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_MapHolder2_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.MapHolder2[scala.Predef.String, co.blocke.scalajack.json.collections.Distance][java.lang.String, co.blocke.scalajack.json.collections.Distance](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder2[scala.Predef.String, co.blocke.scalajack.json.collections.Distance]] { - def encodeValue(`in₄`: co.blocke.scalajack.json.collections.MapHolder2[scala.Predef.String, co.blocke.scalajack.json.collections.Distance], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) - def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder2[scala.Predef.String, co.blocke.scalajack.json.collections.Distance] = r0(`in₅`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder2[scala.Predef.String, co.blocke.scalajack.json.collections.Distance]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_MapHolder3_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: scala.collection.immutable.SeqMap[scala.Predef.String, co.blocke.scalajack.json.collections.Distance], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - in.foreach[scala.Unit](((x$1: scala.Tuple2[java.lang.String, co.blocke.scalajack.json.collections.Distance]) => x$1 match { - case scala.Tuple2(key, value) => - { - out.maybeComma() - out.valueEscaped(key) - out.colon() - out.value(value.meters) - } - })) - out.endObject() - } - def w0(`in₂`: co.blocke.scalajack.json.collections.MapHolder3[scala.Predef.String, co.blocke.scalajack.json.collections.Distance], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `out₂`.label("a") - w1(`in₂`.a, `out₂`) - `out₂`.endObject() - } - def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder3[scala.Predef.String, co.blocke.scalajack.json.collections.Distance] = { - var _a: scala.collection.immutable.SeqMap[scala.Predef.String, co.blocke.scalajack.json.collections.Distance] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_MapHolder3_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.MapHolder3[scala.Predef.String, co.blocke.scalajack.json.collections.Distance]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₃`.expectNull()) null else { - `in₃`.expectToken('{') - scala.collection.immutable.SeqMap.from[java.lang.String, co.blocke.scalajack.json.collections.Distance](`in₃`.parseMap[java.lang.String, co.blocke.scalajack.json.collections.Distance]((() => `in₃`.expectString()), (() => new co.blocke.scalajack.json.collections.Distance(`in₃`.expectDouble())), scala.Predef.Map.empty[java.lang.String, co.blocke.scalajack.json.collections.Distance], true)) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) - case _ => - `in₃`.skipValue() - } - maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_MapHolder3_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.MapHolder3[scala.Predef.String, co.blocke.scalajack.json.collections.Distance][java.lang.String, co.blocke.scalajack.json.collections.Distance](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder3[scala.Predef.String, co.blocke.scalajack.json.collections.Distance]] { - def encodeValue(`in₄`: co.blocke.scalajack.json.collections.MapHolder3[scala.Predef.String, co.blocke.scalajack.json.collections.Distance], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) - def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.MapHolder3[scala.Predef.String, co.blocke.scalajack.json.collections.Distance] = r0(`in₅`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.MapHolder3[scala.Predef.String, co.blocke.scalajack.json.collections.Distance]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_TupleHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: scala.Tuple3[scala.Int, scala.Predef.String, scala.Boolean], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startArray() - out.value(in._1) - out.valueEscaped(in._2) - out.value(in._3) - out.endArray() - } - def w0(`in₂`: co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `out₂`.label("a") - w1(`in₂`.a, `out₂`) - `out₂`.endObject() - } - def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean] = { - var _a: scala.Tuple3[scala.Int, scala.Predef.String, scala.Boolean] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_TupleHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₃`.expectNull()) null else { - `in₃`.expectToken('[') - val tv: scala.Tuple3[scala.Int, scala.Predef.String, scala.Boolean] = new scala.Tuple3[scala.Int, scala.Predef.String, scala.Boolean][scala.Int, java.lang.String, scala.Boolean](`in₃`.expectInt(), { - `in₃`.expectToken(',') - `in₃`.expectString() - }, { - `in₃`.expectToken(',') - `in₃`.expectBoolean() - }) - `in₃`.expectToken(']') - - (tv: scala.Tuple3[scala.Int, scala.Predef.String, scala.Boolean]) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) - case _ => - `in₃`.skipValue() - } - maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_TupleHolder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean][scala.Int, java.lang.String, scala.Boolean](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean]] { - def encodeValue(`in₄`: co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) - def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean] = r0(`in₅`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_TupleHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: scala.Tuple3[scala.Int, scala.Predef.String, scala.Boolean], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startArray() - out.value(in._1) - out.valueEscaped(in._2) - out.value(in._3) - out.endArray() - } - def w0(`in₂`: co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `out₂`.label("a") - w1(`in₂`.a, `out₂`) - `out₂`.endObject() - } - def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean] = { - var _a: scala.Tuple3[scala.Int, scala.Predef.String, scala.Boolean] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_TupleHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₃`.expectNull()) null else { - `in₃`.expectToken('[') - val tv: scala.Tuple3[scala.Int, scala.Predef.String, scala.Boolean] = new scala.Tuple3[scala.Int, scala.Predef.String, scala.Boolean][scala.Int, java.lang.String, scala.Boolean](`in₃`.expectInt(), { - `in₃`.expectToken(',') - `in₃`.expectString() - }, { - `in₃`.expectToken(',') - `in₃`.expectBoolean() - }) - `in₃`.expectToken(']') - - (tv: scala.Tuple3[scala.Int, scala.Predef.String, scala.Boolean]) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) - case _ => - `in₃`.skipValue() - } - maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_TupleHolder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean][scala.Int, java.lang.String, scala.Boolean](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean]] { - def encodeValue(`in₄`: co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) - def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean] = r0(`in₅`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_TupleHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: scala.Tuple3[scala.Int, scala.Predef.String, scala.Boolean], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startArray() - out.value(in._1) - out.valueEscaped(in._2) - out.value(in._3) - out.endArray() - } - def w0(`in₂`: co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `out₂`.label("a") - w1(`in₂`.a, `out₂`) - `out₂`.endObject() - } - def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean] = { - var _a: scala.Tuple3[scala.Int, scala.Predef.String, scala.Boolean] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_TupleHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₃`.expectNull()) null else { - `in₃`.expectToken('[') - val tv: scala.Tuple3[scala.Int, scala.Predef.String, scala.Boolean] = new scala.Tuple3[scala.Int, scala.Predef.String, scala.Boolean][scala.Int, java.lang.String, scala.Boolean](`in₃`.expectInt(), { - `in₃`.expectToken(',') - `in₃`.expectString() - }, { - `in₃`.expectToken(',') - `in₃`.expectBoolean() - }) - `in₃`.expectToken(']') - - (tv: scala.Tuple3[scala.Int, scala.Predef.String, scala.Boolean]) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) - case _ => - `in₃`.skipValue() - } - maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_TupleHolder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean][scala.Int, java.lang.String, scala.Boolean](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean]] { - def encodeValue(`in₄`: co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) - def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean] = r0(`in₅`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean]]) -} -Codec: { - val __co_blocke_scalajack_json_collections_TupleHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: scala.Tuple3[scala.Int, scala.Predef.String, scala.Boolean], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startArray() - out.value(in._1) - out.valueEscaped(in._2) - out.value(in._3) - out.endArray() - } - def w0(`in₂`: co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `out₂`.label("a") - w1(`in₂`.a, `out₂`) - `out₂`.endObject() - } - def r0(`in₃`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean] = { - var _a: scala.Tuple3[scala.Int, scala.Predef.String, scala.Boolean] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₃`.expectFirstObjectField(__co_blocke_scalajack_json_collections_TupleHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₃`.expectNull()) null else { - `in₃`.expectToken('[') - val tv: scala.Tuple3[scala.Int, scala.Predef.String, scala.Boolean] = new scala.Tuple3[scala.Int, scala.Predef.String, scala.Boolean][scala.Int, java.lang.String, scala.Boolean](`in₃`.expectInt(), { - `in₃`.expectToken(',') - `in₃`.expectString() - }, { - `in₃`.expectToken(',') - `in₃`.expectBoolean() - }) - `in₃`.expectToken(']') - - (tv: scala.Tuple3[scala.Int, scala.Predef.String, scala.Boolean]) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₃`) - case _ => - `in₃`.skipValue() - } - maybeFieldNum = `in₃`.expectObjectField(__co_blocke_scalajack_json_collections_TupleHolder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean][scala.Int, java.lang.String, scala.Boolean](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₃`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean]] { - def encodeValue(`in₄`: co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₄`, `out₃`) - def decodeValue(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean] = r0(`in₅`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.collections.TupleHolder[scala.Int, scala.Predef.String, scala.Boolean]]) -} -Codec: { - val __co_blocke_scalajack_json_misc_AliasHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a", "b", "c", "d")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: scala.collection.immutable.List[AliasSpec.this.Count], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startArray() - in.foreach[scala.Unit](((i: scala.Int) => out.value(i))) - out.endArray() - } - def w2(`in₂`: scala.collection.immutable.Map[AliasSpec.this.Count, scala.Predef.String], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `in₂`.foreach[scala.Unit](((x$1: scala.Tuple2[scala.Int, java.lang.String]) => x$1 match { - case scala.Tuple2(key, value) => - { - `out₂`.maybeComma() - `out₂`.valueStringified(scala.Int.int2long(key)) - `out₂`.colon() - `out₂`.valueEscaped(value) - } - })) - `out₂`.endObject() - } - def w3(`in₃`: scala.collection.immutable.Map[scala.Predef.String, AliasSpec.this.Count], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { - `out₃`.startObject() - `in₃`.foreach[scala.Unit](((`x$1₂`: scala.Tuple2[java.lang.String, scala.Int]) => `x$1₂` match { - case scala.Tuple2(key, value) => - { - `out₃`.maybeComma() - `out₃`.valueEscaped(`key₂`) - `out₃`.colon() - `out₃`.value(`value₂`) - } - })) - `out₃`.endObject() - } - def w0(`in₄`: co.blocke.scalajack.json.misc.AliasHolder[AliasSpec.this.Count], `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₄`.==(null)) `out₄`.burpNull() else { - `out₄`.startObject() - `out₄`.label("a") - `out₄`.value(`in₄`.a) - `out₄`.label("b") - w1(`in₄`.b, `out₄`) - `out₄`.label("c") - w2(`in₄`.c, `out₄`) - `out₄`.label("d") - w3(`in₄`.d, `out₄`) - `out₄`.endObject() - } - def r0(`in₅`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.AliasHolder[AliasSpec.this.Count] = { - var _a: scala.Int = 0 - var _b: scala.collection.immutable.List[AliasSpec.this.Count] = null - var _c: scala.collection.immutable.Map[AliasSpec.this.Count, scala.Predef.String] = null - var _d: scala.collection.immutable.Map[scala.Predef.String, AliasSpec.this.Count] = null - var required: scala.Int = 15 - var maybeFieldNum: scala.Option[scala.Int] = `in₅`.expectFirstObjectField(__co_blocke_scalajack_json_misc_AliasHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.AliasHolder[AliasSpec.this.Count]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = `in₅`.expectInt() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₅`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _b = { - val parsedArray: scala.collection.mutable.ListBuffer[scala.Int] = `in₅`.expectArray[scala.Int]((() => `in₅`.expectInt())) - - (if (parsedArray.!=(null)) parsedArray.toList else null: scala.collection.immutable.List[scala.Int]) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b"), `in₅`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _c = if (`in₅`.expectNull()) null else { - `in₅`.expectToken('{') - `in₅`.parseMap[scala.Int, java.lang.String]((() => scala.Predef.augmentString(`in₅`.expectString()).toInt), (() => `in₅`.expectString()), scala.Predef.Map.empty[scala.Int, java.lang.String], true) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c"), `in₅`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _d = if (`in₅`.expectNull()) null else { - `in₅`.expectToken('{') - `in₅`.parseMap[java.lang.String, scala.Int]((() => `in₅`.expectString()), (() => `in₅`.expectInt()), scala.Predef.Map.empty[java.lang.String, scala.Int], true) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d"), `in₅`) - case _ => - `in₅`.skipValue() - } - maybeFieldNum = `in₅`.expectObjectField(__co_blocke_scalajack_json_misc_AliasHolder_fields) - } - if (required.&(15).==(0)) new co.blocke.scalajack.json.misc.AliasHolder[AliasSpec.this.Count][scala.Int](_a, _b, _c, _d) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a", "b", "c", "d")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₅`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.AliasHolder[AliasSpec.this.Count]] { - def encodeValue(`in₆`: co.blocke.scalajack.json.misc.AliasHolder[AliasSpec.this.Count], `out₅`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₆`, `out₅`) - def decodeValue(`in₇`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.AliasHolder[AliasSpec.this.Count] = r0(`in₇`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.AliasHolder[AliasSpec.this.Count]]) -} -Codec: { - val __co_blocke_scalajack_json_misc_AliasHolder2_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a", "b", "c")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: scala.collection.immutable.List[AliasSpec.this.CountX], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startArray() - in.foreach[scala.Unit](((i: scala.Option[scala.Int]) => i match { - case null => - out.burpNull() - case scala.None => - () - case scala.Some(v) => - val vv: scala.Int = v.asInstanceOf[scala.Int] - out.value(vv) - })) - out.endArray() - } - def w2(`in₂`: scala.collection.immutable.Map[scala.Predef.String, AliasSpec.this.CountX], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `in₂`.foreach[scala.Unit](((x$1: scala.Tuple2[java.lang.String, scala.Option[scala.Int]]) => x$1 match { - case scala.Tuple2(key, value) => - value match { - case scala.None => - () - case scala.Some(v) => - { - `out₂`.maybeComma() - `out₂`.valueEscaped(key) - `out₂`.colon() - `out₂`.value(`v₂`) - } - } - })) - `out₂`.endObject() - } - def w0(`in₃`: co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountX], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { - `out₃`.startObject() - `in₃`.a match { - case scala.None => - () - case scala.Some(v) => - { - `out₃`.label("a") - `out₃`.value(`v₃`) - } - } - `out₃`.label("b") - w1(`in₃`.b, `out₃`) - `out₃`.label("c") - w2(`in₃`.c, `out₃`) - `out₃`.endObject() - } - def r0(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountX] = { - var _a: scala.Option[scala.Int] = scala.None - var _b: scala.collection.immutable.List[AliasSpec.this.CountX] = null - var _c: scala.collection.immutable.Map[scala.Predef.String, AliasSpec.this.CountX] = null - var required: scala.Int = 7 - var maybeFieldNum: scala.Option[scala.Int] = `in₄`.expectFirstObjectField(__co_blocke_scalajack_json_misc_AliasHolder2_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountX]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₄`.expectNull()) null else scala.Some.apply[scala.Int](`in₄`.expectInt()) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₄`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _b = { - val parsedArray: scala.collection.mutable.ListBuffer[scala.Option[scala.Int]] = `in₄`.expectArray[scala.Option[scala.Int]]((() => if (`in₄`.expectNull()) null else scala.Some.apply[scala.Int](`in₄`.expectInt()))) - - (if (parsedArray.!=(null)) parsedArray.toList else null: scala.collection.immutable.List[scala.Option[scala.Int]]) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b"), `in₄`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _c = if (`in₄`.expectNull()) null else { - `in₄`.expectToken('{') - `in₄`.parseMap[java.lang.String, scala.Option[scala.Int]]((() => `in₄`.expectString()), (() => if (`in₄`.expectNull()) null else scala.Some.apply[scala.Int](`in₄`.expectInt())), scala.Predef.Map.empty[java.lang.String, scala.Option[scala.Int]], true) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c"), `in₄`) - case _ => - `in₄`.skipValue() - } - maybeFieldNum = `in₄`.expectObjectField(__co_blocke_scalajack_json_misc_AliasHolder2_fields) - } - if (required.&(6).==(0)) new co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountX][scala.Option[scala.Int]](_a, _b, _c) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a", "b", "c")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(6)))), `in₄`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountX]] { - def encodeValue(`in₅`: co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountX], `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₅`, `out₄`) - def decodeValue(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountX] = r0(`in₆`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountX]]) -} -Codec: { - val __co_blocke_scalajack_json_misc_AliasHolder2_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a", "b", "c")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: scala.collection.immutable.List[AliasSpec.this.CountX], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startArray() - in.foreach[scala.Unit](((i: scala.Option[scala.Int]) => i match { - case null => - out.burpNull() - case scala.None => - out.burpNull() - case scala.Some(v) => - val vv: scala.Int = v.asInstanceOf[scala.Int] - out.value(vv) - })) - out.endArray() - } - def w2(`in₂`: scala.collection.immutable.Map[scala.Predef.String, AliasSpec.this.CountX], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `in₂`.foreach[scala.Unit](((x$1: scala.Tuple2[java.lang.String, scala.Option[scala.Int]]) => x$1 match { - case scala.Tuple2(key, value) => - { - `out₂`.maybeComma() - `out₂`.valueEscaped(key) - `out₂`.colon() - value match { - case null => - `out₂`.burpNull() - case scala.None => - `out₂`.burpNull() - case scala.Some(v) => - val `vv₂`: scala.Int = `v₂`.asInstanceOf[scala.Int] - `out₂`.value(`vv₂`) - } - } - })) - `out₂`.endObject() - } - def w0(`in₃`: co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountX], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { - `out₃`.startObject() - `out₃`.label("a") - `in₃`.a match { - case null => - `out₃`.burpNull() - case scala.None => - `out₃`.burpNull() - case scala.Some(v) => - val `vv₃`: scala.Int = `v₃`.asInstanceOf[scala.Int] - `out₃`.value(`vv₃`) - } - `out₃`.label("b") - w1(`in₃`.b, `out₃`) - `out₃`.label("c") - w2(`in₃`.c, `out₃`) - `out₃`.endObject() - } - def r0(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountX] = { - var _a: scala.Option[scala.Int] = scala.None - var _b: scala.collection.immutable.List[AliasSpec.this.CountX] = null - var _c: scala.collection.immutable.Map[scala.Predef.String, AliasSpec.this.CountX] = null - var required: scala.Int = 7 - var maybeFieldNum: scala.Option[scala.Int] = `in₄`.expectFirstObjectField(__co_blocke_scalajack_json_misc_AliasHolder2_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountX]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₄`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₄`.expectInt()) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₄`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _b = { - val parsedArray: scala.collection.mutable.ListBuffer[scala.Option[scala.Int]] = `in₄`.expectArray[scala.Option[scala.Int]]((() => if (`in₄`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₄`.expectInt()))) - - (if (parsedArray.!=(null)) parsedArray.toList else null: scala.collection.immutable.List[scala.Option[scala.Int]]) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b"), `in₄`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _c = if (`in₄`.expectNull()) null else { - `in₄`.expectToken('{') - `in₄`.parseMap[java.lang.String, scala.Option[scala.Int]]((() => `in₄`.expectString()), (() => if (`in₄`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₄`.expectInt())), scala.Predef.Map.empty[java.lang.String, scala.Option[scala.Int]], true) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c"), `in₄`) - case _ => - `in₄`.skipValue() - } - maybeFieldNum = `in₄`.expectObjectField(__co_blocke_scalajack_json_misc_AliasHolder2_fields) - } - if (required.&(6).==(0)) new co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountX][scala.Option[scala.Int]](_a, _b, _c) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a", "b", "c")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(6)))), `in₄`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountX]] { - def encodeValue(`in₅`: co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountX], `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₅`, `out₄`) - def decodeValue(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountX] = r0(`in₆`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountX]]) -} -Codec: { - val __co_blocke_scalajack_json_misc_AliasHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a", "b", "c", "d")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: scala.collection.immutable.List[AliasSpec.this.CountY], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startArray() - in.foreach[scala.Unit](((i: java.lang.String) => out.valueEscaped(i))) - out.endArray() - } - def w2(`in₂`: scala.collection.immutable.Map[AliasSpec.this.CountY, scala.Predef.String], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `in₂`.foreach[scala.Unit](((x$1: scala.Tuple2[java.lang.String, java.lang.String]) => x$1 match { - case scala.Tuple2(key, value) => - { - `out₂`.maybeComma() - `out₂`.valueEscaped(key) - `out₂`.colon() - `out₂`.valueEscaped(value) - } - })) - `out₂`.endObject() - } - def w0(`in₃`: co.blocke.scalajack.json.misc.AliasHolder[AliasSpec.this.CountY], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { - `out₃`.startObject() - `out₃`.label("a") - `out₃`.valueEscaped(`in₃`.a) - `out₃`.label("b") - w1(`in₃`.b, `out₃`) - `out₃`.label("c") - w2(`in₃`.c, `out₃`) - `out₃`.label("d") - w2(`in₃`.d, `out₃`) - `out₃`.endObject() - } - def r0(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.AliasHolder[AliasSpec.this.CountY] = { - var _a: java.lang.String = "" - var _b: scala.collection.immutable.List[AliasSpec.this.CountY] = null - var _c: scala.collection.immutable.Map[AliasSpec.this.CountY, scala.Predef.String] = null - var _d: scala.collection.immutable.Map[scala.Predef.String, AliasSpec.this.CountY] = null - var required: scala.Int = 15 - var maybeFieldNum: scala.Option[scala.Int] = `in₄`.expectFirstObjectField(__co_blocke_scalajack_json_misc_AliasHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.AliasHolder[AliasSpec.this.CountY]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = `in₄`.expectString() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₄`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _b = { - val parsedArray: scala.collection.mutable.ListBuffer[java.lang.String] = `in₄`.expectArray[java.lang.String]((() => `in₄`.expectString())) - - (if (parsedArray.!=(null)) parsedArray.toList else null: scala.collection.immutable.List[java.lang.String]) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b"), `in₄`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _c = if (`in₄`.expectNull()) null else { - `in₄`.expectToken('{') - `in₄`.parseMap[java.lang.String, java.lang.String]((() => `in₄`.expectString()), (() => `in₄`.expectString()), scala.Predef.Map.empty[java.lang.String, java.lang.String], true) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c"), `in₄`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _d = if (`in₄`.expectNull()) null else { - `in₄`.expectToken('{') - `in₄`.parseMap[java.lang.String, java.lang.String]((() => `in₄`.expectString()), (() => `in₄`.expectString()), scala.Predef.Map.empty[java.lang.String, java.lang.String], true) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d"), `in₄`) - case _ => - `in₄`.skipValue() - } - maybeFieldNum = `in₄`.expectObjectField(__co_blocke_scalajack_json_misc_AliasHolder_fields) - } - if (required.&(15).==(0)) new co.blocke.scalajack.json.misc.AliasHolder[AliasSpec.this.CountY][java.lang.String](_a, _b, _c, _d) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a", "b", "c", "d")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₄`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.AliasHolder[AliasSpec.this.CountY]] { - def encodeValue(`in₅`: co.blocke.scalajack.json.misc.AliasHolder[AliasSpec.this.CountY], `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₅`, `out₄`) - def decodeValue(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.AliasHolder[AliasSpec.this.CountY] = r0(`in₆`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.AliasHolder[AliasSpec.this.CountY]]) -} -Codec: { - val __co_blocke_scalajack_json_misc_AliasHolder2_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a", "b", "c")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: scala.collection.immutable.List[AliasSpec.this.CountZ], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startArray() - in.foreach[scala.Unit](((i: scala.Option[scala.Predef.String]) => i match { - case null => - out.burpNull() - case scala.None => - () - case scala.Some(v) => - val vv: java.lang.String = v.asInstanceOf[java.lang.String] - out.valueEscaped(vv) - })) - out.endArray() - } - def w2(`in₂`: scala.collection.immutable.Map[scala.Predef.String, AliasSpec.this.CountZ], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startObject() - `in₂`.foreach[scala.Unit](((x$1: scala.Tuple2[java.lang.String, scala.Option[scala.Predef.String]]) => x$1 match { - case scala.Tuple2(key, value) => - value match { - case scala.None => - () - case scala.Some(v) => - { - `out₂`.maybeComma() - `out₂`.valueEscaped(key) - `out₂`.colon() - `out₂`.valueEscaped(`v₂`) - } - } - })) - `out₂`.endObject() - } - def w0(`in₃`: co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountZ], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { - `out₃`.startObject() - `in₃`.a match { - case scala.None => - () - case scala.Some(v) => - { - `out₃`.label("a") - `out₃`.valueEscaped(`v₃`) - } - } - `out₃`.label("b") - w1(`in₃`.b, `out₃`) - `out₃`.label("c") - w2(`in₃`.c, `out₃`) - `out₃`.endObject() - } - def r0(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountZ] = { - var _a: scala.Option[scala.Predef.String] = scala.None - var _b: scala.collection.immutable.List[AliasSpec.this.CountZ] = null - var _c: scala.collection.immutable.Map[scala.Predef.String, AliasSpec.this.CountZ] = null - var required: scala.Int = 7 - var maybeFieldNum: scala.Option[scala.Int] = `in₄`.expectFirstObjectField(__co_blocke_scalajack_json_misc_AliasHolder2_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountZ]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₄`.expectNull()) null else scala.Some.apply[java.lang.String](`in₄`.expectString()) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₄`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _b = { - val parsedArray: scala.collection.mutable.ListBuffer[scala.Option[scala.Predef.String]] = `in₄`.expectArray[scala.Option[scala.Predef.String]]((() => if (`in₄`.expectNull()) null else scala.Some.apply[java.lang.String](`in₄`.expectString()))) - - (if (parsedArray.!=(null)) parsedArray.toList else null: scala.collection.immutable.List[scala.Option[scala.Predef.String]]) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b"), `in₄`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _c = if (`in₄`.expectNull()) null else { - `in₄`.expectToken('{') - `in₄`.parseMap[java.lang.String, scala.Option[scala.Predef.String]]((() => `in₄`.expectString()), (() => if (`in₄`.expectNull()) null else scala.Some.apply[java.lang.String](`in₄`.expectString())), scala.Predef.Map.empty[java.lang.String, scala.Option[scala.Predef.String]], true) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c"), `in₄`) - case _ => - `in₄`.skipValue() - } - maybeFieldNum = `in₄`.expectObjectField(__co_blocke_scalajack_json_misc_AliasHolder2_fields) - } - if (required.&(6).==(0)) new co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountZ][scala.Option[scala.Predef.String]](_a, _b, _c) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a", "b", "c")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(6)))), `in₄`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountZ]] { - def encodeValue(`in₅`: co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountZ], `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₅`, `out₄`) - def decodeValue(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountZ] = r0(`in₆`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.AliasHolder2[AliasSpec.this.CountZ]]) -} -Codec: { - val __co_blocke_scalajack_json_misc_ComplexEither_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.misc.ComplexEither[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - in.a match { - case scala.None => - () - case scala.Some(v) => - { - if (v.==(null)) out.burpNull() else () - v match { - case scala.Left(v) => - { - out.label("a") - out.valueEscaped(`v₂`.asInstanceOf[java.lang.String]) - } - case scala.Right(v) => - `v₃`.asInstanceOf[scala.Option[scala.Int]] match { - case scala.None => - () - case scala.Some(v) => - { - out.label("a") - out.value(`v₄`) - } - } - } - } - } - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.ComplexEither[scala.Int] = { - var _a: scala.Option[scala.util.Either[scala.Predef.String, scala.Option[scala.Int]]] = scala.None - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_ComplexEither_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.ComplexEither[scala.Int]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₂`.expectNull()) null else scala.Some.apply[scala.util.Either[scala.Predef.String, scala.Option[scala.Int]]]({ - val mark: scala.Int = `in₂`.pos - if (`in₂`.expectNull()) null else scala.util.Try.apply[scala.Option[scala.Int]](if (`in₂`.expectNull()) null else scala.Some.apply[scala.Int](`in₂`.expectInt())) match { - case scala.util.Success(rval) => - scala.Right.apply[scala.Nothing, scala.Option[scala.Int]](rval) - case scala.util.Failure(f) => - `in₂`.revertToPos(mark) - scala.util.Try.apply[java.lang.String](`in₂`.expectString()) match { - case scala.util.Success(lval) => - scala.Left.apply[java.lang.String, scala.Nothing](lval) - case scala.util.Failure(_) => - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) - } - } - }) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_ComplexEither_fields) - } - if (required.&(0).==(0)) new co.blocke.scalajack.json.misc.ComplexEither[scala.Int][scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(0)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.ComplexEither[scala.Int]] { - def encodeValue(`in₃`: co.blocke.scalajack.json.misc.ComplexEither[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.ComplexEither[scala.Int] = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.ComplexEither[scala.Int]]) -} -Codec: { - val __co_blocke_scalajack_json_misc_ComplexEither_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.misc.ComplexEither[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - in.a match { - case scala.None => - () - case scala.Some(v) => - { - if (v.==(null)) out.burpNull() else () - v match { - case scala.Left(v) => - { - out.label("a") - out.valueEscaped(`v₂`.asInstanceOf[java.lang.String]) - } - case scala.Right(v) => - `v₃`.asInstanceOf[scala.Option[scala.Int]] match { - case scala.None => - () - case scala.Some(v) => - { - out.label("a") - out.value(`v₄`) - } - } - } - } - } - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.ComplexEither[scala.Int] = { - var _a: scala.Option[scala.util.Either[scala.Predef.String, scala.Option[scala.Int]]] = scala.None - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_ComplexEither_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.ComplexEither[scala.Int]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₂`.expectNull()) null else scala.Some.apply[scala.util.Either[scala.Predef.String, scala.Option[scala.Int]]]({ - val mark: scala.Int = `in₂`.pos - if (`in₂`.expectNull()) null else scala.util.Try.apply[scala.Option[scala.Int]](if (`in₂`.expectNull()) null else scala.Some.apply[scala.Int](`in₂`.expectInt())) match { - case scala.util.Success(rval) => - scala.Right.apply[scala.Nothing, scala.Option[scala.Int]](rval) - case scala.util.Failure(f) => - `in₂`.revertToPos(mark) - scala.util.Try.apply[java.lang.String](`in₂`.expectString()) match { - case scala.util.Success(lval) => - scala.Left.apply[java.lang.String, scala.Nothing](lval) - case scala.util.Failure(_) => - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) - } - } - }) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_ComplexEither_fields) - } - if (required.&(0).==(0)) new co.blocke.scalajack.json.misc.ComplexEither[scala.Int][scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(0)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.ComplexEither[scala.Int]] { - def encodeValue(`in₃`: co.blocke.scalajack.json.misc.ComplexEither[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.ComplexEither[scala.Int] = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.ComplexEither[scala.Int]]) -} -Codec: { - val __co_blocke_scalajack_json_misc_ComplexEither_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.misc.ComplexEither[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("a") - in.a match { - case null => - out.burpNull() - case scala.None => - out.burpNull() - case scala.Some(v) => - val vv: scala.util.Either[scala.Predef.String, scala.Option[scala.Int]] = v.asInstanceOf[scala.util.Either[scala.Predef.String, scala.Option[scala.Int]]] - if (vv.==(null)) out.burpNull() else vv match { - case scala.Left(v) => - out.valueEscaped(`v₂`.asInstanceOf[java.lang.String]) - case scala.Right(v) => - `v₃`.asInstanceOf[scala.Option[scala.Int]] match { - case null => - out.burpNull() - case scala.None => - out.burpNull() - case scala.Some(v) => - val `vv₂`: scala.Int = `v₄`.asInstanceOf[scala.Int] - out.value(`vv₂`) - } - } - } - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.ComplexEither[scala.Int] = { - var _a: scala.Option[scala.util.Either[scala.Predef.String, scala.Option[scala.Int]]] = scala.None - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_ComplexEither_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.ComplexEither[scala.Int]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₂`.expectNull()) scala.None else scala.Some.apply[scala.util.Either[scala.Predef.String, scala.Option[scala.Int]]]({ - val mark: scala.Int = `in₂`.pos - if (`in₂`.expectNull()) null else scala.util.Try.apply[scala.Option[scala.Int]](if (`in₂`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₂`.expectInt())) match { - case scala.util.Success(rval) => - scala.Right.apply[scala.Nothing, scala.Option[scala.Int]](rval) - case scala.util.Failure(f) => - `in₂`.revertToPos(mark) - scala.util.Try.apply[java.lang.String](`in₂`.expectString()) match { - case scala.util.Success(lval) => - scala.Left.apply[java.lang.String, scala.Nothing](lval) - case scala.util.Failure(_) => - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) - } - } - }) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_ComplexEither_fields) - } - if (required.&(0).==(0)) new co.blocke.scalajack.json.misc.ComplexEither[scala.Int][scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(0)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.ComplexEither[scala.Int]] { - def encodeValue(`in₃`: co.blocke.scalajack.json.misc.ComplexEither[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.ComplexEither[scala.Int] = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.ComplexEither[scala.Int]]) -} -Codec: { - val __co_blocke_scalajack_json_misc_ComplexEither_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.misc.ComplexEither[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - in.a match { - case scala.None => - () - case scala.Some(v) => - { - if (v.==(null)) out.burpNull() else () - v match { - case scala.Left(v) => - { - out.label("a") - out.valueEscaped(`v₂`.asInstanceOf[java.lang.String]) - } - case scala.Right(v) => - `v₃`.asInstanceOf[scala.Option[scala.Int]] match { - case scala.None => - () - case scala.Some(v) => - { - out.label("a") - out.value(`v₄`) - } - } - } - } - } - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.ComplexEither[scala.Int] = { - var _a: scala.Option[scala.util.Either[scala.Predef.String, scala.Option[scala.Int]]] = scala.None - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_ComplexEither_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.ComplexEither[scala.Int]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₂`.expectNull()) null else scala.Some.apply[scala.util.Either[scala.Predef.String, scala.Option[scala.Int]]]({ - val mark: scala.Int = `in₂`.pos - if (`in₂`.expectNull()) null else scala.util.Try.apply[scala.Option[scala.Int]](if (`in₂`.expectNull()) null else scala.Some.apply[scala.Int](`in₂`.expectInt())) match { - case scala.util.Success(rval) => - scala.Right.apply[scala.Nothing, scala.Option[scala.Int]](rval) - case scala.util.Failure(f) => - `in₂`.revertToPos(mark) - scala.util.Try.apply[java.lang.String](`in₂`.expectString()) match { - case scala.util.Success(lval) => - scala.Left.apply[java.lang.String, scala.Nothing](lval) - case scala.util.Failure(_) => - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) - } - } - }) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_ComplexEither_fields) - } - if (required.&(0).==(0)) new co.blocke.scalajack.json.misc.ComplexEither[scala.Int][scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(0)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.ComplexEither[scala.Int]] { - def encodeValue(`in₃`: co.blocke.scalajack.json.misc.ComplexEither[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.ComplexEither[scala.Int] = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.ComplexEither[scala.Int]]) -} -Codec: { - val __co_blocke_scalajack_json_misc_ComplexEither_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.misc.ComplexEither[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - in.a match { - case scala.None => - () - case scala.Some(v) => - { - if (v.==(null)) out.burpNull() else () - v match { - case scala.Left(v) => - { - out.label("a") - out.valueEscaped(`v₂`.asInstanceOf[java.lang.String]) - } - case scala.Right(v) => - `v₃`.asInstanceOf[scala.Option[scala.Int]] match { - case scala.None => - () - case scala.Some(v) => - { - out.label("a") - out.value(`v₄`) - } - } - } - } - } - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.ComplexEither[scala.Int] = { - var _a: scala.Option[scala.util.Either[scala.Predef.String, scala.Option[scala.Int]]] = scala.None - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_ComplexEither_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.ComplexEither[scala.Int]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₂`.expectNull()) null else scala.Some.apply[scala.util.Either[scala.Predef.String, scala.Option[scala.Int]]]({ - val mark: scala.Int = `in₂`.pos - if (`in₂`.expectNull()) null else scala.util.Try.apply[scala.Option[scala.Int]](if (`in₂`.expectNull()) null else scala.Some.apply[scala.Int](`in₂`.expectInt())) match { - case scala.util.Success(rval) => - scala.Right.apply[scala.Nothing, scala.Option[scala.Int]](rval) - case scala.util.Failure(f) => - `in₂`.revertToPos(mark) - scala.util.Try.apply[java.lang.String](`in₂`.expectString()) match { - case scala.util.Success(lval) => - scala.Left.apply[java.lang.String, scala.Nothing](lval) - case scala.util.Failure(_) => - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) - } - } - }) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_ComplexEither_fields) - } - if (required.&(0).==(0)) new co.blocke.scalajack.json.misc.ComplexEither[scala.Int][scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(0)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.ComplexEither[scala.Int]] { - def encodeValue(`in₃`: co.blocke.scalajack.json.misc.ComplexEither[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.ComplexEither[scala.Int] = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.ComplexEither[scala.Int]]) -} -Codec: { - val __co_blocke_scalajack_json_misc_ComplexEither_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.misc.ComplexEither[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - in.a match { - case scala.None => - () - case scala.Some(v) => - { - if (v.==(null)) out.burpNull() else () - v match { - case scala.Left(_) => - out.label("a") - out.burpNull() - case scala.Right(v) => - `v₂`.asInstanceOf[scala.Option[scala.Int]] match { - case scala.None => - () - case scala.Some(v) => - { - out.label("a") - out.value(`v₃`) - } - } - } - } - } - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.ComplexEither[scala.Int] = { - var _a: scala.Option[scala.util.Either[scala.Predef.String, scala.Option[scala.Int]]] = scala.None - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_ComplexEither_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.ComplexEither[scala.Int]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₂`.expectNull()) null else scala.Some.apply[scala.util.Either[scala.Predef.String, scala.Option[scala.Int]]]({ - val mark: scala.Int = `in₂`.pos - if (`in₂`.expectNull()) null else scala.util.Try.apply[scala.Option[scala.Int]](if (`in₂`.expectNull()) null else scala.Some.apply[scala.Int](`in₂`.expectInt())) match { - case scala.util.Success(rval) => - scala.Right.apply[scala.Nothing, scala.Option[scala.Int]](rval) - case scala.util.Failure(f) => - `in₂`.revertToPos(mark) - scala.util.Try.apply[java.lang.String](`in₂`.expectString()) match { - case scala.util.Success(lval) => - scala.Left.apply[java.lang.String, scala.Nothing](lval) - case scala.util.Failure(_) => - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) - } - } - }) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_ComplexEither_fields) - } - if (required.&(0).==(0)) new co.blocke.scalajack.json.misc.ComplexEither[scala.Int][scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(0)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.ComplexEither[scala.Int]] { - def encodeValue(`in₃`: co.blocke.scalajack.json.misc.ComplexEither[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.ComplexEither[scala.Int] = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.ComplexEither[scala.Int]]) -} -Codec: { - val __co_blocke_scalajack_json_misc_ComplexEither_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.misc.ComplexEither[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("a") - in.a match { - case null => - out.burpNull() - case scala.None => - out.burpNull() - case scala.Some(v) => - val vv: scala.util.Either[scala.Predef.String, scala.Option[scala.Int]] = v.asInstanceOf[scala.util.Either[scala.Predef.String, scala.Option[scala.Int]]] - if (vv.==(null)) out.burpNull() else vv match { - case scala.Left(v) => - out.burpNull() - case scala.Right(v) => - `v₂`.asInstanceOf[scala.Option[scala.Int]] match { - case null => - out.burpNull() - case scala.None => - out.burpNull() - case scala.Some(v) => - val `vv₂`: scala.Int = `v₃`.asInstanceOf[scala.Int] - out.value(`vv₂`) - } - } - } - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.ComplexEither[scala.Int] = { - var _a: scala.Option[scala.util.Either[scala.Predef.String, scala.Option[scala.Int]]] = scala.None - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_ComplexEither_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.ComplexEither[scala.Int]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = if (`in₂`.expectNull()) scala.None else scala.Some.apply[scala.util.Either[scala.Predef.String, scala.Option[scala.Int]]]({ - val mark: scala.Int = `in₂`.pos - if (`in₂`.expectNull()) null else scala.util.Try.apply[scala.Option[scala.Int]](if (`in₂`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₂`.expectInt())) match { - case scala.util.Success(rval) => - scala.Right.apply[scala.Nothing, scala.Option[scala.Int]](rval) - case scala.util.Failure(f) => - `in₂`.revertToPos(mark) - scala.util.Try.apply[java.lang.String](`in₂`.expectString()) match { - case scala.util.Success(lval) => - scala.Left.apply[java.lang.String, scala.Nothing](lval) - case scala.util.Failure(_) => - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) - } - } - }) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_ComplexEither_fields) - } - if (required.&(0).==(0)) new co.blocke.scalajack.json.misc.ComplexEither[scala.Int][scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(0)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.ComplexEither[scala.Int]] { - def encodeValue(`in₃`: co.blocke.scalajack.json.misc.ComplexEither[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.ComplexEither[scala.Int] = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.ComplexEither[scala.Int]]) -} -Codec: { - val __co_blocke_scalajack_json_misc_EitherHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a", "b")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.misc.EitherHolder[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - if (in.a.==(null)) out.burpNull() else () - in.a match { - case scala.Left(v) => - { - out.label("a") - out.value(v.asInstanceOf[scala.Int]) - } - case scala.Right(v) => - { - out.label("a") - out.valueEscaped(`v₂`.asInstanceOf[java.lang.String]) - } - } - if (in.b.==(null)) out.burpNull() else () - in.b match { - case scala.Left(v) => - { - out.label("b") - out.valueEscaped(`v₃`.asInstanceOf[java.lang.String]) - } - case scala.Right(v) => - { - out.label("b") - out.value(`v₄`.asInstanceOf[scala.Int]) - } - } - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.EitherHolder[scala.Int] = { - var _a: scala.util.Either[scala.Int, scala.Predef.String] = null - var _b: scala.util.Either[scala.Predef.String, scala.Int] = null - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_EitherHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.EitherHolder[scala.Int]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = { - val mark: scala.Int = `in₂`.pos - if (`in₂`.expectNull()) null else scala.util.Try.apply[java.lang.String](`in₂`.expectString()) match { - case scala.util.Success(rval) => - scala.Right.apply[scala.Nothing, java.lang.String](rval) - case scala.util.Failure(f) => - `in₂`.revertToPos(mark) - scala.util.Try.apply[scala.Int](`in₂`.expectInt()) match { - case scala.util.Success(lval) => - scala.Left.apply[scala.Int, scala.Nothing](lval) - case scala.util.Failure(_) => - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) - } - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _b = { - val `mark₂`: scala.Int = `in₂`.pos - if (`in₂`.expectNull()) null else scala.util.Try.apply[scala.Int](`in₂`.expectInt()) match { - case scala.util.Success(rval) => - scala.Right.apply[scala.Nothing, scala.Int](`rval₂`) - case scala.util.Failure(f) => - `in₂`.revertToPos(`mark₂`) - scala.util.Try.apply[java.lang.String](`in₂`.expectString()) match { - case scala.util.Success(lval) => - scala.Left.apply[java.lang.String, scala.Nothing](`lval₂`) - case scala.util.Failure(_) => - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) - } - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_EitherHolder_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.misc.EitherHolder[scala.Int][scala.Int](_a, _b) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a", "b")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.EitherHolder[scala.Int]] { - def encodeValue(`in₃`: co.blocke.scalajack.json.misc.EitherHolder[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.EitherHolder[scala.Int] = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.EitherHolder[scala.Int]]) -} -Codec: { - val __co_blocke_scalajack_json_misc_EitherHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a", "b")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.misc.EitherHolder[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - if (in.a.==(null)) out.burpNull() else () - in.a match { - case scala.Left(_) => - out.label("a") - out.burpNull() - case scala.Right(v) => - { - out.label("a") - out.valueEscaped(v.asInstanceOf[java.lang.String]) - } - } - if (in.b.==(null)) out.burpNull() else () - in.b match { - case scala.Left(_) => - out.label("b") - out.burpNull() - case scala.Right(v) => - { - out.label("b") - out.value(`v₂`.asInstanceOf[scala.Int]) - } - } - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.EitherHolder[scala.Int] = { - var _a: scala.util.Either[scala.Int, scala.Predef.String] = null - var _b: scala.util.Either[scala.Predef.String, scala.Int] = null - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_EitherHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.EitherHolder[scala.Int]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = { - val mark: scala.Int = `in₂`.pos - if (`in₂`.expectNull()) null else scala.util.Try.apply[java.lang.String](`in₂`.expectString()) match { - case scala.util.Success(rval) => - scala.Right.apply[scala.Nothing, java.lang.String](rval) - case scala.util.Failure(f) => - `in₂`.revertToPos(mark) - scala.util.Try.apply[scala.Int](`in₂`.expectInt()) match { - case scala.util.Success(lval) => - scala.Left.apply[scala.Int, scala.Nothing](lval) - case scala.util.Failure(_) => - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) - } - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _b = { - val `mark₂`: scala.Int = `in₂`.pos - if (`in₂`.expectNull()) null else scala.util.Try.apply[scala.Int](`in₂`.expectInt()) match { - case scala.util.Success(rval) => - scala.Right.apply[scala.Nothing, scala.Int](`rval₂`) - case scala.util.Failure(f) => - `in₂`.revertToPos(`mark₂`) - scala.util.Try.apply[java.lang.String](`in₂`.expectString()) match { - case scala.util.Success(lval) => - scala.Left.apply[java.lang.String, scala.Nothing](`lval₂`) - case scala.util.Failure(_) => - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) - } - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_EitherHolder_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.misc.EitherHolder[scala.Int][scala.Int](_a, _b) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a", "b")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.EitherHolder[scala.Int]] { - def encodeValue(`in₃`: co.blocke.scalajack.json.misc.EitherHolder[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.EitherHolder[scala.Int] = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.EitherHolder[scala.Int]]) -} -Codec: { - val __co_blocke_scalajack_json_misc_EitherHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a", "b")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.misc.EitherHolder[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - if (in.a.==(null)) out.burpNull() else () - in.a match { - case scala.Left(err) => - out.label("a") - out.value("Left Error: ".+(err.toString())) - case scala.Right(v) => - { - out.label("a") - out.valueEscaped(v.asInstanceOf[java.lang.String]) - } - } - if (in.b.==(null)) out.burpNull() else () - in.b match { - case scala.Left(err) => - out.label("b") - out.value("Left Error: ".+(`err₂`.toString())) - case scala.Right(v) => - { - out.label("b") - out.value(`v₂`.asInstanceOf[scala.Int]) - } - } - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.EitherHolder[scala.Int] = { - var _a: scala.util.Either[scala.Int, scala.Predef.String] = null - var _b: scala.util.Either[scala.Predef.String, scala.Int] = null - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_EitherHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.EitherHolder[scala.Int]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = { - val mark: scala.Int = `in₂`.pos - if (`in₂`.expectNull()) null else scala.util.Try.apply[java.lang.String](`in₂`.expectString()) match { - case scala.util.Success(rval) => - scala.Right.apply[scala.Nothing, java.lang.String](rval) - case scala.util.Failure(f) => - `in₂`.revertToPos(mark) - scala.util.Try.apply[scala.Int](`in₂`.expectInt()) match { - case scala.util.Success(lval) => - scala.Left.apply[scala.Int, scala.Nothing](lval) - case scala.util.Failure(_) => - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) - } - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _b = { - val `mark₂`: scala.Int = `in₂`.pos - if (`in₂`.expectNull()) null else scala.util.Try.apply[scala.Int](`in₂`.expectInt()) match { - case scala.util.Success(rval) => - scala.Right.apply[scala.Nothing, scala.Int](`rval₂`) - case scala.util.Failure(f) => - `in₂`.revertToPos(`mark₂`) - scala.util.Try.apply[java.lang.String](`in₂`.expectString()) match { - case scala.util.Success(lval) => - scala.Left.apply[java.lang.String, scala.Nothing](`lval₂`) - case scala.util.Failure(_) => - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) - } - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_EitherHolder_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.misc.EitherHolder[scala.Int][scala.Int](_a, _b) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a", "b")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.EitherHolder[scala.Int]] { - def encodeValue(`in₃`: co.blocke.scalajack.json.misc.EitherHolder[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.EitherHolder[scala.Int] = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.EitherHolder[scala.Int]]) -} -Codec: { - val __co_blocke_scalajack_json_misc_EitherHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a", "b")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.misc.EitherHolder[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - if (in.a.==(null)) out.burpNull() else () - in.a match { - case scala.Left(err) => - throw new co.blocke.scalajack.json.JsonEitherLeftError("Left Error: ".+(err.toString())) - case scala.Right(v) => - { - out.label("a") - out.valueEscaped(v.asInstanceOf[java.lang.String]) - } - } - if (in.b.==(null)) out.burpNull() else () - in.b match { - case scala.Left(err) => - throw new co.blocke.scalajack.json.JsonEitherLeftError("Left Error: ".+(`err₂`.toString())) - case scala.Right(v) => - { - out.label("b") - out.value(`v₂`.asInstanceOf[scala.Int]) - } - } - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.EitherHolder[scala.Int] = { - var _a: scala.util.Either[scala.Int, scala.Predef.String] = null - var _b: scala.util.Either[scala.Predef.String, scala.Int] = null - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_EitherHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.EitherHolder[scala.Int]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = { - val mark: scala.Int = `in₂`.pos - if (`in₂`.expectNull()) null else scala.util.Try.apply[java.lang.String](`in₂`.expectString()) match { - case scala.util.Success(rval) => - scala.Right.apply[scala.Nothing, java.lang.String](rval) - case scala.util.Failure(f) => - `in₂`.revertToPos(mark) - scala.util.Try.apply[scala.Int](`in₂`.expectInt()) match { - case scala.util.Success(lval) => - scala.Left.apply[scala.Int, scala.Nothing](lval) - case scala.util.Failure(_) => - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) - } - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _b = { - val `mark₂`: scala.Int = `in₂`.pos - if (`in₂`.expectNull()) null else scala.util.Try.apply[scala.Int](`in₂`.expectInt()) match { - case scala.util.Success(rval) => - scala.Right.apply[scala.Nothing, scala.Int](`rval₂`) - case scala.util.Failure(f) => - `in₂`.revertToPos(`mark₂`) - scala.util.Try.apply[java.lang.String](`in₂`.expectString()) match { - case scala.util.Success(lval) => - scala.Left.apply[java.lang.String, scala.Nothing](`lval₂`) - case scala.util.Failure(_) => - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) - } - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_EitherHolder_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.misc.EitherHolder[scala.Int][scala.Int](_a, _b) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a", "b")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.EitherHolder[scala.Int]] { - def encodeValue(`in₃`: co.blocke.scalajack.json.misc.EitherHolder[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.EitherHolder[scala.Int] = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.EitherHolder[scala.Int]]) -} -Codec: { - val __co_blocke_scalajack_json_misc_LRUnionHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a", "b")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: scala.collection.immutable.Seq[scala.Option[scala.Int] | scala.Predef.String], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startArray() - in.foreach[scala.Unit](((i: scala.Option[scala.Int] | java.lang.String) => { - out.mark() - scala.util.Try.apply[scala.Unit](out.valueEscaped(i.asInstanceOf[java.lang.String])) match { - case scala.util.Success(_) => - () - case scala.util.Failure(_) => - out.revert() - i.asInstanceOf[scala.Option[scala.Int]] match { - case null => - out.burpNull() - case scala.None => - () - case scala.Some(v) => - val vv: scala.Int = v.asInstanceOf[scala.Int] - out.value(vv) - } - } - })) - out.endArray() - } - def w2(`in₂`: scala.Tuple2[scala.Option[scala.Int] | scala.Predef.String, scala.Option[scala.Int] | scala.Predef.String], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startArray() - `out₂`.mark() - scala.util.Try.apply[scala.Unit](`out₂`.valueEscaped(`in₂`._1.asInstanceOf[java.lang.String])) match { - case scala.util.Success(_) => - () - case scala.util.Failure(_) => - `out₂`.revert() - `in₂`._1.asInstanceOf[scala.Option[scala.Int]] match { - case null => - `out₂`.burpNull() - case scala.None => - `out₂`.burpNull() - case scala.Some(v) => - val `vv₂`: scala.Int = `v₂`.asInstanceOf[scala.Int] - `out₂`.value(`vv₂`) - } - } - `out₂`.mark() - scala.util.Try.apply[scala.Unit](`out₂`.valueEscaped(`in₂`._2.asInstanceOf[java.lang.String])) match { - case scala.util.Success(_) => - () - case scala.util.Failure(_) => - `out₂`.revert() - `in₂`._2.asInstanceOf[scala.Option[scala.Int]] match { - case null => - `out₂`.burpNull() - case scala.None => - `out₂`.burpNull() - case scala.Some(v) => - val `vv₃`: scala.Int = `v₃`.asInstanceOf[scala.Int] - `out₂`.value(`vv₃`) - } - } - `out₂`.endArray() - } - def w0(`in₃`: co.blocke.scalajack.json.misc.LRUnionHolder[scala.Option[scala.Int], scala.Predef.String], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { - `out₃`.startObject() - `out₃`.label("a") - w1(`in₃`.a, `out₃`) - `out₃`.label("b") - w2(`in₃`.b, `out₃`) - `out₃`.endObject() - } - def r0(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.LRUnionHolder[scala.Option[scala.Int], scala.Predef.String] = { - var _a: scala.collection.immutable.Seq[scala.Option[scala.Int] | scala.Predef.String] = null - var _b: scala.Tuple2[scala.Option[scala.Int] | scala.Predef.String, scala.Option[scala.Int] | scala.Predef.String] = null - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₄`.expectFirstObjectField(__co_blocke_scalajack_json_misc_LRUnionHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.LRUnionHolder[scala.Option[scala.Int], scala.Predef.String]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = { - val parsedArray: scala.collection.mutable.ListBuffer[scala.Option[scala.Int] | java.lang.String] = `in₄`.expectArray[scala.Option[scala.Int] | java.lang.String]((() => { - val mark: scala.Int = `in₄`.pos - scala.util.Try.apply[scala.Option[scala.Int]](if (`in₄`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₄`.expectInt())) match { - case scala.util.Success(lval) => - (lval: scala.Option[scala.Int]) - case scala.util.Failure(f) => - `in₄`.revertToPos(mark) - scala.util.Try.apply[java.lang.String](`in₄`.expectString()) match { - case scala.util.Success(rval) => - (rval: java.lang.String) - case scala.util.Failure(_) => - `in₄`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₄`) - } - } - })) - - (if (parsedArray.!=(null)) parsedArray.toSeq else null: scala.collection.immutable.Seq[scala.Option[scala.Int] | java.lang.String]) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₄`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _b = if (`in₄`.expectNull()) null else { - `in₄`.expectToken('[') - val tv: scala.Tuple2[scala.Option[scala.Int] | scala.Predef.String, scala.Option[scala.Int] | scala.Predef.String] = new scala.Tuple2[scala.Option[scala.Int] | scala.Predef.String, scala.Option[scala.Int] | scala.Predef.String][scala.Option[scala.Int] | scala.Predef.String, scala.Option[scala.Int] | scala.Predef.String]({ - val `mark₂`: scala.Int = `in₄`.pos - scala.util.Try.apply[scala.Option[scala.Int]](if (`in₄`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₄`.expectInt())) match { - case scala.util.Success(lval) => - (`lval₂`: scala.Option[scala.Int]) - case scala.util.Failure(f) => - `in₄`.revertToPos(`mark₂`) - scala.util.Try.apply[java.lang.String](`in₄`.expectString()) match { - case scala.util.Success(rval) => - (`rval₂`: java.lang.String) - case scala.util.Failure(_) => - `in₄`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₄`) - } - } - }, { - `in₄`.expectToken(',') - val `mark₃`: scala.Int = `in₄`.pos - scala.util.Try.apply[scala.Option[scala.Int]](if (`in₄`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₄`.expectInt())) match { - case scala.util.Success(lval) => - (`lval₃`: scala.Option[scala.Int]) - case scala.util.Failure(f) => - `in₄`.revertToPos(`mark₃`) - scala.util.Try.apply[java.lang.String](`in₄`.expectString()) match { - case scala.util.Success(rval) => - (`rval₃`: java.lang.String) - case scala.util.Failure(_) => - `in₄`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₄`) - } - } - }) - `in₄`.expectToken(']') - - (tv: scala.Tuple2[scala.Option[scala.Int] | scala.Predef.String, scala.Option[scala.Int] | scala.Predef.String]) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b"), `in₄`) - case _ => - `in₄`.skipValue() - } - maybeFieldNum = `in₄`.expectObjectField(__co_blocke_scalajack_json_misc_LRUnionHolder_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.misc.LRUnionHolder[scala.Option[scala.Int], scala.Predef.String][scala.Option[scala.Int], java.lang.String](_a, _b) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a", "b")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₄`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.LRUnionHolder[scala.Option[scala.Int], scala.Predef.String]] { - def encodeValue(`in₅`: co.blocke.scalajack.json.misc.LRUnionHolder[scala.Option[scala.Int], scala.Predef.String], `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₅`, `out₄`) - def decodeValue(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.LRUnionHolder[scala.Option[scala.Int], scala.Predef.String] = r0(`in₆`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.LRUnionHolder[scala.Option[scala.Int], scala.Predef.String]]) -} -Codec: { - val __co_blocke_scalajack_json_misc_LRUnionHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a", "b")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: scala.collection.immutable.Seq[scala.Option[scala.Int] | scala.Predef.String], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startArray() - in.foreach[scala.Unit](((i: scala.Option[scala.Int] | java.lang.String) => { - out.mark() - scala.util.Try.apply[scala.Unit](out.valueEscaped(i.asInstanceOf[java.lang.String])) match { - case scala.util.Success(_) => - () - case scala.util.Failure(_) => - out.revert() - i.asInstanceOf[scala.Option[scala.Int]] match { - case null => - out.burpNull() - case scala.None => - () - case scala.Some(v) => - val vv: scala.Int = v.asInstanceOf[scala.Int] - out.value(vv) - } - } - })) - out.endArray() - } - def w2(`in₂`: scala.Tuple2[scala.Option[scala.Int] | scala.Predef.String, scala.Option[scala.Int] | scala.Predef.String], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startArray() - `out₂`.mark() - scala.util.Try.apply[scala.Unit](`out₂`.valueEscaped(`in₂`._1.asInstanceOf[java.lang.String])) match { - case scala.util.Success(_) => - () - case scala.util.Failure(_) => - `out₂`.revert() - `in₂`._1.asInstanceOf[scala.Option[scala.Int]] match { - case null => - `out₂`.burpNull() - case scala.None => - `out₂`.burpNull() - case scala.Some(v) => - val `vv₂`: scala.Int = `v₂`.asInstanceOf[scala.Int] - `out₂`.value(`vv₂`) - } - } - `out₂`.mark() - scala.util.Try.apply[scala.Unit](`out₂`.valueEscaped(`in₂`._2.asInstanceOf[java.lang.String])) match { - case scala.util.Success(_) => - () - case scala.util.Failure(_) => - `out₂`.revert() - `in₂`._2.asInstanceOf[scala.Option[scala.Int]] match { - case null => - `out₂`.burpNull() - case scala.None => - `out₂`.burpNull() - case scala.Some(v) => - val `vv₃`: scala.Int = `v₃`.asInstanceOf[scala.Int] - `out₂`.value(`vv₃`) - } - } - `out₂`.endArray() - } - def w0(`in₃`: co.blocke.scalajack.json.misc.LRUnionHolder[scala.Option[scala.Int], scala.Predef.String], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { - `out₃`.startObject() - `out₃`.label("a") - w1(`in₃`.a, `out₃`) - `out₃`.label("b") - w2(`in₃`.b, `out₃`) - `out₃`.endObject() - } - def r0(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.LRUnionHolder[scala.Option[scala.Int], scala.Predef.String] = { - var _a: scala.collection.immutable.Seq[scala.Option[scala.Int] | scala.Predef.String] = null - var _b: scala.Tuple2[scala.Option[scala.Int] | scala.Predef.String, scala.Option[scala.Int] | scala.Predef.String] = null - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₄`.expectFirstObjectField(__co_blocke_scalajack_json_misc_LRUnionHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.LRUnionHolder[scala.Option[scala.Int], scala.Predef.String]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = { - val parsedArray: scala.collection.mutable.ListBuffer[scala.Option[scala.Int] | java.lang.String] = `in₄`.expectArray[scala.Option[scala.Int] | java.lang.String]((() => { - val mark: scala.Int = `in₄`.pos - scala.util.Try.apply[scala.Option[scala.Int]](if (`in₄`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₄`.expectInt())) match { - case scala.util.Success(lval) => - (lval: scala.Option[scala.Int]) - case scala.util.Failure(f) => - `in₄`.revertToPos(mark) - scala.util.Try.apply[java.lang.String](`in₄`.expectString()) match { - case scala.util.Success(rval) => - (rval: java.lang.String) - case scala.util.Failure(_) => - `in₄`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₄`) - } - } - })) - - (if (parsedArray.!=(null)) parsedArray.toSeq else null: scala.collection.immutable.Seq[scala.Option[scala.Int] | java.lang.String]) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₄`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _b = if (`in₄`.expectNull()) null else { - `in₄`.expectToken('[') - val tv: scala.Tuple2[scala.Option[scala.Int] | scala.Predef.String, scala.Option[scala.Int] | scala.Predef.String] = new scala.Tuple2[scala.Option[scala.Int] | scala.Predef.String, scala.Option[scala.Int] | scala.Predef.String][scala.Option[scala.Int] | scala.Predef.String, scala.Option[scala.Int] | scala.Predef.String]({ - val `mark₂`: scala.Int = `in₄`.pos - scala.util.Try.apply[scala.Option[scala.Int]](if (`in₄`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₄`.expectInt())) match { - case scala.util.Success(lval) => - (`lval₂`: scala.Option[scala.Int]) - case scala.util.Failure(f) => - `in₄`.revertToPos(`mark₂`) - scala.util.Try.apply[java.lang.String](`in₄`.expectString()) match { - case scala.util.Success(rval) => - (`rval₂`: java.lang.String) - case scala.util.Failure(_) => - `in₄`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₄`) - } - } - }, { - `in₄`.expectToken(',') - val `mark₃`: scala.Int = `in₄`.pos - scala.util.Try.apply[scala.Option[scala.Int]](if (`in₄`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₄`.expectInt())) match { - case scala.util.Success(lval) => - (`lval₃`: scala.Option[scala.Int]) - case scala.util.Failure(f) => - `in₄`.revertToPos(`mark₃`) - scala.util.Try.apply[java.lang.String](`in₄`.expectString()) match { - case scala.util.Success(rval) => - (`rval₃`: java.lang.String) - case scala.util.Failure(_) => - `in₄`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₄`) - } - } - }) - `in₄`.expectToken(']') - - (tv: scala.Tuple2[scala.Option[scala.Int] | scala.Predef.String, scala.Option[scala.Int] | scala.Predef.String]) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b"), `in₄`) - case _ => - `in₄`.skipValue() - } - maybeFieldNum = `in₄`.expectObjectField(__co_blocke_scalajack_json_misc_LRUnionHolder_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.misc.LRUnionHolder[scala.Option[scala.Int], scala.Predef.String][scala.Option[scala.Int], java.lang.String](_a, _b) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a", "b")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₄`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.LRUnionHolder[scala.Option[scala.Int], scala.Predef.String]] { - def encodeValue(`in₅`: co.blocke.scalajack.json.misc.LRUnionHolder[scala.Option[scala.Int], scala.Predef.String], `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₅`, `out₄`) - def decodeValue(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.LRUnionHolder[scala.Option[scala.Int], scala.Predef.String] = r0(`in₆`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.LRUnionHolder[scala.Option[scala.Int], scala.Predef.String]]) -} -Codec: { - val __co_blocke_scalajack_json_misc_LRUnionHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a", "b")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: scala.collection.immutable.Seq[scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startArray() - in.foreach[scala.Unit](((i: scala.util.Try[scala.Option[scala.Int]] | java.lang.String) => { - out.mark() - scala.util.Try.apply[scala.Unit](out.valueEscaped(i.asInstanceOf[java.lang.String])) match { - case scala.util.Success(_) => - () - case scala.util.Failure(_) => - out.revert() - if (i.asInstanceOf[scala.util.Try[scala.Option[scala.Int]]].==(null)) out.burpNull() else i.asInstanceOf[scala.util.Try[scala.Option[scala.Int]]] match { - case scala.util.Success(v) => - v match { - case null => - out.burpNull() - case scala.None => - () - case scala.Some(v) => - val vv: scala.Int = `v₂`.asInstanceOf[scala.Int] - out.value(vv) - } - case scala.util.Failure(v) => - out.burpNull() - } - } - })) - out.endArray() - } - def w2(`in₂`: scala.Tuple2[scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String, scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startArray() - `out₂`.mark() - scala.util.Try.apply[scala.Unit](`out₂`.valueEscaped(`in₂`._1.asInstanceOf[java.lang.String])) match { - case scala.util.Success(_) => - () - case scala.util.Failure(_) => - `out₂`.revert() - if (`in₂`._1.asInstanceOf[scala.util.Try[scala.Option[scala.Int]]].==(null)) `out₂`.burpNull() else `in₂`._1.asInstanceOf[scala.util.Try[scala.Option[scala.Int]]] match { - case scala.util.Success(v) => - `v₃` match { - case null => - `out₂`.burpNull() - case scala.None => - `out₂`.burpNull() - case scala.Some(v) => - val `vv₂`: scala.Int = `v₄`.asInstanceOf[scala.Int] - `out₂`.value(`vv₂`) - } - case scala.util.Failure(v) => - `out₂`.burpNull() - } - } - `out₂`.mark() - scala.util.Try.apply[scala.Unit](`out₂`.valueEscaped(`in₂`._2.asInstanceOf[java.lang.String])) match { - case scala.util.Success(_) => - () - case scala.util.Failure(_) => - `out₂`.revert() - if (`in₂`._2.asInstanceOf[scala.util.Try[scala.Option[scala.Int]]].==(null)) `out₂`.burpNull() else `in₂`._2.asInstanceOf[scala.util.Try[scala.Option[scala.Int]]] match { - case scala.util.Success(v) => - `v₅` match { - case null => - `out₂`.burpNull() - case scala.None => - `out₂`.burpNull() - case scala.Some(v) => - val `vv₃`: scala.Int = `v₆`.asInstanceOf[scala.Int] - `out₂`.value(`vv₃`) - } - case scala.util.Failure(v) => - `out₂`.burpNull() - } - } - `out₂`.endArray() - } - def w0(`in₃`: co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { - `out₃`.startObject() - `out₃`.label("a") - w1(`in₃`.a, `out₃`) - `out₃`.label("b") - w2(`in₃`.b, `out₃`) - `out₃`.endObject() - } - def r0(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String] = { - var _a: scala.collection.immutable.Seq[scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String] = null - var _b: scala.Tuple2[scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String, scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String] = null - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₄`.expectFirstObjectField(__co_blocke_scalajack_json_misc_LRUnionHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = { - val parsedArray: scala.collection.mutable.ListBuffer[scala.util.Try[scala.Option[scala.Int]] | java.lang.String] = `in₄`.expectArray[scala.util.Try[scala.Option[scala.Int]] | java.lang.String]((() => { - val mark: scala.Int = `in₄`.pos - scala.util.Try.apply[scala.util.Try[scala.Option[scala.Int]]]({ - val `mark₂`: scala.Int = `in₄`.pos - try if (`in₄`.expectNull()) null else scala.util.Success.apply[scala.Option[scala.Int]](if (`in₄`.expectNull()) null else scala.Some.apply[scala.Int](`in₄`.expectInt())) catch { - case t: java.lang.Throwable => - `in₄`.revertToPos(`mark₂`) - throw co.blocke.scalajack.json.JsonParseError.apply("Unsuccessful attempt to read Try type with failure: ".+(t.getMessage()), `in₄`) - } - }) match { - case scala.util.Success(lval) => - (lval: scala.util.Try[scala.Option[scala.Int]]) - case scala.util.Failure(f) => - `in₄`.revertToPos(mark) - scala.util.Try.apply[java.lang.String](`in₄`.expectString()) match { - case scala.util.Success(rval) => - (rval: java.lang.String) - case scala.util.Failure(_) => - `in₄`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₄`) - } - } - })) - - (if (parsedArray.!=(null)) parsedArray.toSeq else null: scala.collection.immutable.Seq[scala.util.Try[scala.Option[scala.Int]] | java.lang.String]) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₄`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _b = if (`in₄`.expectNull()) null else { - `in₄`.expectToken('[') - val tv: scala.Tuple2[scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String, scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String] = new scala.Tuple2[scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String, scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String][scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String, scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String]({ - val `mark₃`: scala.Int = `in₄`.pos - scala.util.Try.apply[scala.util.Try[scala.Option[scala.Int]]]({ - val `mark₄`: scala.Int = `in₄`.pos - try if (`in₄`.expectNull()) null else scala.util.Success.apply[scala.Option[scala.Int]](if (`in₄`.expectNull()) null else scala.Some.apply[scala.Int](`in₄`.expectInt())) catch { - case t: java.lang.Throwable => - `in₄`.revertToPos(`mark₄`) - throw co.blocke.scalajack.json.JsonParseError.apply("Unsuccessful attempt to read Try type with failure: ".+(`t₂`.getMessage()), `in₄`) - } - }) match { - case scala.util.Success(lval) => - (`lval₂`: scala.util.Try[scala.Option[scala.Int]]) - case scala.util.Failure(f) => - `in₄`.revertToPos(`mark₃`) - scala.util.Try.apply[java.lang.String](`in₄`.expectString()) match { - case scala.util.Success(rval) => - (`rval₂`: java.lang.String) - case scala.util.Failure(_) => - `in₄`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₄`) - } - } - }, { - `in₄`.expectToken(',') - val `mark₅`: scala.Int = `in₄`.pos - scala.util.Try.apply[scala.util.Try[scala.Option[scala.Int]]]({ - val `mark₆`: scala.Int = `in₄`.pos - try if (`in₄`.expectNull()) null else scala.util.Success.apply[scala.Option[scala.Int]](if (`in₄`.expectNull()) null else scala.Some.apply[scala.Int](`in₄`.expectInt())) catch { - case t: java.lang.Throwable => - `in₄`.revertToPos(`mark₆`) - throw co.blocke.scalajack.json.JsonParseError.apply("Unsuccessful attempt to read Try type with failure: ".+(`t₃`.getMessage()), `in₄`) - } - }) match { - case scala.util.Success(lval) => - (`lval₃`: scala.util.Try[scala.Option[scala.Int]]) - case scala.util.Failure(f) => - `in₄`.revertToPos(`mark₅`) - scala.util.Try.apply[java.lang.String](`in₄`.expectString()) match { - case scala.util.Success(rval) => - (`rval₃`: java.lang.String) - case scala.util.Failure(_) => - `in₄`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₄`) - } - } - }) - `in₄`.expectToken(']') - - (tv: scala.Tuple2[scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String, scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String]) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b"), `in₄`) - case _ => - `in₄`.skipValue() - } - maybeFieldNum = `in₄`.expectObjectField(__co_blocke_scalajack_json_misc_LRUnionHolder_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String][scala.util.Try[scala.Option[scala.Int]], java.lang.String](_a, _b) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a", "b")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₄`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String]] { - def encodeValue(`in₅`: co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String], `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₅`, `out₄`) - def decodeValue(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String] = r0(`in₆`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String]]) -} -Codec: { - val __co_blocke_scalajack_json_misc_LRUnionHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a", "b")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: scala.collection.immutable.Seq[scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startArray() - in.foreach[scala.Unit](((i: scala.util.Try[scala.Option[scala.Int]] | java.lang.String) => { - out.mark() - scala.util.Try.apply[scala.Unit](out.valueEscaped(i.asInstanceOf[java.lang.String])) match { - case scala.util.Success(_) => - () - case scala.util.Failure(_) => - out.revert() - if (i.asInstanceOf[scala.util.Try[scala.Option[scala.Int]]].==(null)) out.burpNull() else i.asInstanceOf[scala.util.Try[scala.Option[scala.Int]]] match { - case scala.util.Success(v) => - v match { - case null => - out.burpNull() - case scala.None => - () - case scala.Some(v) => - val vv: scala.Int = `v₂`.asInstanceOf[scala.Int] - out.value(vv) - } - case scala.util.Failure(v) => - out.burpNull() - } - } - })) - out.endArray() - } - def w2(`in₂`: scala.Tuple2[scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String, scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startArray() - `out₂`.mark() - scala.util.Try.apply[scala.Unit](`out₂`.valueEscaped(`in₂`._1.asInstanceOf[java.lang.String])) match { - case scala.util.Success(_) => - () - case scala.util.Failure(_) => - `out₂`.revert() - if (`in₂`._1.asInstanceOf[scala.util.Try[scala.Option[scala.Int]]].==(null)) `out₂`.burpNull() else `in₂`._1.asInstanceOf[scala.util.Try[scala.Option[scala.Int]]] match { - case scala.util.Success(v) => - `v₃` match { - case null => - `out₂`.burpNull() - case scala.None => - `out₂`.burpNull() - case scala.Some(v) => - val `vv₂`: scala.Int = `v₄`.asInstanceOf[scala.Int] - `out₂`.value(`vv₂`) - } - case scala.util.Failure(v) => - `out₂`.burpNull() - } - } - `out₂`.mark() - scala.util.Try.apply[scala.Unit](`out₂`.valueEscaped(`in₂`._2.asInstanceOf[java.lang.String])) match { - case scala.util.Success(_) => - () - case scala.util.Failure(_) => - `out₂`.revert() - if (`in₂`._2.asInstanceOf[scala.util.Try[scala.Option[scala.Int]]].==(null)) `out₂`.burpNull() else `in₂`._2.asInstanceOf[scala.util.Try[scala.Option[scala.Int]]] match { - case scala.util.Success(v) => - `v₅` match { - case null => - `out₂`.burpNull() - case scala.None => - `out₂`.burpNull() - case scala.Some(v) => - val `vv₃`: scala.Int = `v₆`.asInstanceOf[scala.Int] - `out₂`.value(`vv₃`) - } - case scala.util.Failure(v) => - `out₂`.burpNull() - } - } - `out₂`.endArray() - } - def w0(`in₃`: co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { - `out₃`.startObject() - `out₃`.label("a") - w1(`in₃`.a, `out₃`) - `out₃`.label("b") - w2(`in₃`.b, `out₃`) - `out₃`.endObject() - } - def r0(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String] = { - var _a: scala.collection.immutable.Seq[scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String] = null - var _b: scala.Tuple2[scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String, scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String] = null - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₄`.expectFirstObjectField(__co_blocke_scalajack_json_misc_LRUnionHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = { - val parsedArray: scala.collection.mutable.ListBuffer[scala.util.Try[scala.Option[scala.Int]] | java.lang.String] = `in₄`.expectArray[scala.util.Try[scala.Option[scala.Int]] | java.lang.String]((() => { - val mark: scala.Int = `in₄`.pos - scala.util.Try.apply[scala.util.Try[scala.Option[scala.Int]]]({ - val `mark₂`: scala.Int = `in₄`.pos - try if (`in₄`.expectNull()) null else scala.util.Success.apply[scala.Option[scala.Int]](if (`in₄`.expectNull()) null else scala.Some.apply[scala.Int](`in₄`.expectInt())) catch { - case t: java.lang.Throwable => - `in₄`.revertToPos(`mark₂`) - throw co.blocke.scalajack.json.JsonParseError.apply("Unsuccessful attempt to read Try type with failure: ".+(t.getMessage()), `in₄`) - } - }) match { - case scala.util.Success(lval) => - (lval: scala.util.Try[scala.Option[scala.Int]]) - case scala.util.Failure(f) => - `in₄`.revertToPos(mark) - scala.util.Try.apply[java.lang.String](`in₄`.expectString()) match { - case scala.util.Success(rval) => - (rval: java.lang.String) - case scala.util.Failure(_) => - `in₄`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₄`) - } - } - })) - - (if (parsedArray.!=(null)) parsedArray.toSeq else null: scala.collection.immutable.Seq[scala.util.Try[scala.Option[scala.Int]] | java.lang.String]) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₄`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _b = if (`in₄`.expectNull()) null else { - `in₄`.expectToken('[') - val tv: scala.Tuple2[scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String, scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String] = new scala.Tuple2[scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String, scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String][scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String, scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String]({ - val `mark₃`: scala.Int = `in₄`.pos - scala.util.Try.apply[scala.util.Try[scala.Option[scala.Int]]]({ - val `mark₄`: scala.Int = `in₄`.pos - try if (`in₄`.expectNull()) null else scala.util.Success.apply[scala.Option[scala.Int]](if (`in₄`.expectNull()) null else scala.Some.apply[scala.Int](`in₄`.expectInt())) catch { - case t: java.lang.Throwable => - `in₄`.revertToPos(`mark₄`) - throw co.blocke.scalajack.json.JsonParseError.apply("Unsuccessful attempt to read Try type with failure: ".+(`t₂`.getMessage()), `in₄`) - } - }) match { - case scala.util.Success(lval) => - (`lval₂`: scala.util.Try[scala.Option[scala.Int]]) - case scala.util.Failure(f) => - `in₄`.revertToPos(`mark₃`) - scala.util.Try.apply[java.lang.String](`in₄`.expectString()) match { - case scala.util.Success(rval) => - (`rval₂`: java.lang.String) - case scala.util.Failure(_) => - `in₄`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₄`) - } - } - }, { - `in₄`.expectToken(',') - val `mark₅`: scala.Int = `in₄`.pos - scala.util.Try.apply[scala.util.Try[scala.Option[scala.Int]]]({ - val `mark₆`: scala.Int = `in₄`.pos - try if (`in₄`.expectNull()) null else scala.util.Success.apply[scala.Option[scala.Int]](if (`in₄`.expectNull()) null else scala.Some.apply[scala.Int](`in₄`.expectInt())) catch { - case t: java.lang.Throwable => - `in₄`.revertToPos(`mark₆`) - throw co.blocke.scalajack.json.JsonParseError.apply("Unsuccessful attempt to read Try type with failure: ".+(`t₃`.getMessage()), `in₄`) - } - }) match { - case scala.util.Success(lval) => - (`lval₃`: scala.util.Try[scala.Option[scala.Int]]) - case scala.util.Failure(f) => - `in₄`.revertToPos(`mark₅`) - scala.util.Try.apply[java.lang.String](`in₄`.expectString()) match { - case scala.util.Success(rval) => - (`rval₃`: java.lang.String) - case scala.util.Failure(_) => - `in₄`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₄`) - } - } - }) - `in₄`.expectToken(']') - - (tv: scala.Tuple2[scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String, scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String]) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b"), `in₄`) - case _ => - `in₄`.skipValue() - } - maybeFieldNum = `in₄`.expectObjectField(__co_blocke_scalajack_json_misc_LRUnionHolder_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String][scala.util.Try[scala.Option[scala.Int]], java.lang.String](_a, _b) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a", "b")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₄`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String]] { - def encodeValue(`in₅`: co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String], `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₅`, `out₄`) - def decodeValue(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String] = r0(`in₆`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String]]) -} -Codec: { - val __co_blocke_scalajack_json_misc_LRUnionHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a", "b")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: scala.collection.immutable.Seq[scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startArray() - in.foreach[scala.Unit](((i: scala.util.Try[scala.Option[scala.Int]] | java.lang.String) => { - out.mark() - scala.util.Try.apply[scala.Unit](out.valueEscaped(i.asInstanceOf[java.lang.String])) match { - case scala.util.Success(_) => - () - case scala.util.Failure(_) => - out.revert() - if (i.asInstanceOf[scala.util.Try[scala.Option[scala.Int]]].==(null)) out.burpNull() else i.asInstanceOf[scala.util.Try[scala.Option[scala.Int]]] match { - case scala.util.Success(v) => - v match { - case null => - out.burpNull() - case scala.None => - () - case scala.Some(v) => - val vv: scala.Int = `v₂`.asInstanceOf[scala.Int] - out.value(vv) - } - case scala.util.Failure(v) => - out.burpNull() - } - } - })) - out.endArray() - } - def w2(`in₂`: scala.Tuple2[scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String, scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startArray() - `out₂`.mark() - scala.util.Try.apply[scala.Unit](`out₂`.valueEscaped(`in₂`._1.asInstanceOf[java.lang.String])) match { - case scala.util.Success(_) => - () - case scala.util.Failure(_) => - `out₂`.revert() - if (`in₂`._1.asInstanceOf[scala.util.Try[scala.Option[scala.Int]]].==(null)) `out₂`.burpNull() else `in₂`._1.asInstanceOf[scala.util.Try[scala.Option[scala.Int]]] match { - case scala.util.Success(v) => - `v₃` match { - case null => - `out₂`.burpNull() - case scala.None => - `out₂`.burpNull() - case scala.Some(v) => - val `vv₂`: scala.Int = `v₄`.asInstanceOf[scala.Int] - `out₂`.value(`vv₂`) - } - case scala.util.Failure(v) => - `out₂`.burpNull() - } - } - `out₂`.mark() - scala.util.Try.apply[scala.Unit](`out₂`.valueEscaped(`in₂`._2.asInstanceOf[java.lang.String])) match { - case scala.util.Success(_) => - () - case scala.util.Failure(_) => - `out₂`.revert() - if (`in₂`._2.asInstanceOf[scala.util.Try[scala.Option[scala.Int]]].==(null)) `out₂`.burpNull() else `in₂`._2.asInstanceOf[scala.util.Try[scala.Option[scala.Int]]] match { - case scala.util.Success(v) => - `v₅` match { - case null => - `out₂`.burpNull() - case scala.None => - `out₂`.burpNull() - case scala.Some(v) => - val `vv₃`: scala.Int = `v₆`.asInstanceOf[scala.Int] - `out₂`.value(`vv₃`) - } - case scala.util.Failure(v) => - `out₂`.burpNull() - } - } - `out₂`.endArray() - } - def w0(`in₃`: co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { - `out₃`.startObject() - `out₃`.label("a") - w1(`in₃`.a, `out₃`) - `out₃`.label("b") - w2(`in₃`.b, `out₃`) - `out₃`.endObject() - } - def r0(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String] = { - var _a: scala.collection.immutable.Seq[scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String] = null - var _b: scala.Tuple2[scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String, scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String] = null - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₄`.expectFirstObjectField(__co_blocke_scalajack_json_misc_LRUnionHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = { - val parsedArray: scala.collection.mutable.ListBuffer[scala.util.Try[scala.Option[scala.Int]] | java.lang.String] = `in₄`.expectArray[scala.util.Try[scala.Option[scala.Int]] | java.lang.String]((() => { - val mark: scala.Int = `in₄`.pos - scala.util.Try.apply[scala.util.Try[scala.Option[scala.Int]]]({ - val `mark₂`: scala.Int = `in₄`.pos - try if (`in₄`.expectNull()) null else scala.util.Success.apply[scala.Option[scala.Int]](if (`in₄`.expectNull()) null else scala.Some.apply[scala.Int](`in₄`.expectInt())) catch { - case t: java.lang.Throwable => - `in₄`.revertToPos(`mark₂`) - throw co.blocke.scalajack.json.JsonParseError.apply("Unsuccessful attempt to read Try type with failure: ".+(t.getMessage()), `in₄`) - } - }) match { - case scala.util.Success(lval) => - (lval: scala.util.Try[scala.Option[scala.Int]]) - case scala.util.Failure(f) => - `in₄`.revertToPos(mark) - scala.util.Try.apply[java.lang.String](`in₄`.expectString()) match { - case scala.util.Success(rval) => - (rval: java.lang.String) - case scala.util.Failure(_) => - `in₄`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₄`) - } - } - })) - - (if (parsedArray.!=(null)) parsedArray.toSeq else null: scala.collection.immutable.Seq[scala.util.Try[scala.Option[scala.Int]] | java.lang.String]) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₄`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _b = if (`in₄`.expectNull()) null else { - `in₄`.expectToken('[') - val tv: scala.Tuple2[scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String, scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String] = new scala.Tuple2[scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String, scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String][scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String, scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String]({ - val `mark₃`: scala.Int = `in₄`.pos - scala.util.Try.apply[scala.util.Try[scala.Option[scala.Int]]]({ - val `mark₄`: scala.Int = `in₄`.pos - try if (`in₄`.expectNull()) null else scala.util.Success.apply[scala.Option[scala.Int]](if (`in₄`.expectNull()) null else scala.Some.apply[scala.Int](`in₄`.expectInt())) catch { - case t: java.lang.Throwable => - `in₄`.revertToPos(`mark₄`) - throw co.blocke.scalajack.json.JsonParseError.apply("Unsuccessful attempt to read Try type with failure: ".+(`t₂`.getMessage()), `in₄`) - } - }) match { - case scala.util.Success(lval) => - (`lval₂`: scala.util.Try[scala.Option[scala.Int]]) - case scala.util.Failure(f) => - `in₄`.revertToPos(`mark₃`) - scala.util.Try.apply[java.lang.String](`in₄`.expectString()) match { - case scala.util.Success(rval) => - (`rval₂`: java.lang.String) - case scala.util.Failure(_) => - `in₄`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₄`) - } - } - }, { - `in₄`.expectToken(',') - val `mark₅`: scala.Int = `in₄`.pos - scala.util.Try.apply[scala.util.Try[scala.Option[scala.Int]]]({ - val `mark₆`: scala.Int = `in₄`.pos - try if (`in₄`.expectNull()) null else scala.util.Success.apply[scala.Option[scala.Int]](if (`in₄`.expectNull()) null else scala.Some.apply[scala.Int](`in₄`.expectInt())) catch { - case t: java.lang.Throwable => - `in₄`.revertToPos(`mark₆`) - throw co.blocke.scalajack.json.JsonParseError.apply("Unsuccessful attempt to read Try type with failure: ".+(`t₃`.getMessage()), `in₄`) - } - }) match { - case scala.util.Success(lval) => - (`lval₃`: scala.util.Try[scala.Option[scala.Int]]) - case scala.util.Failure(f) => - `in₄`.revertToPos(`mark₅`) - scala.util.Try.apply[java.lang.String](`in₄`.expectString()) match { - case scala.util.Success(rval) => - (`rval₃`: java.lang.String) - case scala.util.Failure(_) => - `in₄`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₄`) - } - } - }) - `in₄`.expectToken(']') - - (tv: scala.Tuple2[scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String, scala.util.Try[scala.Option[scala.Int]] | scala.Predef.String]) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b"), `in₄`) - case _ => - `in₄`.skipValue() - } - maybeFieldNum = `in₄`.expectObjectField(__co_blocke_scalajack_json_misc_LRUnionHolder_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String][scala.util.Try[scala.Option[scala.Int]], java.lang.String](_a, _b) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a", "b")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₄`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String]] { - def encodeValue(`in₅`: co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String], `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₅`, `out₄`) - def decodeValue(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String] = r0(`in₆`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.LRUnionHolder[scala.util.Try[scala.Option[scala.Int]], scala.Predef.String]]) -} -Codec: { - val __co_blocke_scalajack_json_misc_Person_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("name", "age")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - val __co_blocke_scalajack_json_misc_OptionHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a", "b", "c", "d", "e", "f", "g", "h", "i", "j")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: scala.Tuple2[scala.Option[scala.Int], scala.Predef.String], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startArray() - in._1 match { - case null => - out.burpNull() - case scala.None => - out.burpNull() - case scala.Some(v) => - val vv: scala.Int = v.asInstanceOf[scala.Int] - out.value(vv) - } - out.valueEscaped(in._2) - out.endArray() - } - def w2(`in₂`: scala.collection.immutable.List[scala.Option[scala.Int]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startArray() - `in₂`.foreach[scala.Unit](((i: scala.Option[scala.Int]) => i match { - case null => - `out₂`.burpNull() - case scala.None => - () - case scala.Some(v) => - val `vv₂`: scala.Int = `v₂`.asInstanceOf[scala.Int] - `out₂`.value(`vv₂`) - })) - `out₂`.endArray() - } - def w3(`in₃`: scala.collection.immutable.Map[scala.Int, scala.Option[scala.Int]], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { - `out₃`.startObject() - `in₃`.foreach[scala.Unit](((x$1: scala.Tuple2[scala.Int, scala.Option[scala.Int]]) => x$1 match { - case scala.Tuple2(key, value) => - value match { - case scala.None => - () - case scala.Some(v) => - { - `out₃`.maybeComma() - `out₃`.valueStringified(scala.Int.int2long(key)) - `out₃`.colon() - `out₃`.value(`v₃`) - } - } - })) - `out₃`.endObject() - } - def w4(`in₄`: co.blocke.scalajack.json.misc.Person, `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₄`.==(null)) `out₄`.burpNull() else { - `out₄`.startObject() - `out₄`.label("name") - `out₄`.valueEscaped(`in₄`.name) - `out₄`.label("age") - `out₄`.value(`in₄`.age) - `out₄`.endObject() - } - def w0(`in₅`: co.blocke.scalajack.json.misc.OptionHolder[scala.Int], `out₅`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₅`.==(null)) `out₅`.burpNull() else { - `out₅`.startObject() - `in₅`.a match { - case scala.None => - () - case scala.Some(v) => - { - `out₅`.label("a") - `out₅`.value(`v₄`) - } - } - `out₅`.label("b") - w1(`in₅`.b, `out₅`) - `out₅`.label("c") - w2(`in₅`.c, `out₅`) - `out₅`.label("d") - w3(`in₅`.d, `out₅`) - if (`in₅`.e.==(null)) `out₅`.burpNull() else { - `out₅`.mark() - scala.util.Try.apply[scala.Unit](`in₅`.e.asInstanceOf[scala.Option[scala.Int]] match { - case scala.None => - () - case scala.Some(v) => - { - `out₅`.label("e") - `out₅`.value(`v₅`) - } - }) match { - case scala.util.Success(_) => - () - case scala.util.Failure(f) => - `out₅`.revert() - { - `out₅`.label("e") - `out₅`.value(`in₅`.e.asInstanceOf[scala.Int]) - } - } - } - if (`in₅`.f.==(null)) `out₅`.burpNull() else { - `out₅`.mark() - scala.util.Try.apply[scala.Unit]({ - `out₅`.label("f") - `out₅`.value(`in₅`.f.asInstanceOf[scala.Int]) - }) match { - case scala.util.Success(_) => - () - case scala.util.Failure(f) => - `out₅`.revert() - `in₅`.f.asInstanceOf[scala.Option[scala.Int]] match { - case scala.None => - () - case scala.Some(v) => - { - `out₅`.label("f") - `out₅`.value(`v₆`) - } - } - } - } - `in₅`.g match { - case scala.None => - () - case scala.Some(v) => - `v₇` match { - case scala.None => - () - case scala.Some(v) => - { - `out₅`.label("g") - `out₅`.value(`v₈`) - } - } - } - `in₅`.h match { - case scala.None => - () - case scala.Some(v) => - { - `out₅`.label("h") - w4(`v₉`, `out₅`) - } - } - if (`in₅`.i.==(null)) `out₅`.burpNull() else () - `in₅`.i match { - case scala.Left(v) => - { - `out₅`.label("i") - `out₅`.value(`v₁₀`.asInstanceOf[scala.Int]) - } - case scala.Right(v) => - `v₁₁`.asInstanceOf[scala.Option[scala.Int]] match { - case scala.None => - () - case scala.Some(v) => - { - `out₅`.label("i") - `out₅`.value(`v₁₂`) - } - } - } - if (`in₅`.j.==(null)) `out₅`.burpNull() else () - `in₅`.j match { - case scala.Left(v) => - `v₁₃`.asInstanceOf[scala.Option[scala.Int]] match { - case scala.None => - () - case scala.Some(v) => - { - `out₅`.label("j") - `out₅`.value(`v₁₄`) - } - } - case scala.Right(v) => - { - `out₅`.label("j") - `out₅`.value(`v₁₅`.asInstanceOf[scala.Int]) - } - } - `out₅`.endObject() - } - def r1(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.Person = { - var _name: java.lang.String = "" - var _age: scala.Int = 0 - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₆`.expectFirstObjectField(__co_blocke_scalajack_json_misc_Person_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.Person] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _name = `in₆`.expectString() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("name"), `in₆`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _age = `in₆`.expectInt() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("age"), `in₆`) - case _ => - `in₆`.skipValue() - } - maybeFieldNum = `in₆`.expectObjectField(__co_blocke_scalajack_json_misc_Person_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.misc.Person(_name, _age) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("name", "age")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₆`) - } - } - def r0(`in₇`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.OptionHolder[scala.Int] = { - var _a: scala.Option[scala.Int] = scala.None - var _b: scala.Tuple2[scala.Option[scala.Int], scala.Predef.String] = null - var _c: scala.collection.immutable.List[scala.Option[scala.Int]] = null - var _d: scala.collection.immutable.Map[scala.Int, scala.Option[scala.Int]] = null - var _e: scala.Int | scala.Option[scala.Int] = scala.None - var _f: scala.Option[scala.Int] | scala.Int = scala.None - var _g: scala.Option[scala.Option[scala.Int]] = scala.None - var _h: scala.Option[co.blocke.scalajack.json.misc.Person] = scala.None - var _i: scala.util.Either[scala.Int, scala.Option[scala.Int]] = scala.Predef.augmentString("r").foldRight[scala.Any]((scala.None: scala.Any))(((c: scala.Char, acc: scala.Any) => if (c.==('r')) scala.Right.apply[scala.Nothing, scala.Any](acc) else scala.Left.apply[scala.Any, scala.Nothing](acc))).asInstanceOf[scala.util.Either[scala.Int, scala.Option[scala.Int]]] - var _j: scala.util.Either[scala.Option[scala.Int], scala.Int] = scala.Predef.augmentString("l").foldRight[scala.Any]((scala.None: scala.Any))(((`c₂`: scala.Char, `acc₂`: scala.Any) => if (`c₂`.==('r')) scala.Right.apply[scala.Nothing, scala.Any](`acc₂`) else scala.Left.apply[scala.Any, scala.Nothing](`acc₂`))).asInstanceOf[scala.util.Either[scala.Option[scala.Int], scala.Int]] - var `required₂`: scala.Int = 1023 - var `maybeFieldNum₂`: scala.Option[scala.Int] = `in₇`.expectFirstObjectField(__co_blocke_scalajack_json_misc_OptionHolder_fields) - if (`maybeFieldNum₂`.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.OptionHolder[scala.Int]] else { - while (`maybeFieldNum₂`.isDefined) { - `maybeFieldNum₂`.get match { - case 0 => - if (`required₂`.&(1).!=(0)) { - `required₂` = `required₂`.^(1) - _a = if (`in₇`.expectNull()) null else scala.Some.apply[scala.Int](`in₇`.expectInt()) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₇`) - case 1 => - if (`required₂`.&(2).!=(0)) { - `required₂` = `required₂`.^(2) - _b = if (`in₇`.expectNull()) null else { - `in₇`.expectToken('[') - val tv: scala.Tuple2[scala.Option[scala.Int], scala.Predef.String] = new scala.Tuple2[scala.Option[scala.Int], scala.Predef.String][scala.Option[scala.Int], java.lang.String](if (`in₇`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₇`.expectInt()), { - `in₇`.expectToken(',') - `in₇`.expectString() - }) - `in₇`.expectToken(']') - - (tv: scala.Tuple2[scala.Option[scala.Int], scala.Predef.String]) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b"), `in₇`) - case 2 => - if (`required₂`.&(4).!=(0)) { - `required₂` = `required₂`.^(4) - _c = { - val parsedArray: scala.collection.mutable.ListBuffer[scala.Option[scala.Int]] = `in₇`.expectArray[scala.Option[scala.Int]]((() => if (`in₇`.expectNull()) null else scala.Some.apply[scala.Int](`in₇`.expectInt()))) - - (if (parsedArray.!=(null)) parsedArray.toList else null: scala.collection.immutable.List[scala.Option[scala.Int]]) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c"), `in₇`) - case 3 => - if (`required₂`.&(8).!=(0)) { - `required₂` = `required₂`.^(8) - _d = if (`in₇`.expectNull()) null else { - `in₇`.expectToken('{') - `in₇`.parseMap[scala.Int, scala.Option[scala.Int]]((() => scala.Predef.augmentString(`in₇`.expectString()).toInt), (() => if (`in₇`.expectNull()) null else scala.Some.apply[scala.Int](`in₇`.expectInt())), scala.Predef.Map.empty[scala.Int, scala.Option[scala.Int]], true) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d"), `in₇`) - case 4 => - if (`required₂`.&(16).!=(0)) { - `required₂` = `required₂`.^(16) - _e = { - val mark: scala.Int = `in₇`.pos - scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { - case scala.util.Success(lval) => - (lval: scala.Int) - case scala.util.Failure(f) => - `in₇`.revertToPos(mark) - scala.util.Try.apply[scala.Option[scala.Int]](if (`in₇`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₇`.expectInt())) match { - case scala.util.Success(rval) => - (rval: scala.Option[scala.Int]) - case scala.util.Failure(_) => - `in₇`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₇`) - } - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("e"), `in₇`) - case 5 => - if (`required₂`.&(32).!=(0)) { - `required₂` = `required₂`.^(32) - _f = { - val `mark₂`: scala.Int = `in₇`.pos - scala.util.Try.apply[scala.Option[scala.Int]](if (`in₇`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₇`.expectInt())) match { - case scala.util.Success(lval) => - (`lval₂`: scala.Option[scala.Int]) - case scala.util.Failure(f) => - `in₇`.revertToPos(`mark₂`) - scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { - case scala.util.Success(rval) => - (`rval₂`: scala.Int) - case scala.util.Failure(_) => - `in₇`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₇`) - } - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f"), `in₇`) - case 6 => - if (`required₂`.&(64).!=(0)) { - `required₂` = `required₂`.^(64) - _g = if (`in₇`.expectNull()) null else scala.Some.apply[scala.Option[scala.Int]](if (`in₇`.expectNull()) null else scala.Some.apply[scala.Int](`in₇`.expectInt())) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("g"), `in₇`) - case 7 => - if (`required₂`.&(128).!=(0)) { - `required₂` = `required₂`.^(128) - _h = if (`in₇`.expectNull()) null else scala.Some.apply[co.blocke.scalajack.json.misc.Person](r1(`in₇`)) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("h"), `in₇`) - case 8 => - if (`required₂`.&(256).!=(0)) { - `required₂` = `required₂`.^(256) - _i = { - val `mark₃`: scala.Int = `in₇`.pos - if (`in₇`.expectNull()) null else scala.util.Try.apply[scala.Option[scala.Int]](if (`in₇`.expectNull()) null else scala.Some.apply[scala.Int](`in₇`.expectInt())) match { - case scala.util.Success(rval) => - scala.Right.apply[scala.Nothing, scala.Option[scala.Int]](`rval₃`) - case scala.util.Failure(f) => - `in₇`.revertToPos(`mark₃`) - scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { - case scala.util.Success(lval) => - scala.Left.apply[scala.Int, scala.Nothing](`lval₃`) - case scala.util.Failure(_) => - `in₇`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₇`) - } - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i"), `in₇`) - case 9 => - if (`required₂`.&(512).!=(0)) { - `required₂` = `required₂`.^(512) - _j = { - val `mark₄`: scala.Int = `in₇`.pos - if (`in₇`.expectNull()) null else scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { - case scala.util.Success(rval) => - scala.Right.apply[scala.Nothing, scala.Int](`rval₄`) - case scala.util.Failure(f) => - `in₇`.revertToPos(`mark₄`) - scala.util.Try.apply[scala.Option[scala.Int]](if (`in₇`.expectNull()) null else scala.Some.apply[scala.Int](`in₇`.expectInt())) match { - case scala.util.Success(lval) => - scala.Left.apply[scala.Option[scala.Int], scala.Nothing](`lval₄`) - case scala.util.Failure(_) => - `in₇`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₇`) - } - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("j"), `in₇`) - case _ => - `in₇`.skipValue() - } - `maybeFieldNum₂` = `in₇`.expectObjectField(__co_blocke_scalajack_json_misc_OptionHolder_fields) - } - if (`required₂`.&(14).==(0)) new co.blocke.scalajack.json.misc.OptionHolder[scala.Int][scala.Int](_a, _b, _c, _d, _e, _f, _g, _h, _i, _j) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a", "b", "c", "d", "e", "f", "g", "h", "i", "j")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(`required₂`.&(14)))), `in₇`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.OptionHolder[scala.Int]] { - def encodeValue(`in₈`: co.blocke.scalajack.json.misc.OptionHolder[scala.Int], `out₆`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₈`, `out₆`) - def decodeValue(`in₉`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.OptionHolder[scala.Int] = r0(`in₉`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.OptionHolder[scala.Int]]) -} -Codec: { - val __co_blocke_scalajack_json_misc_Person_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("name", "age")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - val __co_blocke_scalajack_json_misc_OptionHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a", "b", "c", "d", "e", "f", "g", "h", "i", "j")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: scala.Tuple2[scala.Option[scala.Int], scala.Predef.String], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startArray() - in._1 match { - case null => - out.burpNull() - case scala.None => - out.burpNull() - case scala.Some(v) => - val vv: scala.Int = v.asInstanceOf[scala.Int] - out.value(vv) - } - out.valueEscaped(in._2) - out.endArray() - } - def w2(`in₂`: scala.collection.immutable.List[scala.Option[scala.Int]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startArray() - `in₂`.foreach[scala.Unit](((i: scala.Option[scala.Int]) => i match { - case null => - `out₂`.burpNull() - case scala.None => - () - case scala.Some(v) => - val `vv₂`: scala.Int = `v₂`.asInstanceOf[scala.Int] - `out₂`.value(`vv₂`) - })) - `out₂`.endArray() - } - def w3(`in₃`: scala.collection.immutable.Map[scala.Int, scala.Option[scala.Int]], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { - `out₃`.startObject() - `in₃`.foreach[scala.Unit](((x$1: scala.Tuple2[scala.Int, scala.Option[scala.Int]]) => x$1 match { - case scala.Tuple2(key, value) => - value match { - case scala.None => - () - case scala.Some(v) => - { - `out₃`.maybeComma() - `out₃`.valueStringified(scala.Int.int2long(key)) - `out₃`.colon() - `out₃`.value(`v₃`) - } - } - })) - `out₃`.endObject() - } - def w4(`in₄`: co.blocke.scalajack.json.misc.Person, `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₄`.==(null)) `out₄`.burpNull() else { - `out₄`.startObject() - `out₄`.label("name") - `out₄`.valueEscaped(`in₄`.name) - `out₄`.label("age") - `out₄`.value(`in₄`.age) - `out₄`.endObject() - } - def w0(`in₅`: co.blocke.scalajack.json.misc.OptionHolder[scala.Int], `out₅`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₅`.==(null)) `out₅`.burpNull() else { - `out₅`.startObject() - `in₅`.a match { - case scala.None => - () - case scala.Some(v) => - { - `out₅`.label("a") - `out₅`.value(`v₄`) - } - } - `out₅`.label("b") - w1(`in₅`.b, `out₅`) - `out₅`.label("c") - w2(`in₅`.c, `out₅`) - `out₅`.label("d") - w3(`in₅`.d, `out₅`) - if (`in₅`.e.==(null)) `out₅`.burpNull() else { - `out₅`.mark() - scala.util.Try.apply[scala.Unit](`in₅`.e.asInstanceOf[scala.Option[scala.Int]] match { - case scala.None => - () - case scala.Some(v) => - { - `out₅`.label("e") - `out₅`.value(`v₅`) - } - }) match { - case scala.util.Success(_) => - () - case scala.util.Failure(f) => - `out₅`.revert() - { - `out₅`.label("e") - `out₅`.value(`in₅`.e.asInstanceOf[scala.Int]) - } - } - } - if (`in₅`.f.==(null)) `out₅`.burpNull() else { - `out₅`.mark() - scala.util.Try.apply[scala.Unit]({ - `out₅`.label("f") - `out₅`.value(`in₅`.f.asInstanceOf[scala.Int]) - }) match { - case scala.util.Success(_) => - () - case scala.util.Failure(f) => - `out₅`.revert() - `in₅`.f.asInstanceOf[scala.Option[scala.Int]] match { - case scala.None => - () - case scala.Some(v) => - { - `out₅`.label("f") - `out₅`.value(`v₆`) - } - } - } - } - `in₅`.g match { - case scala.None => - () - case scala.Some(v) => - `v₇` match { - case scala.None => - () - case scala.Some(v) => - { - `out₅`.label("g") - `out₅`.value(`v₈`) - } - } - } - `in₅`.h match { - case scala.None => - () - case scala.Some(v) => - { - `out₅`.label("h") - w4(`v₉`, `out₅`) - } - } - if (`in₅`.i.==(null)) `out₅`.burpNull() else () - `in₅`.i match { - case scala.Left(v) => - { - `out₅`.label("i") - `out₅`.value(`v₁₀`.asInstanceOf[scala.Int]) - } - case scala.Right(v) => - `v₁₁`.asInstanceOf[scala.Option[scala.Int]] match { - case scala.None => - () - case scala.Some(v) => - { - `out₅`.label("i") - `out₅`.value(`v₁₂`) - } - } - } - if (`in₅`.j.==(null)) `out₅`.burpNull() else () - `in₅`.j match { - case scala.Left(v) => - `v₁₃`.asInstanceOf[scala.Option[scala.Int]] match { - case scala.None => - () - case scala.Some(v) => - { - `out₅`.label("j") - `out₅`.value(`v₁₄`) - } - } - case scala.Right(v) => - { - `out₅`.label("j") - `out₅`.value(`v₁₅`.asInstanceOf[scala.Int]) - } - } - `out₅`.endObject() - } - def r1(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.Person = { - var _name: java.lang.String = "" - var _age: scala.Int = 0 - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₆`.expectFirstObjectField(__co_blocke_scalajack_json_misc_Person_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.Person] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _name = `in₆`.expectString() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("name"), `in₆`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _age = `in₆`.expectInt() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("age"), `in₆`) - case _ => - `in₆`.skipValue() - } - maybeFieldNum = `in₆`.expectObjectField(__co_blocke_scalajack_json_misc_Person_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.misc.Person(_name, _age) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("name", "age")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₆`) - } - } - def r0(`in₇`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.OptionHolder[scala.Int] = { - var _a: scala.Option[scala.Int] = scala.None - var _b: scala.Tuple2[scala.Option[scala.Int], scala.Predef.String] = null - var _c: scala.collection.immutable.List[scala.Option[scala.Int]] = null - var _d: scala.collection.immutable.Map[scala.Int, scala.Option[scala.Int]] = null - var _e: scala.Int | scala.Option[scala.Int] = scala.None - var _f: scala.Option[scala.Int] | scala.Int = scala.None - var _g: scala.Option[scala.Option[scala.Int]] = scala.None - var _h: scala.Option[co.blocke.scalajack.json.misc.Person] = scala.None - var _i: scala.util.Either[scala.Int, scala.Option[scala.Int]] = scala.Predef.augmentString("r").foldRight[scala.Any]((scala.None: scala.Any))(((c: scala.Char, acc: scala.Any) => if (c.==('r')) scala.Right.apply[scala.Nothing, scala.Any](acc) else scala.Left.apply[scala.Any, scala.Nothing](acc))).asInstanceOf[scala.util.Either[scala.Int, scala.Option[scala.Int]]] - var _j: scala.util.Either[scala.Option[scala.Int], scala.Int] = scala.Predef.augmentString("l").foldRight[scala.Any]((scala.None: scala.Any))(((`c₂`: scala.Char, `acc₂`: scala.Any) => if (`c₂`.==('r')) scala.Right.apply[scala.Nothing, scala.Any](`acc₂`) else scala.Left.apply[scala.Any, scala.Nothing](`acc₂`))).asInstanceOf[scala.util.Either[scala.Option[scala.Int], scala.Int]] - var `required₂`: scala.Int = 1023 - var `maybeFieldNum₂`: scala.Option[scala.Int] = `in₇`.expectFirstObjectField(__co_blocke_scalajack_json_misc_OptionHolder_fields) - if (`maybeFieldNum₂`.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.OptionHolder[scala.Int]] else { - while (`maybeFieldNum₂`.isDefined) { - `maybeFieldNum₂`.get match { - case 0 => - if (`required₂`.&(1).!=(0)) { - `required₂` = `required₂`.^(1) - _a = if (`in₇`.expectNull()) null else scala.Some.apply[scala.Int](`in₇`.expectInt()) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₇`) - case 1 => - if (`required₂`.&(2).!=(0)) { - `required₂` = `required₂`.^(2) - _b = if (`in₇`.expectNull()) null else { - `in₇`.expectToken('[') - val tv: scala.Tuple2[scala.Option[scala.Int], scala.Predef.String] = new scala.Tuple2[scala.Option[scala.Int], scala.Predef.String][scala.Option[scala.Int], java.lang.String](if (`in₇`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₇`.expectInt()), { - `in₇`.expectToken(',') - `in₇`.expectString() - }) - `in₇`.expectToken(']') - - (tv: scala.Tuple2[scala.Option[scala.Int], scala.Predef.String]) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b"), `in₇`) - case 2 => - if (`required₂`.&(4).!=(0)) { - `required₂` = `required₂`.^(4) - _c = { - val parsedArray: scala.collection.mutable.ListBuffer[scala.Option[scala.Int]] = `in₇`.expectArray[scala.Option[scala.Int]]((() => if (`in₇`.expectNull()) null else scala.Some.apply[scala.Int](`in₇`.expectInt()))) - - (if (parsedArray.!=(null)) parsedArray.toList else null: scala.collection.immutable.List[scala.Option[scala.Int]]) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c"), `in₇`) - case 3 => - if (`required₂`.&(8).!=(0)) { - `required₂` = `required₂`.^(8) - _d = if (`in₇`.expectNull()) null else { - `in₇`.expectToken('{') - `in₇`.parseMap[scala.Int, scala.Option[scala.Int]]((() => scala.Predef.augmentString(`in₇`.expectString()).toInt), (() => if (`in₇`.expectNull()) null else scala.Some.apply[scala.Int](`in₇`.expectInt())), scala.Predef.Map.empty[scala.Int, scala.Option[scala.Int]], true) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d"), `in₇`) - case 4 => - if (`required₂`.&(16).!=(0)) { - `required₂` = `required₂`.^(16) - _e = { - val mark: scala.Int = `in₇`.pos - scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { - case scala.util.Success(lval) => - (lval: scala.Int) - case scala.util.Failure(f) => - `in₇`.revertToPos(mark) - scala.util.Try.apply[scala.Option[scala.Int]](if (`in₇`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₇`.expectInt())) match { - case scala.util.Success(rval) => - (rval: scala.Option[scala.Int]) - case scala.util.Failure(_) => - `in₇`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₇`) - } - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("e"), `in₇`) - case 5 => - if (`required₂`.&(32).!=(0)) { - `required₂` = `required₂`.^(32) - _f = { - val `mark₂`: scala.Int = `in₇`.pos - scala.util.Try.apply[scala.Option[scala.Int]](if (`in₇`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₇`.expectInt())) match { - case scala.util.Success(lval) => - (`lval₂`: scala.Option[scala.Int]) - case scala.util.Failure(f) => - `in₇`.revertToPos(`mark₂`) - scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { - case scala.util.Success(rval) => - (`rval₂`: scala.Int) - case scala.util.Failure(_) => - `in₇`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₇`) - } - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f"), `in₇`) - case 6 => - if (`required₂`.&(64).!=(0)) { - `required₂` = `required₂`.^(64) - _g = if (`in₇`.expectNull()) null else scala.Some.apply[scala.Option[scala.Int]](if (`in₇`.expectNull()) null else scala.Some.apply[scala.Int](`in₇`.expectInt())) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("g"), `in₇`) - case 7 => - if (`required₂`.&(128).!=(0)) { - `required₂` = `required₂`.^(128) - _h = if (`in₇`.expectNull()) null else scala.Some.apply[co.blocke.scalajack.json.misc.Person](r1(`in₇`)) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("h"), `in₇`) - case 8 => - if (`required₂`.&(256).!=(0)) { - `required₂` = `required₂`.^(256) - _i = { - val `mark₃`: scala.Int = `in₇`.pos - if (`in₇`.expectNull()) null else scala.util.Try.apply[scala.Option[scala.Int]](if (`in₇`.expectNull()) null else scala.Some.apply[scala.Int](`in₇`.expectInt())) match { - case scala.util.Success(rval) => - scala.Right.apply[scala.Nothing, scala.Option[scala.Int]](`rval₃`) - case scala.util.Failure(f) => - `in₇`.revertToPos(`mark₃`) - scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { - case scala.util.Success(lval) => - scala.Left.apply[scala.Int, scala.Nothing](`lval₃`) - case scala.util.Failure(_) => - `in₇`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₇`) - } - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i"), `in₇`) - case 9 => - if (`required₂`.&(512).!=(0)) { - `required₂` = `required₂`.^(512) - _j = { - val `mark₄`: scala.Int = `in₇`.pos - if (`in₇`.expectNull()) null else scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { - case scala.util.Success(rval) => - scala.Right.apply[scala.Nothing, scala.Int](`rval₄`) - case scala.util.Failure(f) => - `in₇`.revertToPos(`mark₄`) - scala.util.Try.apply[scala.Option[scala.Int]](if (`in₇`.expectNull()) null else scala.Some.apply[scala.Int](`in₇`.expectInt())) match { - case scala.util.Success(lval) => - scala.Left.apply[scala.Option[scala.Int], scala.Nothing](`lval₄`) - case scala.util.Failure(_) => - `in₇`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₇`) - } - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("j"), `in₇`) - case _ => - `in₇`.skipValue() - } - `maybeFieldNum₂` = `in₇`.expectObjectField(__co_blocke_scalajack_json_misc_OptionHolder_fields) - } - if (`required₂`.&(14).==(0)) new co.blocke.scalajack.json.misc.OptionHolder[scala.Int][scala.Int](_a, _b, _c, _d, _e, _f, _g, _h, _i, _j) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a", "b", "c", "d", "e", "f", "g", "h", "i", "j")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(`required₂`.&(14)))), `in₇`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.OptionHolder[scala.Int]] { - def encodeValue(`in₈`: co.blocke.scalajack.json.misc.OptionHolder[scala.Int], `out₆`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₈`, `out₆`) - def decodeValue(`in₉`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.OptionHolder[scala.Int] = r0(`in₉`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.OptionHolder[scala.Int]]) -} -Codec: { - val __co_blocke_scalajack_json_misc_Person_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("name", "age")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - val __co_blocke_scalajack_json_misc_OptionHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a", "b", "c", "d", "e", "f", "g", "h", "i", "j")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: scala.Tuple2[scala.Option[scala.Int], scala.Predef.String], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startArray() - in._1 match { - case null => - out.burpNull() - case scala.None => - out.burpNull() - case scala.Some(v) => - val vv: scala.Int = v.asInstanceOf[scala.Int] - out.value(vv) - } - out.valueEscaped(in._2) - out.endArray() - } - def w2(`in₂`: scala.collection.immutable.List[scala.Option[scala.Int]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startArray() - `in₂`.foreach[scala.Unit](((i: scala.Option[scala.Int]) => i match { - case null => - `out₂`.burpNull() - case scala.None => - `out₂`.burpNull() - case scala.Some(v) => - val `vv₂`: scala.Int = `v₂`.asInstanceOf[scala.Int] - `out₂`.value(`vv₂`) - })) - `out₂`.endArray() - } - def w3(`in₃`: scala.collection.immutable.Map[scala.Int, scala.Option[scala.Int]], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { - `out₃`.startObject() - `in₃`.foreach[scala.Unit](((x$1: scala.Tuple2[scala.Int, scala.Option[scala.Int]]) => x$1 match { - case scala.Tuple2(key, value) => - { - `out₃`.maybeComma() - `out₃`.valueStringified(scala.Int.int2long(key)) - `out₃`.colon() - value match { - case null => - `out₃`.burpNull() - case scala.None => - `out₃`.burpNull() - case scala.Some(v) => - val `vv₃`: scala.Int = `v₃`.asInstanceOf[scala.Int] - `out₃`.value(`vv₃`) - } - } - })) - `out₃`.endObject() - } - def w4(`in₄`: co.blocke.scalajack.json.misc.Person, `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₄`.==(null)) `out₄`.burpNull() else { - `out₄`.startObject() - `out₄`.label("name") - `out₄`.valueEscaped(`in₄`.name) - `out₄`.label("age") - `out₄`.value(`in₄`.age) - `out₄`.endObject() - } - def w0(`in₅`: co.blocke.scalajack.json.misc.OptionHolder[scala.Int], `out₅`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₅`.==(null)) `out₅`.burpNull() else { - `out₅`.startObject() - `out₅`.label("a") - `in₅`.a match { - case null => - `out₅`.burpNull() - case scala.None => - `out₅`.burpNull() - case scala.Some(v) => - val `vv₄`: scala.Int = `v₄`.asInstanceOf[scala.Int] - `out₅`.value(`vv₄`) - } - `out₅`.label("b") - w1(`in₅`.b, `out₅`) - `out₅`.label("c") - w2(`in₅`.c, `out₅`) - `out₅`.label("d") - w3(`in₅`.d, `out₅`) - if (`in₅`.e.==(null)) `out₅`.burpNull() else { - `out₅`.mark() - scala.util.Try.apply[scala.Unit]({ - `out₅`.label("e") - `in₅`.e.asInstanceOf[scala.Option[scala.Int]] match { - case null => - `out₅`.burpNull() - case scala.None => - `out₅`.burpNull() - case scala.Some(v) => - val `vv₅`: scala.Int = `v₅`.asInstanceOf[scala.Int] - `out₅`.value(`vv₅`) - } - }) match { - case scala.util.Success(_) => - () - case scala.util.Failure(f) => - `out₅`.revert() - { - `out₅`.label("e") - `out₅`.value(`in₅`.e.asInstanceOf[scala.Int]) - } - } - } - if (`in₅`.f.==(null)) `out₅`.burpNull() else { - `out₅`.mark() - scala.util.Try.apply[scala.Unit]({ - `out₅`.label("f") - `out₅`.value(`in₅`.f.asInstanceOf[scala.Int]) - }) match { - case scala.util.Success(_) => - () - case scala.util.Failure(f) => - `out₅`.revert() - { - `out₅`.label("f") - `in₅`.f.asInstanceOf[scala.Option[scala.Int]] match { - case null => - `out₅`.burpNull() - case scala.None => - `out₅`.burpNull() - case scala.Some(v) => - val `vv₆`: scala.Int = `v₆`.asInstanceOf[scala.Int] - `out₅`.value(`vv₆`) - } - } - } - } - `out₅`.label("g") - `in₅`.g match { - case null => - `out₅`.burpNull() - case scala.None => - `out₅`.burpNull() - case scala.Some(v) => - val `vv₇`: scala.Option[scala.Int] = `v₇`.asInstanceOf[scala.Option[scala.Int]] - `vv₇` match { - case null => - `out₅`.burpNull() - case scala.None => - `out₅`.burpNull() - case scala.Some(v) => - val `vv₈`: scala.Int = `v₈`.asInstanceOf[scala.Int] - `out₅`.value(`vv₈`) - } - } - `out₅`.label("h") - `in₅`.h match { - case null => - `out₅`.burpNull() - case scala.None => - `out₅`.burpNull() - case scala.Some(v) => - val `vv₉`: co.blocke.scalajack.json.misc.Person = `v₉`.asInstanceOf[co.blocke.scalajack.json.misc.Person] - w4(`vv₉`, `out₅`) - } - if (`in₅`.i.==(null)) `out₅`.burpNull() else () - `in₅`.i match { - case scala.Left(v) => - { - `out₅`.label("i") - `out₅`.value(`v₁₀`.asInstanceOf[scala.Int]) - } - case scala.Right(v) => - { - `out₅`.label("i") - `v₁₁`.asInstanceOf[scala.Option[scala.Int]] match { - case null => - `out₅`.burpNull() - case scala.None => - `out₅`.burpNull() - case scala.Some(v) => - val `vv₁₀`: scala.Int = `v₁₂`.asInstanceOf[scala.Int] - `out₅`.value(`vv₁₀`) - } - } - } - if (`in₅`.j.==(null)) `out₅`.burpNull() else () - `in₅`.j match { - case scala.Left(v) => - { - `out₅`.label("j") - `v₁₃`.asInstanceOf[scala.Option[scala.Int]] match { - case null => - `out₅`.burpNull() - case scala.None => - `out₅`.burpNull() - case scala.Some(v) => - val `vv₁₁`: scala.Int = `v₁₄`.asInstanceOf[scala.Int] - `out₅`.value(`vv₁₁`) - } - } - case scala.Right(v) => - { - `out₅`.label("j") - `out₅`.value(`v₁₅`.asInstanceOf[scala.Int]) - } - } - `out₅`.endObject() - } - def r1(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.Person = { - var _name: java.lang.String = "" - var _age: scala.Int = 0 - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₆`.expectFirstObjectField(__co_blocke_scalajack_json_misc_Person_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.Person] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _name = `in₆`.expectString() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("name"), `in₆`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _age = `in₆`.expectInt() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("age"), `in₆`) - case _ => - `in₆`.skipValue() - } - maybeFieldNum = `in₆`.expectObjectField(__co_blocke_scalajack_json_misc_Person_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.misc.Person(_name, _age) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("name", "age")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₆`) - } - } - def r0(`in₇`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.OptionHolder[scala.Int] = { - var _a: scala.Option[scala.Int] = scala.None - var _b: scala.Tuple2[scala.Option[scala.Int], scala.Predef.String] = null - var _c: scala.collection.immutable.List[scala.Option[scala.Int]] = null - var _d: scala.collection.immutable.Map[scala.Int, scala.Option[scala.Int]] = null - var _e: scala.Int | scala.Option[scala.Int] = scala.None - var _f: scala.Option[scala.Int] | scala.Int = scala.None - var _g: scala.Option[scala.Option[scala.Int]] = scala.None - var _h: scala.Option[co.blocke.scalajack.json.misc.Person] = scala.None - var _i: scala.util.Either[scala.Int, scala.Option[scala.Int]] = scala.Predef.augmentString("r").foldRight[scala.Any]((scala.None: scala.Any))(((c: scala.Char, acc: scala.Any) => if (c.==('r')) scala.Right.apply[scala.Nothing, scala.Any](acc) else scala.Left.apply[scala.Any, scala.Nothing](acc))).asInstanceOf[scala.util.Either[scala.Int, scala.Option[scala.Int]]] - var _j: scala.util.Either[scala.Option[scala.Int], scala.Int] = scala.Predef.augmentString("l").foldRight[scala.Any]((scala.None: scala.Any))(((`c₂`: scala.Char, `acc₂`: scala.Any) => if (`c₂`.==('r')) scala.Right.apply[scala.Nothing, scala.Any](`acc₂`) else scala.Left.apply[scala.Any, scala.Nothing](`acc₂`))).asInstanceOf[scala.util.Either[scala.Option[scala.Int], scala.Int]] - var `required₂`: scala.Int = 1023 - var `maybeFieldNum₂`: scala.Option[scala.Int] = `in₇`.expectFirstObjectField(__co_blocke_scalajack_json_misc_OptionHolder_fields) - if (`maybeFieldNum₂`.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.OptionHolder[scala.Int]] else { - while (`maybeFieldNum₂`.isDefined) { - `maybeFieldNum₂`.get match { - case 0 => - if (`required₂`.&(1).!=(0)) { - `required₂` = `required₂`.^(1) - _a = if (`in₇`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₇`.expectInt()) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₇`) - case 1 => - if (`required₂`.&(2).!=(0)) { - `required₂` = `required₂`.^(2) - _b = if (`in₇`.expectNull()) null else { - `in₇`.expectToken('[') - val tv: scala.Tuple2[scala.Option[scala.Int], scala.Predef.String] = new scala.Tuple2[scala.Option[scala.Int], scala.Predef.String][scala.Option[scala.Int], java.lang.String](if (`in₇`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₇`.expectInt()), { - `in₇`.expectToken(',') - `in₇`.expectString() - }) - `in₇`.expectToken(']') - - (tv: scala.Tuple2[scala.Option[scala.Int], scala.Predef.String]) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b"), `in₇`) - case 2 => - if (`required₂`.&(4).!=(0)) { - `required₂` = `required₂`.^(4) - _c = { - val parsedArray: scala.collection.mutable.ListBuffer[scala.Option[scala.Int]] = `in₇`.expectArray[scala.Option[scala.Int]]((() => if (`in₇`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₇`.expectInt()))) - - (if (parsedArray.!=(null)) parsedArray.toList else null: scala.collection.immutable.List[scala.Option[scala.Int]]) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c"), `in₇`) - case 3 => - if (`required₂`.&(8).!=(0)) { - `required₂` = `required₂`.^(8) - _d = if (`in₇`.expectNull()) null else { - `in₇`.expectToken('{') - `in₇`.parseMap[scala.Int, scala.Option[scala.Int]]((() => scala.Predef.augmentString(`in₇`.expectString()).toInt), (() => if (`in₇`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₇`.expectInt())), scala.Predef.Map.empty[scala.Int, scala.Option[scala.Int]], true) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d"), `in₇`) - case 4 => - if (`required₂`.&(16).!=(0)) { - `required₂` = `required₂`.^(16) - _e = { - val mark: scala.Int = `in₇`.pos - scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { - case scala.util.Success(lval) => - (lval: scala.Int) - case scala.util.Failure(f) => - `in₇`.revertToPos(mark) - scala.util.Try.apply[scala.Option[scala.Int]](if (`in₇`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₇`.expectInt())) match { - case scala.util.Success(rval) => - (rval: scala.Option[scala.Int]) - case scala.util.Failure(_) => - `in₇`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₇`) - } - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("e"), `in₇`) - case 5 => - if (`required₂`.&(32).!=(0)) { - `required₂` = `required₂`.^(32) - _f = { - val `mark₂`: scala.Int = `in₇`.pos - scala.util.Try.apply[scala.Option[scala.Int]](if (`in₇`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₇`.expectInt())) match { - case scala.util.Success(lval) => - (`lval₂`: scala.Option[scala.Int]) - case scala.util.Failure(f) => - `in₇`.revertToPos(`mark₂`) - scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { - case scala.util.Success(rval) => - (`rval₂`: scala.Int) - case scala.util.Failure(_) => - `in₇`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₇`) - } - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f"), `in₇`) - case 6 => - if (`required₂`.&(64).!=(0)) { - `required₂` = `required₂`.^(64) - _g = if (`in₇`.expectNull()) scala.None else scala.Some.apply[scala.Option[scala.Int]](if (`in₇`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₇`.expectInt())) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("g"), `in₇`) - case 7 => - if (`required₂`.&(128).!=(0)) { - `required₂` = `required₂`.^(128) - _h = if (`in₇`.expectNull()) scala.None else scala.Some.apply[co.blocke.scalajack.json.misc.Person](r1(`in₇`)) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("h"), `in₇`) - case 8 => - if (`required₂`.&(256).!=(0)) { - `required₂` = `required₂`.^(256) - _i = { - val `mark₃`: scala.Int = `in₇`.pos - if (`in₇`.expectNull()) null else scala.util.Try.apply[scala.Option[scala.Int]](if (`in₇`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₇`.expectInt())) match { - case scala.util.Success(rval) => - scala.Right.apply[scala.Nothing, scala.Option[scala.Int]](`rval₃`) - case scala.util.Failure(f) => - `in₇`.revertToPos(`mark₃`) - scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { - case scala.util.Success(lval) => - scala.Left.apply[scala.Int, scala.Nothing](`lval₃`) - case scala.util.Failure(_) => - `in₇`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₇`) - } - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i"), `in₇`) - case 9 => - if (`required₂`.&(512).!=(0)) { - `required₂` = `required₂`.^(512) - _j = { - val `mark₄`: scala.Int = `in₇`.pos - if (`in₇`.expectNull()) null else scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { - case scala.util.Success(rval) => - scala.Right.apply[scala.Nothing, scala.Int](`rval₄`) - case scala.util.Failure(f) => - `in₇`.revertToPos(`mark₄`) - scala.util.Try.apply[scala.Option[scala.Int]](if (`in₇`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₇`.expectInt())) match { - case scala.util.Success(lval) => - scala.Left.apply[scala.Option[scala.Int], scala.Nothing](`lval₄`) - case scala.util.Failure(_) => - `in₇`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₇`) - } - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("j"), `in₇`) - case _ => - `in₇`.skipValue() - } - `maybeFieldNum₂` = `in₇`.expectObjectField(__co_blocke_scalajack_json_misc_OptionHolder_fields) - } - if (`required₂`.&(14).==(0)) new co.blocke.scalajack.json.misc.OptionHolder[scala.Int][scala.Int](_a, _b, _c, _d, _e, _f, _g, _h, _i, _j) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a", "b", "c", "d", "e", "f", "g", "h", "i", "j")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(`required₂`.&(14)))), `in₇`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.OptionHolder[scala.Int]] { - def encodeValue(`in₈`: co.blocke.scalajack.json.misc.OptionHolder[scala.Int], `out₆`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₈`, `out₆`) - def decodeValue(`in₉`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.OptionHolder[scala.Int] = r0(`in₉`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.OptionHolder[scala.Int]]) -} -Codec: { - val __co_blocke_scalajack_json_misc_EitherRecipe_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.misc.EitherRecipe[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - if (in.a.==(null)) out.burpNull() else () - in.a match { - case scala.Left(v) => - { - out.label("a") - out.value(v.asInstanceOf[scala.Boolean]) - } - case scala.Right(v) => - { - if (`v₂`.asInstanceOf[scala.util.Either[scala.Option[scala.Int], scala.Predef.String]].==(null)) out.burpNull() else () - `v₂`.asInstanceOf[scala.util.Either[scala.Option[scala.Int], scala.Predef.String]] match { - case scala.Left(v) => - `v₃`.asInstanceOf[scala.Option[scala.Int]] match { - case scala.None => - () - case scala.Some(v) => - { - out.label("a") - out.value(`v₄`) - } - } - case scala.Right(v) => - { - out.label("a") - out.valueEscaped(`v₅`.asInstanceOf[java.lang.String]) - } - } - } - } - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.EitherRecipe[scala.Int] = { - var _a: scala.util.Either[scala.Boolean, scala.util.Either[scala.Option[scala.Int], scala.Predef.String]] = scala.Predef.augmentString("rl").foldRight[scala.Any]((scala.None: scala.Any))(((c: scala.Char, acc: scala.Any) => if (c.==('r')) scala.Right.apply[scala.Nothing, scala.Any](acc) else scala.Left.apply[scala.Any, scala.Nothing](acc))).asInstanceOf[scala.util.Either[scala.Boolean, scala.util.Either[scala.Option[scala.Int], scala.Predef.String]]] - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_EitherRecipe_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.EitherRecipe[scala.Int]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = { - val mark: scala.Int = `in₂`.pos - if (`in₂`.expectNull()) null else scala.util.Try.apply[scala.util.Either[scala.Option[scala.Int], scala.Predef.String]]({ - val `mark₂`: scala.Int = `in₂`.pos - if (`in₂`.expectNull()) null else scala.util.Try.apply[java.lang.String](`in₂`.expectString()) match { - case scala.util.Success(rval) => - scala.Right.apply[scala.Nothing, java.lang.String](rval) - case scala.util.Failure(f) => - `in₂`.revertToPos(`mark₂`) - scala.util.Try.apply[scala.Option[scala.Int]](if (`in₂`.expectNull()) null else scala.Some.apply[scala.Int](`in₂`.expectInt())) match { - case scala.util.Success(lval) => - scala.Left.apply[scala.Option[scala.Int], scala.Nothing](lval) - case scala.util.Failure(_) => - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) - } - } - }) match { - case scala.util.Success(rval) => - scala.Right.apply[scala.Nothing, scala.util.Either[scala.Option[scala.Int], scala.Predef.String]](`rval₂`) - case scala.util.Failure(f) => - `in₂`.revertToPos(mark) - scala.util.Try.apply[scala.Boolean](`in₂`.expectBoolean()) match { - case scala.util.Success(lval) => - scala.Left.apply[scala.Boolean, scala.Nothing](`lval₂`) - case scala.util.Failure(_) => - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) - } - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_EitherRecipe_fields) - } - if (required.&(0).==(0)) new co.blocke.scalajack.json.misc.EitherRecipe[scala.Int][scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(0)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.EitherRecipe[scala.Int]] { - def encodeValue(`in₃`: co.blocke.scalajack.json.misc.EitherRecipe[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.EitherRecipe[scala.Int] = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.EitherRecipe[scala.Int]]) -} -Codec: { - val __co_blocke_scalajack_json_misc_EitherRecipe_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.misc.EitherRecipe[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - if (in.a.==(null)) out.burpNull() else () - in.a match { - case scala.Left(v) => - { - out.label("a") - out.value(v.asInstanceOf[scala.Boolean]) - } - case scala.Right(v) => - { - if (`v₂`.asInstanceOf[scala.util.Either[scala.Option[scala.Int], scala.Predef.String]].==(null)) out.burpNull() else () - `v₂`.asInstanceOf[scala.util.Either[scala.Option[scala.Int], scala.Predef.String]] match { - case scala.Left(v) => - `v₃`.asInstanceOf[scala.Option[scala.Int]] match { - case scala.None => - () - case scala.Some(v) => - { - out.label("a") - out.value(`v₄`) - } - } - case scala.Right(v) => - { - out.label("a") - out.valueEscaped(`v₅`.asInstanceOf[java.lang.String]) - } - } - } - } - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.EitherRecipe[scala.Int] = { - var _a: scala.util.Either[scala.Boolean, scala.util.Either[scala.Option[scala.Int], scala.Predef.String]] = scala.Predef.augmentString("rl").foldRight[scala.Any]((scala.None: scala.Any))(((c: scala.Char, acc: scala.Any) => if (c.==('r')) scala.Right.apply[scala.Nothing, scala.Any](acc) else scala.Left.apply[scala.Any, scala.Nothing](acc))).asInstanceOf[scala.util.Either[scala.Boolean, scala.util.Either[scala.Option[scala.Int], scala.Predef.String]]] - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_EitherRecipe_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.EitherRecipe[scala.Int]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = { - val mark: scala.Int = `in₂`.pos - if (`in₂`.expectNull()) null else scala.util.Try.apply[scala.util.Either[scala.Option[scala.Int], scala.Predef.String]]({ - val `mark₂`: scala.Int = `in₂`.pos - if (`in₂`.expectNull()) null else scala.util.Try.apply[java.lang.String](`in₂`.expectString()) match { - case scala.util.Success(rval) => - scala.Right.apply[scala.Nothing, java.lang.String](rval) - case scala.util.Failure(f) => - `in₂`.revertToPos(`mark₂`) - scala.util.Try.apply[scala.Option[scala.Int]](if (`in₂`.expectNull()) null else scala.Some.apply[scala.Int](`in₂`.expectInt())) match { - case scala.util.Success(lval) => - scala.Left.apply[scala.Option[scala.Int], scala.Nothing](lval) - case scala.util.Failure(_) => - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) - } - } - }) match { - case scala.util.Success(rval) => - scala.Right.apply[scala.Nothing, scala.util.Either[scala.Option[scala.Int], scala.Predef.String]](`rval₂`) - case scala.util.Failure(f) => - `in₂`.revertToPos(mark) - scala.util.Try.apply[scala.Boolean](`in₂`.expectBoolean()) match { - case scala.util.Success(lval) => - scala.Left.apply[scala.Boolean, scala.Nothing](`lval₂`) - case scala.util.Failure(_) => - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) - } - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_EitherRecipe_fields) - } - if (required.&(0).==(0)) new co.blocke.scalajack.json.misc.EitherRecipe[scala.Int][scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(0)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.EitherRecipe[scala.Int]] { - def encodeValue(`in₃`: co.blocke.scalajack.json.misc.EitherRecipe[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.EitherRecipe[scala.Int] = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.EitherRecipe[scala.Int]]) -} -Codec: { - val __co_blocke_scalajack_json_misc_EitherRecipe_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.misc.EitherRecipe[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - if (in.a.==(null)) out.burpNull() else () - in.a match { - case scala.Left(v) => - { - out.label("a") - out.value(v.asInstanceOf[scala.Boolean]) - } - case scala.Right(v) => - { - if (`v₂`.asInstanceOf[scala.util.Either[scala.Option[scala.Int], scala.Predef.String]].==(null)) out.burpNull() else () - `v₂`.asInstanceOf[scala.util.Either[scala.Option[scala.Int], scala.Predef.String]] match { - case scala.Left(v) => - { - out.label("a") - `v₃`.asInstanceOf[scala.Option[scala.Int]] match { - case null => - out.burpNull() - case scala.None => - out.burpNull() - case scala.Some(v) => - val vv: scala.Int = `v₄`.asInstanceOf[scala.Int] - out.value(vv) - } - } - case scala.Right(v) => - { - out.label("a") - out.valueEscaped(`v₅`.asInstanceOf[java.lang.String]) - } - } - } - } - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.EitherRecipe[scala.Int] = { - var _a: scala.util.Either[scala.Boolean, scala.util.Either[scala.Option[scala.Int], scala.Predef.String]] = scala.Predef.augmentString("rl").foldRight[scala.Any]((scala.None: scala.Any))(((c: scala.Char, acc: scala.Any) => if (c.==('r')) scala.Right.apply[scala.Nothing, scala.Any](acc) else scala.Left.apply[scala.Any, scala.Nothing](acc))).asInstanceOf[scala.util.Either[scala.Boolean, scala.util.Either[scala.Option[scala.Int], scala.Predef.String]]] - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_EitherRecipe_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.EitherRecipe[scala.Int]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = { - val mark: scala.Int = `in₂`.pos - if (`in₂`.expectNull()) null else scala.util.Try.apply[scala.util.Either[scala.Option[scala.Int], scala.Predef.String]]({ - val `mark₂`: scala.Int = `in₂`.pos - if (`in₂`.expectNull()) null else scala.util.Try.apply[java.lang.String](`in₂`.expectString()) match { - case scala.util.Success(rval) => - scala.Right.apply[scala.Nothing, java.lang.String](rval) - case scala.util.Failure(f) => - `in₂`.revertToPos(`mark₂`) - scala.util.Try.apply[scala.Option[scala.Int]](if (`in₂`.expectNull()) scala.None else scala.Some.apply[scala.Int](`in₂`.expectInt())) match { - case scala.util.Success(lval) => - scala.Left.apply[scala.Option[scala.Int], scala.Nothing](lval) - case scala.util.Failure(_) => - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) - } - } - }) match { - case scala.util.Success(rval) => - scala.Right.apply[scala.Nothing, scala.util.Either[scala.Option[scala.Int], scala.Predef.String]](`rval₂`) - case scala.util.Failure(f) => - `in₂`.revertToPos(mark) - scala.util.Try.apply[scala.Boolean](`in₂`.expectBoolean()) match { - case scala.util.Success(lval) => - scala.Left.apply[scala.Boolean, scala.Nothing](`lval₂`) - case scala.util.Failure(_) => - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) - } - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_EitherRecipe_fields) - } - if (required.&(0).==(0)) new co.blocke.scalajack.json.misc.EitherRecipe[scala.Int][scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(0)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.EitherRecipe[scala.Int]] { - def encodeValue(`in₃`: co.blocke.scalajack.json.misc.EitherRecipe[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.EitherRecipe[scala.Int] = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.EitherRecipe[scala.Int]]) -} -Codec: { - val __co_blocke_scalajack_json_misc_Person_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("name", "age")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - val __co_blocke_scalajack_json_misc_OptionalHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a", "b", "c", "d", "e", "f", "g", "h", "i", "j")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: scala.Tuple2[java.util.Optional[scala.Int], scala.Predef.String], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startArray() - in._1.asInstanceOf[java.util.Optional[scala.Int]] match { - case null => - out.burpNull() - case o if o.isEmpty() => - out.burpNull() - case o => - val vv: scala.Int = `o₂`.get().asInstanceOf[scala.Int] - out.value(vv) - } - out.valueEscaped(in._2) - out.endArray() - } - def w2(`in₂`: scala.collection.immutable.List[java.util.Optional[scala.Int]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startArray() - `in₂`.foreach[scala.Unit](((i: java.util.Optional[scala.Int]) => i.asInstanceOf[java.util.Optional[scala.Int]] match { - case null => - `out₂`.burpNull() - case o if `o₃`.isEmpty() => - () - case o => - val `vv₂`: scala.Int = `o₄`.get().asInstanceOf[scala.Int] - `out₂`.value(`vv₂`) - })) - `out₂`.endArray() - } - def w3(`in₃`: scala.collection.immutable.Map[scala.Int, java.util.Optional[scala.Int]], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { - `out₃`.startObject() - `in₃`.foreach[scala.Unit](((x$1: scala.Tuple2[scala.Int, java.util.Optional[scala.Int]]) => x$1 match { - case scala.Tuple2(key, value) => - if (value.isEmpty().unary_!) { - `out₃`.maybeComma() - `out₃`.valueStringified(scala.Int.int2long(key)) - `out₃`.colon() - `out₃`.value(value.get()) - } else () - })) - `out₃`.endObject() - } - def w4(`in₄`: co.blocke.scalajack.json.misc.Person, `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₄`.==(null)) `out₄`.burpNull() else { - `out₄`.startObject() - `out₄`.label("name") - `out₄`.valueEscaped(`in₄`.name) - `out₄`.label("age") - `out₄`.value(`in₄`.age) - `out₄`.endObject() - } - def w0(`in₅`: co.blocke.scalajack.json.misc.OptionalHolder[scala.Int], `out₅`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₅`.==(null)) `out₅`.burpNull() else { - `out₅`.startObject() - if (`in₅`.a.isEmpty().unary_!) { - `out₅`.label("a") - `out₅`.value(`in₅`.a.get()) - } else () - `out₅`.label("b") - w1(`in₅`.b, `out₅`) - `out₅`.label("c") - w2(`in₅`.c, `out₅`) - `out₅`.label("d") - w3(`in₅`.d, `out₅`) - if (`in₅`.e.==(null)) `out₅`.burpNull() else { - `out₅`.mark() - scala.util.Try.apply[scala.Unit](if (`in₅`.e.asInstanceOf[java.util.Optional[scala.Int]].isEmpty().unary_!) { - `out₅`.label("e") - `out₅`.value(`in₅`.e.asInstanceOf[java.util.Optional[scala.Int]].get()) - } else ()) match { - case scala.util.Success(_) => - () - case scala.util.Failure(f) => - `out₅`.revert() - { - `out₅`.label("e") - `out₅`.value(`in₅`.e.asInstanceOf[scala.Int]) - } - } - } - if (`in₅`.f.==(null)) `out₅`.burpNull() else { - `out₅`.mark() - scala.util.Try.apply[scala.Unit]({ - `out₅`.label("f") - `out₅`.value(`in₅`.f.asInstanceOf[scala.Int]) - }) match { - case scala.util.Success(_) => - () - case scala.util.Failure(f) => - `out₅`.revert() - if (`in₅`.f.asInstanceOf[java.util.Optional[scala.Int]].isEmpty().unary_!) { - `out₅`.label("f") - `out₅`.value(`in₅`.f.asInstanceOf[java.util.Optional[scala.Int]].get()) - } else () - } - } - if (`in₅`.g.isEmpty().unary_!) if (`in₅`.g.get().isEmpty().unary_!) { - `out₅`.label("g") - `out₅`.value(`in₅`.g.get().get()) - } else () else () - if (`in₅`.h.isEmpty().unary_!) { - `out₅`.label("h") - w4(`in₅`.h.get(), `out₅`) - } else () - if (`in₅`.i.==(null)) `out₅`.burpNull() else () - `in₅`.i match { - case scala.Left(v) => - { - `out₅`.label("i") - `out₅`.value(v.asInstanceOf[scala.Int]) - } - case scala.Right(v) => - if (`v₂`.asInstanceOf[java.util.Optional[scala.Int]].isEmpty().unary_!) { - `out₅`.label("i") - `out₅`.value(`v₂`.asInstanceOf[java.util.Optional[scala.Int]].get()) - } else () - } - if (`in₅`.j.==(null)) `out₅`.burpNull() else () - `in₅`.j match { - case scala.Left(v) => - if (`v₃`.asInstanceOf[java.util.Optional[scala.Int]].isEmpty().unary_!) { - `out₅`.label("j") - `out₅`.value(`v₃`.asInstanceOf[java.util.Optional[scala.Int]].get()) - } else () - case scala.Right(v) => - { - `out₅`.label("j") - `out₅`.value(`v₄`.asInstanceOf[scala.Int]) - } - } - `out₅`.endObject() - } - def r1(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.Person = { - var _name: java.lang.String = "" - var _age: scala.Int = 0 - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₆`.expectFirstObjectField(__co_blocke_scalajack_json_misc_Person_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.Person] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _name = `in₆`.expectString() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("name"), `in₆`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _age = `in₆`.expectInt() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("age"), `in₆`) - case _ => - `in₆`.skipValue() - } - maybeFieldNum = `in₆`.expectObjectField(__co_blocke_scalajack_json_misc_Person_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.misc.Person(_name, _age) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("name", "age")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₆`) - } - } - def r0(`in₇`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.OptionalHolder[scala.Int] = { - var _a: java.util.Optional[scala.Int] = java.util.Optional.empty[java.lang.Object]().asInstanceOf[java.util.Optional[scala.Int]] - var _b: scala.Tuple2[java.util.Optional[scala.Int], scala.Predef.String] = null - var _c: scala.collection.immutable.List[java.util.Optional[scala.Int]] = null - var _d: scala.collection.immutable.Map[scala.Int, java.util.Optional[scala.Int]] = null - var _e: scala.Int | java.util.Optional[scala.Int] = java.util.Optional.empty[java.lang.Object]().asInstanceOf[scala.Int | java.util.Optional[scala.Int]] - var _f: java.util.Optional[scala.Int] | scala.Int = java.util.Optional.empty[java.lang.Object]().asInstanceOf[java.util.Optional[scala.Int] | scala.Int] - var _g: java.util.Optional[java.util.Optional[scala.Int]] = java.util.Optional.empty[java.lang.Object]().asInstanceOf[java.util.Optional[java.util.Optional[scala.Int]]] - var _h: java.util.Optional[co.blocke.scalajack.json.misc.Person] = java.util.Optional.empty[java.lang.Object]().asInstanceOf[java.util.Optional[co.blocke.scalajack.json.misc.Person]] - var _i: scala.util.Either[scala.Int, java.util.Optional[scala.Int]] = scala.Predef.augmentString("r").foldRight[scala.Any]((java.util.Optional.empty[java.lang.Object](): scala.Any))(((c: scala.Char, acc: scala.Any) => if (c.==('r')) scala.Right.apply[scala.Nothing, scala.Any](acc) else scala.Left.apply[scala.Any, scala.Nothing](acc))).asInstanceOf[scala.util.Either[scala.Int, java.util.Optional[scala.Int]]] - var _j: scala.util.Either[java.util.Optional[scala.Int], scala.Int] = scala.Predef.augmentString("l").foldRight[scala.Any]((java.util.Optional.empty[java.lang.Object](): scala.Any))(((`c₂`: scala.Char, `acc₂`: scala.Any) => if (`c₂`.==('r')) scala.Right.apply[scala.Nothing, scala.Any](`acc₂`) else scala.Left.apply[scala.Any, scala.Nothing](`acc₂`))).asInstanceOf[scala.util.Either[java.util.Optional[scala.Int], scala.Int]] - var `required₂`: scala.Int = 1023 - var `maybeFieldNum₂`: scala.Option[scala.Int] = `in₇`.expectFirstObjectField(__co_blocke_scalajack_json_misc_OptionalHolder_fields) - if (`maybeFieldNum₂`.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.OptionalHolder[scala.Int]] else { - while (`maybeFieldNum₂`.isDefined) { - `maybeFieldNum₂`.get match { - case 0 => - if (`required₂`.&(1).!=(0)) { - `required₂` = `required₂`.^(1) - _a = if (`in₇`.expectNull()) null else java.util.Optional.of[scala.Int](`in₇`.expectInt()) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₇`) - case 1 => - if (`required₂`.&(2).!=(0)) { - `required₂` = `required₂`.^(2) - _b = if (`in₇`.expectNull()) null else { - `in₇`.expectToken('[') - val tv: scala.Tuple2[java.util.Optional[scala.Int], scala.Predef.String] = new scala.Tuple2[java.util.Optional[scala.Int], scala.Predef.String][java.util.Optional[scala.Int], java.lang.String](if (`in₇`.expectNull()) java.util.Optional.empty[scala.Int]() else java.util.Optional.of[scala.Int](`in₇`.expectInt()), { - `in₇`.expectToken(',') - `in₇`.expectString() - }) - `in₇`.expectToken(']') - - (tv: scala.Tuple2[java.util.Optional[scala.Int], scala.Predef.String]) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b"), `in₇`) - case 2 => - if (`required₂`.&(4).!=(0)) { - `required₂` = `required₂`.^(4) - _c = { - val parsedArray: scala.collection.mutable.ListBuffer[java.util.Optional[scala.Int]] = `in₇`.expectArray[java.util.Optional[scala.Int]]((() => if (`in₇`.expectNull()) null else java.util.Optional.of[scala.Int](`in₇`.expectInt()))) - - (if (parsedArray.!=(null)) parsedArray.toList else null: scala.collection.immutable.List[java.util.Optional[scala.Int]]) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c"), `in₇`) - case 3 => - if (`required₂`.&(8).!=(0)) { - `required₂` = `required₂`.^(8) - _d = if (`in₇`.expectNull()) null else { - `in₇`.expectToken('{') - `in₇`.parseMap[scala.Int, java.util.Optional[scala.Int]]((() => scala.Predef.augmentString(`in₇`.expectString()).toInt), (() => if (`in₇`.expectNull()) null else java.util.Optional.of[scala.Int](`in₇`.expectInt())), scala.Predef.Map.empty[scala.Int, java.util.Optional[scala.Int]], true) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d"), `in₇`) - case 4 => - if (`required₂`.&(16).!=(0)) { - `required₂` = `required₂`.^(16) - _e = { - val mark: scala.Int = `in₇`.pos - scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { - case scala.util.Success(lval) => - (lval: scala.Int) - case scala.util.Failure(f) => - `in₇`.revertToPos(mark) - scala.util.Try.apply[java.util.Optional[scala.Int]](if (`in₇`.expectNull()) java.util.Optional.empty[scala.Int]() else java.util.Optional.of[scala.Int](`in₇`.expectInt())) match { - case scala.util.Success(rval) => - (rval: java.util.Optional[scala.Int]) - case scala.util.Failure(_) => - `in₇`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₇`) - } - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("e"), `in₇`) - case 5 => - if (`required₂`.&(32).!=(0)) { - `required₂` = `required₂`.^(32) - _f = { - val `mark₂`: scala.Int = `in₇`.pos - scala.util.Try.apply[java.util.Optional[scala.Int]](if (`in₇`.expectNull()) java.util.Optional.empty[scala.Int]() else java.util.Optional.of[scala.Int](`in₇`.expectInt())) match { - case scala.util.Success(lval) => - (`lval₂`: java.util.Optional[scala.Int]) - case scala.util.Failure(f) => - `in₇`.revertToPos(`mark₂`) - scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { - case scala.util.Success(rval) => - (`rval₂`: scala.Int) - case scala.util.Failure(_) => - `in₇`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₇`) - } - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f"), `in₇`) - case 6 => - if (`required₂`.&(64).!=(0)) { - `required₂` = `required₂`.^(64) - _g = if (`in₇`.expectNull()) null else java.util.Optional.of[java.util.Optional[scala.Int]](if (`in₇`.expectNull()) null else java.util.Optional.of[scala.Int](`in₇`.expectInt())) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("g"), `in₇`) - case 7 => - if (`required₂`.&(128).!=(0)) { - `required₂` = `required₂`.^(128) - _h = if (`in₇`.expectNull()) null else java.util.Optional.of[co.blocke.scalajack.json.misc.Person](r1(`in₇`)) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("h"), `in₇`) - case 8 => - if (`required₂`.&(256).!=(0)) { - `required₂` = `required₂`.^(256) - _i = { - val `mark₃`: scala.Int = `in₇`.pos - if (`in₇`.expectNull()) null else scala.util.Try.apply[java.util.Optional[scala.Int]](if (`in₇`.expectNull()) null else java.util.Optional.of[scala.Int](`in₇`.expectInt())) match { - case scala.util.Success(rval) => - scala.Right.apply[scala.Nothing, java.util.Optional[scala.Int]](`rval₃`) - case scala.util.Failure(f) => - `in₇`.revertToPos(`mark₃`) - scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { - case scala.util.Success(lval) => - scala.Left.apply[scala.Int, scala.Nothing](`lval₃`) - case scala.util.Failure(_) => - `in₇`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₇`) - } - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i"), `in₇`) - case 9 => - if (`required₂`.&(512).!=(0)) { - `required₂` = `required₂`.^(512) - _j = { - val `mark₄`: scala.Int = `in₇`.pos - if (`in₇`.expectNull()) null else scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { - case scala.util.Success(rval) => - scala.Right.apply[scala.Nothing, scala.Int](`rval₄`) - case scala.util.Failure(f) => - `in₇`.revertToPos(`mark₄`) - scala.util.Try.apply[java.util.Optional[scala.Int]](if (`in₇`.expectNull()) null else java.util.Optional.of[scala.Int](`in₇`.expectInt())) match { - case scala.util.Success(lval) => - scala.Left.apply[java.util.Optional[scala.Int], scala.Nothing](`lval₄`) - case scala.util.Failure(_) => - `in₇`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₇`) - } - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("j"), `in₇`) - case _ => - `in₇`.skipValue() - } - `maybeFieldNum₂` = `in₇`.expectObjectField(__co_blocke_scalajack_json_misc_OptionalHolder_fields) - } - if (`required₂`.&(14).==(0)) new co.blocke.scalajack.json.misc.OptionalHolder[scala.Int][scala.Int](_a, _b, _c, _d, _e, _f, _g, _h, _i, _j) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a", "b", "c", "d", "e", "f", "g", "h", "i", "j")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(`required₂`.&(14)))), `in₇`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.OptionalHolder[scala.Int]] { - def encodeValue(`in₈`: co.blocke.scalajack.json.misc.OptionalHolder[scala.Int], `out₆`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₈`, `out₆`) - def decodeValue(`in₉`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.OptionalHolder[scala.Int] = r0(`in₉`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.OptionalHolder[scala.Int]]) -} -Codec: { - val __co_blocke_scalajack_json_misc_Person_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("name", "age")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - val __co_blocke_scalajack_json_misc_OptionalHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a", "b", "c", "d", "e", "f", "g", "h", "i", "j")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: scala.Tuple2[java.util.Optional[scala.Int], scala.Predef.String], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startArray() - in._1.asInstanceOf[java.util.Optional[scala.Int]] match { - case null => - out.burpNull() - case o if o.isEmpty() => - out.burpNull() - case o => - val vv: scala.Int = `o₂`.get().asInstanceOf[scala.Int] - out.value(vv) - } - out.valueEscaped(in._2) - out.endArray() - } - def w2(`in₂`: scala.collection.immutable.List[java.util.Optional[scala.Int]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startArray() - `in₂`.foreach[scala.Unit](((i: java.util.Optional[scala.Int]) => i.asInstanceOf[java.util.Optional[scala.Int]] match { - case null => - `out₂`.burpNull() - case o if `o₃`.isEmpty() => - () - case o => - val `vv₂`: scala.Int = `o₄`.get().asInstanceOf[scala.Int] - `out₂`.value(`vv₂`) - })) - `out₂`.endArray() - } - def w3(`in₃`: scala.collection.immutable.Map[scala.Int, java.util.Optional[scala.Int]], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { - `out₃`.startObject() - `in₃`.foreach[scala.Unit](((x$1: scala.Tuple2[scala.Int, java.util.Optional[scala.Int]]) => x$1 match { - case scala.Tuple2(key, value) => - if (value.isEmpty().unary_!) { - `out₃`.maybeComma() - `out₃`.valueStringified(scala.Int.int2long(key)) - `out₃`.colon() - `out₃`.value(value.get()) - } else () - })) - `out₃`.endObject() - } - def w4(`in₄`: co.blocke.scalajack.json.misc.Person, `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₄`.==(null)) `out₄`.burpNull() else { - `out₄`.startObject() - `out₄`.label("name") - `out₄`.valueEscaped(`in₄`.name) - `out₄`.label("age") - `out₄`.value(`in₄`.age) - `out₄`.endObject() - } - def w0(`in₅`: co.blocke.scalajack.json.misc.OptionalHolder[scala.Int], `out₅`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₅`.==(null)) `out₅`.burpNull() else { - `out₅`.startObject() - if (`in₅`.a.isEmpty().unary_!) { - `out₅`.label("a") - `out₅`.value(`in₅`.a.get()) - } else () - `out₅`.label("b") - w1(`in₅`.b, `out₅`) - `out₅`.label("c") - w2(`in₅`.c, `out₅`) - `out₅`.label("d") - w3(`in₅`.d, `out₅`) - if (`in₅`.e.==(null)) `out₅`.burpNull() else { - `out₅`.mark() - scala.util.Try.apply[scala.Unit](if (`in₅`.e.asInstanceOf[java.util.Optional[scala.Int]].isEmpty().unary_!) { - `out₅`.label("e") - `out₅`.value(`in₅`.e.asInstanceOf[java.util.Optional[scala.Int]].get()) - } else ()) match { - case scala.util.Success(_) => - () - case scala.util.Failure(f) => - `out₅`.revert() - { - `out₅`.label("e") - `out₅`.value(`in₅`.e.asInstanceOf[scala.Int]) - } - } - } - if (`in₅`.f.==(null)) `out₅`.burpNull() else { - `out₅`.mark() - scala.util.Try.apply[scala.Unit]({ - `out₅`.label("f") - `out₅`.value(`in₅`.f.asInstanceOf[scala.Int]) - }) match { - case scala.util.Success(_) => - () - case scala.util.Failure(f) => - `out₅`.revert() - if (`in₅`.f.asInstanceOf[java.util.Optional[scala.Int]].isEmpty().unary_!) { - `out₅`.label("f") - `out₅`.value(`in₅`.f.asInstanceOf[java.util.Optional[scala.Int]].get()) - } else () - } - } - if (`in₅`.g.isEmpty().unary_!) if (`in₅`.g.get().isEmpty().unary_!) { - `out₅`.label("g") - `out₅`.value(`in₅`.g.get().get()) - } else () else () - if (`in₅`.h.isEmpty().unary_!) { - `out₅`.label("h") - w4(`in₅`.h.get(), `out₅`) - } else () - if (`in₅`.i.==(null)) `out₅`.burpNull() else () - `in₅`.i match { - case scala.Left(v) => - { - `out₅`.label("i") - `out₅`.value(v.asInstanceOf[scala.Int]) - } - case scala.Right(v) => - if (`v₂`.asInstanceOf[java.util.Optional[scala.Int]].isEmpty().unary_!) { - `out₅`.label("i") - `out₅`.value(`v₂`.asInstanceOf[java.util.Optional[scala.Int]].get()) - } else () - } - if (`in₅`.j.==(null)) `out₅`.burpNull() else () - `in₅`.j match { - case scala.Left(v) => - if (`v₃`.asInstanceOf[java.util.Optional[scala.Int]].isEmpty().unary_!) { - `out₅`.label("j") - `out₅`.value(`v₃`.asInstanceOf[java.util.Optional[scala.Int]].get()) - } else () - case scala.Right(v) => - { - `out₅`.label("j") - `out₅`.value(`v₄`.asInstanceOf[scala.Int]) - } - } - `out₅`.endObject() - } - def r1(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.Person = { - var _name: java.lang.String = "" - var _age: scala.Int = 0 - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₆`.expectFirstObjectField(__co_blocke_scalajack_json_misc_Person_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.Person] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _name = `in₆`.expectString() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("name"), `in₆`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _age = `in₆`.expectInt() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("age"), `in₆`) - case _ => - `in₆`.skipValue() - } - maybeFieldNum = `in₆`.expectObjectField(__co_blocke_scalajack_json_misc_Person_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.misc.Person(_name, _age) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("name", "age")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₆`) - } - } - def r0(`in₇`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.OptionalHolder[scala.Int] = { - var _a: java.util.Optional[scala.Int] = java.util.Optional.empty[java.lang.Object]().asInstanceOf[java.util.Optional[scala.Int]] - var _b: scala.Tuple2[java.util.Optional[scala.Int], scala.Predef.String] = null - var _c: scala.collection.immutable.List[java.util.Optional[scala.Int]] = null - var _d: scala.collection.immutable.Map[scala.Int, java.util.Optional[scala.Int]] = null - var _e: scala.Int | java.util.Optional[scala.Int] = java.util.Optional.empty[java.lang.Object]().asInstanceOf[scala.Int | java.util.Optional[scala.Int]] - var _f: java.util.Optional[scala.Int] | scala.Int = java.util.Optional.empty[java.lang.Object]().asInstanceOf[java.util.Optional[scala.Int] | scala.Int] - var _g: java.util.Optional[java.util.Optional[scala.Int]] = java.util.Optional.empty[java.lang.Object]().asInstanceOf[java.util.Optional[java.util.Optional[scala.Int]]] - var _h: java.util.Optional[co.blocke.scalajack.json.misc.Person] = java.util.Optional.empty[java.lang.Object]().asInstanceOf[java.util.Optional[co.blocke.scalajack.json.misc.Person]] - var _i: scala.util.Either[scala.Int, java.util.Optional[scala.Int]] = scala.Predef.augmentString("r").foldRight[scala.Any]((java.util.Optional.empty[java.lang.Object](): scala.Any))(((c: scala.Char, acc: scala.Any) => if (c.==('r')) scala.Right.apply[scala.Nothing, scala.Any](acc) else scala.Left.apply[scala.Any, scala.Nothing](acc))).asInstanceOf[scala.util.Either[scala.Int, java.util.Optional[scala.Int]]] - var _j: scala.util.Either[java.util.Optional[scala.Int], scala.Int] = scala.Predef.augmentString("l").foldRight[scala.Any]((java.util.Optional.empty[java.lang.Object](): scala.Any))(((`c₂`: scala.Char, `acc₂`: scala.Any) => if (`c₂`.==('r')) scala.Right.apply[scala.Nothing, scala.Any](`acc₂`) else scala.Left.apply[scala.Any, scala.Nothing](`acc₂`))).asInstanceOf[scala.util.Either[java.util.Optional[scala.Int], scala.Int]] - var `required₂`: scala.Int = 1023 - var `maybeFieldNum₂`: scala.Option[scala.Int] = `in₇`.expectFirstObjectField(__co_blocke_scalajack_json_misc_OptionalHolder_fields) - if (`maybeFieldNum₂`.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.OptionalHolder[scala.Int]] else { - while (`maybeFieldNum₂`.isDefined) { - `maybeFieldNum₂`.get match { - case 0 => - if (`required₂`.&(1).!=(0)) { - `required₂` = `required₂`.^(1) - _a = if (`in₇`.expectNull()) null else java.util.Optional.of[scala.Int](`in₇`.expectInt()) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₇`) - case 1 => - if (`required₂`.&(2).!=(0)) { - `required₂` = `required₂`.^(2) - _b = if (`in₇`.expectNull()) null else { - `in₇`.expectToken('[') - val tv: scala.Tuple2[java.util.Optional[scala.Int], scala.Predef.String] = new scala.Tuple2[java.util.Optional[scala.Int], scala.Predef.String][java.util.Optional[scala.Int], java.lang.String](if (`in₇`.expectNull()) java.util.Optional.empty[scala.Int]() else java.util.Optional.of[scala.Int](`in₇`.expectInt()), { - `in₇`.expectToken(',') - `in₇`.expectString() - }) - `in₇`.expectToken(']') - - (tv: scala.Tuple2[java.util.Optional[scala.Int], scala.Predef.String]) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b"), `in₇`) - case 2 => - if (`required₂`.&(4).!=(0)) { - `required₂` = `required₂`.^(4) - _c = { - val parsedArray: scala.collection.mutable.ListBuffer[java.util.Optional[scala.Int]] = `in₇`.expectArray[java.util.Optional[scala.Int]]((() => if (`in₇`.expectNull()) null else java.util.Optional.of[scala.Int](`in₇`.expectInt()))) - - (if (parsedArray.!=(null)) parsedArray.toList else null: scala.collection.immutable.List[java.util.Optional[scala.Int]]) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c"), `in₇`) - case 3 => - if (`required₂`.&(8).!=(0)) { - `required₂` = `required₂`.^(8) - _d = if (`in₇`.expectNull()) null else { - `in₇`.expectToken('{') - `in₇`.parseMap[scala.Int, java.util.Optional[scala.Int]]((() => scala.Predef.augmentString(`in₇`.expectString()).toInt), (() => if (`in₇`.expectNull()) null else java.util.Optional.of[scala.Int](`in₇`.expectInt())), scala.Predef.Map.empty[scala.Int, java.util.Optional[scala.Int]], true) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d"), `in₇`) - case 4 => - if (`required₂`.&(16).!=(0)) { - `required₂` = `required₂`.^(16) - _e = { - val mark: scala.Int = `in₇`.pos - scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { - case scala.util.Success(lval) => - (lval: scala.Int) - case scala.util.Failure(f) => - `in₇`.revertToPos(mark) - scala.util.Try.apply[java.util.Optional[scala.Int]](if (`in₇`.expectNull()) java.util.Optional.empty[scala.Int]() else java.util.Optional.of[scala.Int](`in₇`.expectInt())) match { - case scala.util.Success(rval) => - (rval: java.util.Optional[scala.Int]) - case scala.util.Failure(_) => - `in₇`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₇`) - } - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("e"), `in₇`) - case 5 => - if (`required₂`.&(32).!=(0)) { - `required₂` = `required₂`.^(32) - _f = { - val `mark₂`: scala.Int = `in₇`.pos - scala.util.Try.apply[java.util.Optional[scala.Int]](if (`in₇`.expectNull()) java.util.Optional.empty[scala.Int]() else java.util.Optional.of[scala.Int](`in₇`.expectInt())) match { - case scala.util.Success(lval) => - (`lval₂`: java.util.Optional[scala.Int]) - case scala.util.Failure(f) => - `in₇`.revertToPos(`mark₂`) - scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { - case scala.util.Success(rval) => - (`rval₂`: scala.Int) - case scala.util.Failure(_) => - `in₇`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₇`) - } - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f"), `in₇`) - case 6 => - if (`required₂`.&(64).!=(0)) { - `required₂` = `required₂`.^(64) - _g = if (`in₇`.expectNull()) null else java.util.Optional.of[java.util.Optional[scala.Int]](if (`in₇`.expectNull()) null else java.util.Optional.of[scala.Int](`in₇`.expectInt())) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("g"), `in₇`) - case 7 => - if (`required₂`.&(128).!=(0)) { - `required₂` = `required₂`.^(128) - _h = if (`in₇`.expectNull()) null else java.util.Optional.of[co.blocke.scalajack.json.misc.Person](r1(`in₇`)) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("h"), `in₇`) - case 8 => - if (`required₂`.&(256).!=(0)) { - `required₂` = `required₂`.^(256) - _i = { - val `mark₃`: scala.Int = `in₇`.pos - if (`in₇`.expectNull()) null else scala.util.Try.apply[java.util.Optional[scala.Int]](if (`in₇`.expectNull()) null else java.util.Optional.of[scala.Int](`in₇`.expectInt())) match { - case scala.util.Success(rval) => - scala.Right.apply[scala.Nothing, java.util.Optional[scala.Int]](`rval₃`) - case scala.util.Failure(f) => - `in₇`.revertToPos(`mark₃`) - scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { - case scala.util.Success(lval) => - scala.Left.apply[scala.Int, scala.Nothing](`lval₃`) - case scala.util.Failure(_) => - `in₇`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₇`) - } - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i"), `in₇`) - case 9 => - if (`required₂`.&(512).!=(0)) { - `required₂` = `required₂`.^(512) - _j = { - val `mark₄`: scala.Int = `in₇`.pos - if (`in₇`.expectNull()) null else scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { - case scala.util.Success(rval) => - scala.Right.apply[scala.Nothing, scala.Int](`rval₄`) - case scala.util.Failure(f) => - `in₇`.revertToPos(`mark₄`) - scala.util.Try.apply[java.util.Optional[scala.Int]](if (`in₇`.expectNull()) null else java.util.Optional.of[scala.Int](`in₇`.expectInt())) match { - case scala.util.Success(lval) => - scala.Left.apply[java.util.Optional[scala.Int], scala.Nothing](`lval₄`) - case scala.util.Failure(_) => - `in₇`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₇`) - } - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("j"), `in₇`) - case _ => - `in₇`.skipValue() - } - `maybeFieldNum₂` = `in₇`.expectObjectField(__co_blocke_scalajack_json_misc_OptionalHolder_fields) - } - if (`required₂`.&(14).==(0)) new co.blocke.scalajack.json.misc.OptionalHolder[scala.Int][scala.Int](_a, _b, _c, _d, _e, _f, _g, _h, _i, _j) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a", "b", "c", "d", "e", "f", "g", "h", "i", "j")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(`required₂`.&(14)))), `in₇`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.OptionalHolder[scala.Int]] { - def encodeValue(`in₈`: co.blocke.scalajack.json.misc.OptionalHolder[scala.Int], `out₆`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₈`, `out₆`) - def decodeValue(`in₉`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.OptionalHolder[scala.Int] = r0(`in₉`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.OptionalHolder[scala.Int]]) -} -Codec: { - val __co_blocke_scalajack_json_misc_Person_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("name", "age")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - val __co_blocke_scalajack_json_misc_OptionalHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a", "b", "c", "d", "e", "f", "g", "h", "i", "j")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: scala.Tuple2[java.util.Optional[scala.Int], scala.Predef.String], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startArray() - in._1.asInstanceOf[java.util.Optional[scala.Int]] match { - case null => - out.burpNull() - case o if o.isEmpty() => - out.burpNull() - case o => - val vv: scala.Int = `o₂`.get().asInstanceOf[scala.Int] - out.value(vv) - } - out.valueEscaped(in._2) - out.endArray() - } - def w2(`in₂`: scala.collection.immutable.List[java.util.Optional[scala.Int]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startArray() - `in₂`.foreach[scala.Unit](((i: java.util.Optional[scala.Int]) => i.asInstanceOf[java.util.Optional[scala.Int]] match { - case null => - `out₂`.burpNull() - case o if `o₃`.isEmpty() => - `out₂`.burpNull() - case o => - val `vv₂`: scala.Int = `o₄`.get().asInstanceOf[scala.Int] - `out₂`.value(`vv₂`) - })) - `out₂`.endArray() - } - def w3(`in₃`: scala.collection.immutable.Map[scala.Int, java.util.Optional[scala.Int]], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { - `out₃`.startObject() - `in₃`.foreach[scala.Unit](((x$1: scala.Tuple2[scala.Int, java.util.Optional[scala.Int]]) => x$1 match { - case scala.Tuple2(key, value) => - { - `out₃`.maybeComma() - `out₃`.valueStringified(scala.Int.int2long(key)) - `out₃`.colon() - value.asInstanceOf[java.util.Optional[scala.Int]] match { - case null => - `out₃`.burpNull() - case o if `o₅`.isEmpty() => - `out₃`.burpNull() - case o => - val `vv₃`: scala.Int = `o₆`.get().asInstanceOf[scala.Int] - `out₃`.value(`vv₃`) - } - } - })) - `out₃`.endObject() - } - def w4(`in₄`: co.blocke.scalajack.json.misc.Person, `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₄`.==(null)) `out₄`.burpNull() else { - `out₄`.startObject() - `out₄`.label("name") - `out₄`.valueEscaped(`in₄`.name) - `out₄`.label("age") - `out₄`.value(`in₄`.age) - `out₄`.endObject() - } - def w0(`in₅`: co.blocke.scalajack.json.misc.OptionalHolder[scala.Int], `out₅`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₅`.==(null)) `out₅`.burpNull() else { - `out₅`.startObject() - `out₅`.label("a") - `in₅`.a.asInstanceOf[java.util.Optional[scala.Int]] match { - case null => - `out₅`.burpNull() - case o if `o₇`.isEmpty() => - `out₅`.burpNull() - case o => - val `vv₄`: scala.Int = `o₈`.get().asInstanceOf[scala.Int] - `out₅`.value(`vv₄`) - } - `out₅`.label("b") - w1(`in₅`.b, `out₅`) - `out₅`.label("c") - w2(`in₅`.c, `out₅`) - `out₅`.label("d") - w3(`in₅`.d, `out₅`) - if (`in₅`.e.==(null)) `out₅`.burpNull() else { - `out₅`.mark() - scala.util.Try.apply[scala.Unit]({ - `out₅`.label("e") - `in₅`.e.asInstanceOf[java.util.Optional[scala.Int]].asInstanceOf[java.util.Optional[scala.Int]] match { - case null => - `out₅`.burpNull() - case o if `o₉`.isEmpty() => - `out₅`.burpNull() - case o => - val `vv₅`: scala.Int = `o₁₀`.get().asInstanceOf[scala.Int] - `out₅`.value(`vv₅`) - } - }) match { - case scala.util.Success(_) => - () - case scala.util.Failure(f) => - `out₅`.revert() - { - `out₅`.label("e") - `out₅`.value(`in₅`.e.asInstanceOf[scala.Int]) - } - } - } - if (`in₅`.f.==(null)) `out₅`.burpNull() else { - `out₅`.mark() - scala.util.Try.apply[scala.Unit]({ - `out₅`.label("f") - `out₅`.value(`in₅`.f.asInstanceOf[scala.Int]) - }) match { - case scala.util.Success(_) => - () - case scala.util.Failure(f) => - `out₅`.revert() - { - `out₅`.label("f") - `in₅`.f.asInstanceOf[java.util.Optional[scala.Int]].asInstanceOf[java.util.Optional[scala.Int]] match { - case null => - `out₅`.burpNull() - case o if `o₁₁`.isEmpty() => - `out₅`.burpNull() - case o => - val `vv₆`: scala.Int = `o₁₂`.get().asInstanceOf[scala.Int] - `out₅`.value(`vv₆`) - } - } - } - } - `out₅`.label("g") - `in₅`.g.asInstanceOf[java.util.Optional[java.util.Optional[scala.Int]]] match { - case null => - `out₅`.burpNull() - case o if `o₁₃`.isEmpty() => - `out₅`.burpNull() - case o => - val `vv₇`: java.util.Optional[scala.Int] = `o₁₄`.get().asInstanceOf[java.util.Optional[scala.Int]] - `vv₇`.asInstanceOf[java.util.Optional[scala.Int]] match { - case null => - `out₅`.burpNull() - case o if `o₁₅`.isEmpty() => - `out₅`.burpNull() - case o => - val `vv₈`: scala.Int = `o₁₆`.get().asInstanceOf[scala.Int] - `out₅`.value(`vv₈`) - } - } - `out₅`.label("h") - `in₅`.h.asInstanceOf[java.util.Optional[co.blocke.scalajack.json.misc.Person]] match { - case null => - `out₅`.burpNull() - case o if `o₁₇`.isEmpty() => - `out₅`.burpNull() - case o => - val `vv₉`: co.blocke.scalajack.json.misc.Person = `o₁₈`.get().asInstanceOf[co.blocke.scalajack.json.misc.Person] - w4(`vv₉`, `out₅`) - } - if (`in₅`.i.==(null)) `out₅`.burpNull() else () - `in₅`.i match { - case scala.Left(v) => - { - `out₅`.label("i") - `out₅`.value(v.asInstanceOf[scala.Int]) - } - case scala.Right(v) => - { - `out₅`.label("i") - `v₂`.asInstanceOf[java.util.Optional[scala.Int]].asInstanceOf[java.util.Optional[scala.Int]] match { - case null => - `out₅`.burpNull() - case o if `o₁₉`.isEmpty() => - `out₅`.burpNull() - case o => - val `vv₁₀`: scala.Int = `o₂₀`.get().asInstanceOf[scala.Int] - `out₅`.value(`vv₁₀`) - } - } - } - if (`in₅`.j.==(null)) `out₅`.burpNull() else () - `in₅`.j match { - case scala.Left(v) => - { - `out₅`.label("j") - `v₃`.asInstanceOf[java.util.Optional[scala.Int]].asInstanceOf[java.util.Optional[scala.Int]] match { - case null => - `out₅`.burpNull() - case o if `o₂₁`.isEmpty() => - `out₅`.burpNull() - case o => - val `vv₁₁`: scala.Int = `o₂₂`.get().asInstanceOf[scala.Int] - `out₅`.value(`vv₁₁`) - } - } - case scala.Right(v) => - { - `out₅`.label("j") - `out₅`.value(`v₄`.asInstanceOf[scala.Int]) - } - } - `out₅`.endObject() - } - def r1(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.Person = { - var _name: java.lang.String = "" - var _age: scala.Int = 0 - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₆`.expectFirstObjectField(__co_blocke_scalajack_json_misc_Person_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.Person] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _name = `in₆`.expectString() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("name"), `in₆`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _age = `in₆`.expectInt() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("age"), `in₆`) - case _ => - `in₆`.skipValue() - } - maybeFieldNum = `in₆`.expectObjectField(__co_blocke_scalajack_json_misc_Person_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.misc.Person(_name, _age) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("name", "age")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₆`) - } - } - def r0(`in₇`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.OptionalHolder[scala.Int] = { - var _a: java.util.Optional[scala.Int] = java.util.Optional.empty[java.lang.Object]().asInstanceOf[java.util.Optional[scala.Int]] - var _b: scala.Tuple2[java.util.Optional[scala.Int], scala.Predef.String] = null - var _c: scala.collection.immutable.List[java.util.Optional[scala.Int]] = null - var _d: scala.collection.immutable.Map[scala.Int, java.util.Optional[scala.Int]] = null - var _e: scala.Int | java.util.Optional[scala.Int] = java.util.Optional.empty[java.lang.Object]().asInstanceOf[scala.Int | java.util.Optional[scala.Int]] - var _f: java.util.Optional[scala.Int] | scala.Int = java.util.Optional.empty[java.lang.Object]().asInstanceOf[java.util.Optional[scala.Int] | scala.Int] - var _g: java.util.Optional[java.util.Optional[scala.Int]] = java.util.Optional.empty[java.lang.Object]().asInstanceOf[java.util.Optional[java.util.Optional[scala.Int]]] - var _h: java.util.Optional[co.blocke.scalajack.json.misc.Person] = java.util.Optional.empty[java.lang.Object]().asInstanceOf[java.util.Optional[co.blocke.scalajack.json.misc.Person]] - var _i: scala.util.Either[scala.Int, java.util.Optional[scala.Int]] = scala.Predef.augmentString("r").foldRight[scala.Any]((java.util.Optional.empty[java.lang.Object](): scala.Any))(((c: scala.Char, acc: scala.Any) => if (c.==('r')) scala.Right.apply[scala.Nothing, scala.Any](acc) else scala.Left.apply[scala.Any, scala.Nothing](acc))).asInstanceOf[scala.util.Either[scala.Int, java.util.Optional[scala.Int]]] - var _j: scala.util.Either[java.util.Optional[scala.Int], scala.Int] = scala.Predef.augmentString("l").foldRight[scala.Any]((java.util.Optional.empty[java.lang.Object](): scala.Any))(((`c₂`: scala.Char, `acc₂`: scala.Any) => if (`c₂`.==('r')) scala.Right.apply[scala.Nothing, scala.Any](`acc₂`) else scala.Left.apply[scala.Any, scala.Nothing](`acc₂`))).asInstanceOf[scala.util.Either[java.util.Optional[scala.Int], scala.Int]] - var `required₂`: scala.Int = 1023 - var `maybeFieldNum₂`: scala.Option[scala.Int] = `in₇`.expectFirstObjectField(__co_blocke_scalajack_json_misc_OptionalHolder_fields) - if (`maybeFieldNum₂`.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.OptionalHolder[scala.Int]] else { - while (`maybeFieldNum₂`.isDefined) { - `maybeFieldNum₂`.get match { - case 0 => - if (`required₂`.&(1).!=(0)) { - `required₂` = `required₂`.^(1) - _a = if (`in₇`.expectNull()) java.util.Optional.empty[scala.Int]() else java.util.Optional.of[scala.Int](`in₇`.expectInt()) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₇`) - case 1 => - if (`required₂`.&(2).!=(0)) { - `required₂` = `required₂`.^(2) - _b = if (`in₇`.expectNull()) null else { - `in₇`.expectToken('[') - val tv: scala.Tuple2[java.util.Optional[scala.Int], scala.Predef.String] = new scala.Tuple2[java.util.Optional[scala.Int], scala.Predef.String][java.util.Optional[scala.Int], java.lang.String](if (`in₇`.expectNull()) java.util.Optional.empty[scala.Int]() else java.util.Optional.of[scala.Int](`in₇`.expectInt()), { - `in₇`.expectToken(',') - `in₇`.expectString() - }) - `in₇`.expectToken(']') - - (tv: scala.Tuple2[java.util.Optional[scala.Int], scala.Predef.String]) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b"), `in₇`) - case 2 => - if (`required₂`.&(4).!=(0)) { - `required₂` = `required₂`.^(4) - _c = { - val parsedArray: scala.collection.mutable.ListBuffer[java.util.Optional[scala.Int]] = `in₇`.expectArray[java.util.Optional[scala.Int]]((() => if (`in₇`.expectNull()) java.util.Optional.empty[scala.Int]() else java.util.Optional.of[scala.Int](`in₇`.expectInt()))) - - (if (parsedArray.!=(null)) parsedArray.toList else null: scala.collection.immutable.List[java.util.Optional[scala.Int]]) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c"), `in₇`) - case 3 => - if (`required₂`.&(8).!=(0)) { - `required₂` = `required₂`.^(8) - _d = if (`in₇`.expectNull()) null else { - `in₇`.expectToken('{') - `in₇`.parseMap[scala.Int, java.util.Optional[scala.Int]]((() => scala.Predef.augmentString(`in₇`.expectString()).toInt), (() => if (`in₇`.expectNull()) java.util.Optional.empty[scala.Int]() else java.util.Optional.of[scala.Int](`in₇`.expectInt())), scala.Predef.Map.empty[scala.Int, java.util.Optional[scala.Int]], true) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d"), `in₇`) - case 4 => - if (`required₂`.&(16).!=(0)) { - `required₂` = `required₂`.^(16) - _e = { - val mark: scala.Int = `in₇`.pos - scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { - case scala.util.Success(lval) => - (lval: scala.Int) - case scala.util.Failure(f) => - `in₇`.revertToPos(mark) - scala.util.Try.apply[java.util.Optional[scala.Int]](if (`in₇`.expectNull()) java.util.Optional.empty[scala.Int]() else java.util.Optional.of[scala.Int](`in₇`.expectInt())) match { - case scala.util.Success(rval) => - (rval: java.util.Optional[scala.Int]) - case scala.util.Failure(_) => - `in₇`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₇`) - } - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("e"), `in₇`) - case 5 => - if (`required₂`.&(32).!=(0)) { - `required₂` = `required₂`.^(32) - _f = { - val `mark₂`: scala.Int = `in₇`.pos - scala.util.Try.apply[java.util.Optional[scala.Int]](if (`in₇`.expectNull()) java.util.Optional.empty[scala.Int]() else java.util.Optional.of[scala.Int](`in₇`.expectInt())) match { - case scala.util.Success(lval) => - (`lval₂`: java.util.Optional[scala.Int]) - case scala.util.Failure(f) => - `in₇`.revertToPos(`mark₂`) - scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { - case scala.util.Success(rval) => - (`rval₂`: scala.Int) - case scala.util.Failure(_) => - `in₇`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Union type", `in₇`) - } - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f"), `in₇`) - case 6 => - if (`required₂`.&(64).!=(0)) { - `required₂` = `required₂`.^(64) - _g = if (`in₇`.expectNull()) java.util.Optional.empty[java.util.Optional[scala.Int]]() else java.util.Optional.of[java.util.Optional[scala.Int]](if (`in₇`.expectNull()) java.util.Optional.empty[scala.Int]() else java.util.Optional.of[scala.Int](`in₇`.expectInt())) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("g"), `in₇`) - case 7 => - if (`required₂`.&(128).!=(0)) { - `required₂` = `required₂`.^(128) - _h = if (`in₇`.expectNull()) java.util.Optional.empty[co.blocke.scalajack.json.misc.Person]() else java.util.Optional.of[co.blocke.scalajack.json.misc.Person](r1(`in₇`)) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("h"), `in₇`) - case 8 => - if (`required₂`.&(256).!=(0)) { - `required₂` = `required₂`.^(256) - _i = { - val `mark₃`: scala.Int = `in₇`.pos - if (`in₇`.expectNull()) null else scala.util.Try.apply[java.util.Optional[scala.Int]](if (`in₇`.expectNull()) java.util.Optional.empty[scala.Int]() else java.util.Optional.of[scala.Int](`in₇`.expectInt())) match { - case scala.util.Success(rval) => - scala.Right.apply[scala.Nothing, java.util.Optional[scala.Int]](`rval₃`) - case scala.util.Failure(f) => - `in₇`.revertToPos(`mark₃`) - scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { - case scala.util.Success(lval) => - scala.Left.apply[scala.Int, scala.Nothing](`lval₃`) - case scala.util.Failure(_) => - `in₇`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₇`) - } - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i"), `in₇`) - case 9 => - if (`required₂`.&(512).!=(0)) { - `required₂` = `required₂`.^(512) - _j = { - val `mark₄`: scala.Int = `in₇`.pos - if (`in₇`.expectNull()) null else scala.util.Try.apply[scala.Int](`in₇`.expectInt()) match { - case scala.util.Success(rval) => - scala.Right.apply[scala.Nothing, scala.Int](`rval₄`) - case scala.util.Failure(f) => - `in₇`.revertToPos(`mark₄`) - scala.util.Try.apply[java.util.Optional[scala.Int]](if (`in₇`.expectNull()) java.util.Optional.empty[scala.Int]() else java.util.Optional.of[scala.Int](`in₇`.expectInt())) match { - case scala.util.Success(lval) => - scala.Left.apply[java.util.Optional[scala.Int], scala.Nothing](`lval₄`) - case scala.util.Failure(_) => - `in₇`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₇`) - } - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("j"), `in₇`) - case _ => - `in₇`.skipValue() - } - `maybeFieldNum₂` = `in₇`.expectObjectField(__co_blocke_scalajack_json_misc_OptionalHolder_fields) - } - if (`required₂`.&(14).==(0)) new co.blocke.scalajack.json.misc.OptionalHolder[scala.Int][scala.Int](_a, _b, _c, _d, _e, _f, _g, _h, _i, _j) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a", "b", "c", "d", "e", "f", "g", "h", "i", "j")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(`required₂`.&(14)))), `in₇`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.OptionalHolder[scala.Int]] { - def encodeValue(`in₈`: co.blocke.scalajack.json.misc.OptionalHolder[scala.Int], `out₆`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₈`, `out₆`) - def decodeValue(`in₉`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.OptionalHolder[scala.Int] = r0(`in₉`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.OptionalHolder[scala.Int]]) -} -Codec: { - val __co_blocke_scalajack_json_misc_EitherRecipeJ_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - if (in.a.==(null)) out.burpNull() else () - in.a match { - case scala.Left(v) => - { - out.label("a") - out.value(v.asInstanceOf[scala.Boolean]) - } - case scala.Right(v) => - { - if (`v₂`.asInstanceOf[scala.util.Either[java.util.Optional[scala.Int], scala.Predef.String]].==(null)) out.burpNull() else () - `v₂`.asInstanceOf[scala.util.Either[java.util.Optional[scala.Int], scala.Predef.String]] match { - case scala.Left(v) => - if (`v₃`.asInstanceOf[java.util.Optional[scala.Int]].isEmpty().unary_!) { - out.label("a") - out.value(`v₃`.asInstanceOf[java.util.Optional[scala.Int]].get()) - } else () - case scala.Right(v) => - { - out.label("a") - out.valueEscaped(`v₄`.asInstanceOf[java.lang.String]) - } - } - } - } - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int] = { - var _a: scala.util.Either[scala.Boolean, scala.util.Either[java.util.Optional[scala.Int], scala.Predef.String]] = scala.Predef.augmentString("rl").foldRight[scala.Any]((java.util.Optional.empty[java.lang.Object](): scala.Any))(((c: scala.Char, acc: scala.Any) => if (c.==('r')) scala.Right.apply[scala.Nothing, scala.Any](acc) else scala.Left.apply[scala.Any, scala.Nothing](acc))).asInstanceOf[scala.util.Either[scala.Boolean, scala.util.Either[java.util.Optional[scala.Int], scala.Predef.String]]] - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_EitherRecipeJ_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = { - val mark: scala.Int = `in₂`.pos - if (`in₂`.expectNull()) null else scala.util.Try.apply[scala.util.Either[java.util.Optional[scala.Int], scala.Predef.String]]({ - val `mark₂`: scala.Int = `in₂`.pos - if (`in₂`.expectNull()) null else scala.util.Try.apply[java.lang.String](`in₂`.expectString()) match { - case scala.util.Success(rval) => - scala.Right.apply[scala.Nothing, java.lang.String](rval) - case scala.util.Failure(f) => - `in₂`.revertToPos(`mark₂`) - scala.util.Try.apply[java.util.Optional[scala.Int]](if (`in₂`.expectNull()) null else java.util.Optional.of[scala.Int](`in₂`.expectInt())) match { - case scala.util.Success(lval) => - scala.Left.apply[java.util.Optional[scala.Int], scala.Nothing](lval) - case scala.util.Failure(_) => - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) - } - } - }) match { - case scala.util.Success(rval) => - scala.Right.apply[scala.Nothing, scala.util.Either[java.util.Optional[scala.Int], scala.Predef.String]](`rval₂`) - case scala.util.Failure(f) => - `in₂`.revertToPos(mark) - scala.util.Try.apply[scala.Boolean](`in₂`.expectBoolean()) match { - case scala.util.Success(lval) => - scala.Left.apply[scala.Boolean, scala.Nothing](`lval₂`) - case scala.util.Failure(_) => - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) - } - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_EitherRecipeJ_fields) - } - if (required.&(0).==(0)) new co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int][scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(0)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int]] { - def encodeValue(`in₃`: co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int] = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int]]) -} -Codec: { - val __co_blocke_scalajack_json_misc_EitherRecipeJ_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - if (in.a.==(null)) out.burpNull() else () - in.a match { - case scala.Left(v) => - { - out.label("a") - out.value(v.asInstanceOf[scala.Boolean]) - } - case scala.Right(v) => - { - if (`v₂`.asInstanceOf[scala.util.Either[java.util.Optional[scala.Int], scala.Predef.String]].==(null)) out.burpNull() else () - `v₂`.asInstanceOf[scala.util.Either[java.util.Optional[scala.Int], scala.Predef.String]] match { - case scala.Left(v) => - if (`v₃`.asInstanceOf[java.util.Optional[scala.Int]].isEmpty().unary_!) { - out.label("a") - out.value(`v₃`.asInstanceOf[java.util.Optional[scala.Int]].get()) - } else () - case scala.Right(v) => - { - out.label("a") - out.valueEscaped(`v₄`.asInstanceOf[java.lang.String]) - } - } - } - } - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int] = { - var _a: scala.util.Either[scala.Boolean, scala.util.Either[java.util.Optional[scala.Int], scala.Predef.String]] = scala.Predef.augmentString("rl").foldRight[scala.Any]((java.util.Optional.empty[java.lang.Object](): scala.Any))(((c: scala.Char, acc: scala.Any) => if (c.==('r')) scala.Right.apply[scala.Nothing, scala.Any](acc) else scala.Left.apply[scala.Any, scala.Nothing](acc))).asInstanceOf[scala.util.Either[scala.Boolean, scala.util.Either[java.util.Optional[scala.Int], scala.Predef.String]]] - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_EitherRecipeJ_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = { - val mark: scala.Int = `in₂`.pos - if (`in₂`.expectNull()) null else scala.util.Try.apply[scala.util.Either[java.util.Optional[scala.Int], scala.Predef.String]]({ - val `mark₂`: scala.Int = `in₂`.pos - if (`in₂`.expectNull()) null else scala.util.Try.apply[java.lang.String](`in₂`.expectString()) match { - case scala.util.Success(rval) => - scala.Right.apply[scala.Nothing, java.lang.String](rval) - case scala.util.Failure(f) => - `in₂`.revertToPos(`mark₂`) - scala.util.Try.apply[java.util.Optional[scala.Int]](if (`in₂`.expectNull()) null else java.util.Optional.of[scala.Int](`in₂`.expectInt())) match { - case scala.util.Success(lval) => - scala.Left.apply[java.util.Optional[scala.Int], scala.Nothing](lval) - case scala.util.Failure(_) => - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) - } - } - }) match { - case scala.util.Success(rval) => - scala.Right.apply[scala.Nothing, scala.util.Either[java.util.Optional[scala.Int], scala.Predef.String]](`rval₂`) - case scala.util.Failure(f) => - `in₂`.revertToPos(mark) - scala.util.Try.apply[scala.Boolean](`in₂`.expectBoolean()) match { - case scala.util.Success(lval) => - scala.Left.apply[scala.Boolean, scala.Nothing](`lval₂`) - case scala.util.Failure(_) => - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) - } - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_EitherRecipeJ_fields) - } - if (required.&(0).==(0)) new co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int][scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(0)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int]] { - def encodeValue(`in₃`: co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int] = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int]]) -} -Codec: { - val __co_blocke_scalajack_json_misc_EitherRecipeJ_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - if (in.a.==(null)) out.burpNull() else () - in.a match { - case scala.Left(v) => - { - out.label("a") - out.value(v.asInstanceOf[scala.Boolean]) - } - case scala.Right(v) => - { - if (`v₂`.asInstanceOf[scala.util.Either[java.util.Optional[scala.Int], scala.Predef.String]].==(null)) out.burpNull() else () - `v₂`.asInstanceOf[scala.util.Either[java.util.Optional[scala.Int], scala.Predef.String]] match { - case scala.Left(v) => - { - out.label("a") - `v₃`.asInstanceOf[java.util.Optional[scala.Int]].asInstanceOf[java.util.Optional[scala.Int]] match { - case null => - out.burpNull() - case o if o.isEmpty() => - out.burpNull() - case o => - val vv: scala.Int = `o₂`.get().asInstanceOf[scala.Int] - out.value(vv) - } - } - case scala.Right(v) => - { - out.label("a") - out.valueEscaped(`v₄`.asInstanceOf[java.lang.String]) - } - } - } - } - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int] = { - var _a: scala.util.Either[scala.Boolean, scala.util.Either[java.util.Optional[scala.Int], scala.Predef.String]] = scala.Predef.augmentString("rl").foldRight[scala.Any]((java.util.Optional.empty[java.lang.Object](): scala.Any))(((c: scala.Char, acc: scala.Any) => if (c.==('r')) scala.Right.apply[scala.Nothing, scala.Any](acc) else scala.Left.apply[scala.Any, scala.Nothing](acc))).asInstanceOf[scala.util.Either[scala.Boolean, scala.util.Either[java.util.Optional[scala.Int], scala.Predef.String]]] - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_EitherRecipeJ_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = { - val mark: scala.Int = `in₂`.pos - if (`in₂`.expectNull()) null else scala.util.Try.apply[scala.util.Either[java.util.Optional[scala.Int], scala.Predef.String]]({ - val `mark₂`: scala.Int = `in₂`.pos - if (`in₂`.expectNull()) null else scala.util.Try.apply[java.lang.String](`in₂`.expectString()) match { - case scala.util.Success(rval) => - scala.Right.apply[scala.Nothing, java.lang.String](rval) - case scala.util.Failure(f) => - `in₂`.revertToPos(`mark₂`) - scala.util.Try.apply[java.util.Optional[scala.Int]](if (`in₂`.expectNull()) java.util.Optional.empty[scala.Int]() else java.util.Optional.of[scala.Int](`in₂`.expectInt())) match { - case scala.util.Success(lval) => - scala.Left.apply[java.util.Optional[scala.Int], scala.Nothing](lval) - case scala.util.Failure(_) => - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) - } - } - }) match { - case scala.util.Success(rval) => - scala.Right.apply[scala.Nothing, scala.util.Either[java.util.Optional[scala.Int], scala.Predef.String]](`rval₂`) - case scala.util.Failure(f) => - `in₂`.revertToPos(mark) - scala.util.Try.apply[scala.Boolean](`in₂`.expectBoolean()) match { - case scala.util.Success(lval) => - scala.Left.apply[scala.Boolean, scala.Nothing](`lval₂`) - case scala.util.Failure(_) => - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Failed to read either side of Either type", `in₂`) - } - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_EitherRecipeJ_fields) - } - if (required.&(0).==(0)) new co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int][scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(0)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int]] { - def encodeValue(`in₃`: co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int] = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.EitherRecipeJ[scala.Int]]) -} -Codec: { - val __co_blocke_scalajack_json_misc_TryHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.misc.TryHolder[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - in.a match { - case scala.util.Failure(v) => - { - out.label("a") - out.burpNull() - } - case _ => - { - out.label("a") - out.value(in.a.get) - } - } - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.TryHolder[scala.Int] = { - var _a: scala.util.Try[scala.Int] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_TryHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.TryHolder[scala.Int]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = { - val mark: scala.Int = `in₂`.pos - try if (`in₂`.expectNull()) null else scala.util.Success.apply[scala.Int](`in₂`.expectInt()) catch { - case t: java.lang.Throwable => - `in₂`.revertToPos(mark) - throw co.blocke.scalajack.json.JsonParseError.apply("Unsuccessful attempt to read Try type with failure: ".+(t.getMessage()), `in₂`) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_TryHolder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.misc.TryHolder[scala.Int][scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.TryHolder[scala.Int]] { - def encodeValue(`in₃`: co.blocke.scalajack.json.misc.TryHolder[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.TryHolder[scala.Int] = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.TryHolder[scala.Int]]) -} -Codec: { - val __co_blocke_scalajack_json_misc_TryHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.misc.TryHolder[scala.Option[scala.Int]], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - in.a match { - case scala.util.Failure(v) => - { - out.label("a") - out.burpNull() - } - case _ => - in.a.get match { - case scala.None => - () - case scala.Some(v) => - { - out.label("a") - out.value(v) - } - } - } - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.TryHolder[scala.Option[scala.Int]] = { - var _a: scala.util.Try[scala.Option[scala.Int]] = scala.util.Success.apply[scala.None.type](scala.None) - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_TryHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.TryHolder[scala.Option[scala.Int]]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = { - val mark: scala.Int = `in₂`.pos - try if (`in₂`.expectNull()) null else scala.util.Success.apply[scala.Option[scala.Int]](if (`in₂`.expectNull()) null else scala.Some.apply[scala.Int](`in₂`.expectInt())) catch { - case t: java.lang.Throwable => - `in₂`.revertToPos(mark) - throw co.blocke.scalajack.json.JsonParseError.apply("Unsuccessful attempt to read Try type with failure: ".+(t.getMessage()), `in₂`) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_TryHolder_fields) - } - if (required.&(0).==(0)) new co.blocke.scalajack.json.misc.TryHolder[scala.Option[scala.Int]][scala.Option[scala.Int]](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(0)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.TryHolder[scala.Option[scala.Int]]] { - def encodeValue(`in₃`: co.blocke.scalajack.json.misc.TryHolder[scala.Option[scala.Int]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.TryHolder[scala.Option[scala.Int]] = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.TryHolder[scala.Option[scala.Int]]]) -} -Codec: { - val __co_blocke_scalajack_json_misc_TryHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.misc.TryHolder[scala.Option[scala.Int]], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - in.a match { - case scala.util.Failure(v) => - { - out.label("a") - out.burpNull() - } - case _ => - in.a.get match { - case scala.None => - () - case scala.Some(v) => - { - out.label("a") - out.value(v) - } - } - } - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.TryHolder[scala.Option[scala.Int]] = { - var _a: scala.util.Try[scala.Option[scala.Int]] = scala.util.Success.apply[scala.None.type](scala.None) - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_TryHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.TryHolder[scala.Option[scala.Int]]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = { - val mark: scala.Int = `in₂`.pos - try if (`in₂`.expectNull()) null else scala.util.Success.apply[scala.Option[scala.Int]](if (`in₂`.expectNull()) null else scala.Some.apply[scala.Int](`in₂`.expectInt())) catch { - case t: java.lang.Throwable => - `in₂`.revertToPos(mark) - throw co.blocke.scalajack.json.JsonParseError.apply("Unsuccessful attempt to read Try type with failure: ".+(t.getMessage()), `in₂`) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_TryHolder_fields) - } - if (required.&(0).==(0)) new co.blocke.scalajack.json.misc.TryHolder[scala.Option[scala.Int]][scala.Option[scala.Int]](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(0)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.TryHolder[scala.Option[scala.Int]]] { - def encodeValue(`in₃`: co.blocke.scalajack.json.misc.TryHolder[scala.Option[scala.Int]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.TryHolder[scala.Option[scala.Int]] = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.TryHolder[scala.Option[scala.Int]]]) -} -Codec: { - val __co_blocke_scalajack_json_misc_TryHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.misc.TryHolder[java.util.Optional[scala.Int]], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - in.a match { - case scala.util.Failure(v) => - { - out.label("a") - out.burpNull() - } - case _ => - if (in.a.get.isEmpty().unary_!) { - out.label("a") - out.value(in.a.get.get()) - } else () - } - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.TryHolder[java.util.Optional[scala.Int]] = { - var _a: scala.util.Try[java.util.Optional[scala.Int]] = scala.util.Success.apply[java.util.Optional[java.lang.Object]](java.util.Optional.empty[java.lang.Object]()).asInstanceOf[scala.util.Try[java.util.Optional[scala.Int]]] - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_TryHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.TryHolder[java.util.Optional[scala.Int]]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = { - val mark: scala.Int = `in₂`.pos - try if (`in₂`.expectNull()) null else scala.util.Success.apply[java.util.Optional[scala.Int]](if (`in₂`.expectNull()) null else java.util.Optional.of[scala.Int](`in₂`.expectInt())) catch { - case t: java.lang.Throwable => - `in₂`.revertToPos(mark) - throw co.blocke.scalajack.json.JsonParseError.apply("Unsuccessful attempt to read Try type with failure: ".+(t.getMessage()), `in₂`) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_TryHolder_fields) - } - if (required.&(0).==(0)) new co.blocke.scalajack.json.misc.TryHolder[java.util.Optional[scala.Int]][java.util.Optional[scala.Int]](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(0)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.TryHolder[java.util.Optional[scala.Int]]] { - def encodeValue(`in₃`: co.blocke.scalajack.json.misc.TryHolder[java.util.Optional[scala.Int]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.TryHolder[java.util.Optional[scala.Int]] = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.TryHolder[java.util.Optional[scala.Int]]]) -} -Codec: { - val __co_blocke_scalajack_json_misc_TryHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.misc.TryHolder[java.util.Optional[scala.Int]], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - in.a match { - case scala.util.Failure(v) => - { - out.label("a") - out.burpNull() - } - case _ => - if (in.a.get.isEmpty().unary_!) { - out.label("a") - out.value(in.a.get.get()) - } else () - } - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.TryHolder[java.util.Optional[scala.Int]] = { - var _a: scala.util.Try[java.util.Optional[scala.Int]] = scala.util.Success.apply[java.util.Optional[java.lang.Object]](java.util.Optional.empty[java.lang.Object]()).asInstanceOf[scala.util.Try[java.util.Optional[scala.Int]]] - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_TryHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.TryHolder[java.util.Optional[scala.Int]]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = { - val mark: scala.Int = `in₂`.pos - try if (`in₂`.expectNull()) null else scala.util.Success.apply[java.util.Optional[scala.Int]](if (`in₂`.expectNull()) null else java.util.Optional.of[scala.Int](`in₂`.expectInt())) catch { - case t: java.lang.Throwable => - `in₂`.revertToPos(mark) - throw co.blocke.scalajack.json.JsonParseError.apply("Unsuccessful attempt to read Try type with failure: ".+(t.getMessage()), `in₂`) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_TryHolder_fields) - } - if (required.&(0).==(0)) new co.blocke.scalajack.json.misc.TryHolder[java.util.Optional[scala.Int]][java.util.Optional[scala.Int]](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(0)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.TryHolder[java.util.Optional[scala.Int]]] { - def encodeValue(`in₃`: co.blocke.scalajack.json.misc.TryHolder[java.util.Optional[scala.Int]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.TryHolder[java.util.Optional[scala.Int]] = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.TryHolder[java.util.Optional[scala.Int]]]) -} -Codec: { - val __co_blocke_scalajack_json_misc_TryHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.misc.TryHolder[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - in.a match { - case scala.util.Failure(v) => - { - out.label("a") - out.burpNull() - } - case _ => - { - out.label("a") - out.value(in.a.get) - } - } - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.TryHolder[scala.Int] = { - var _a: scala.util.Try[scala.Int] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_TryHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.TryHolder[scala.Int]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = { - val mark: scala.Int = `in₂`.pos - try if (`in₂`.expectNull()) null else scala.util.Success.apply[scala.Int](`in₂`.expectInt()) catch { - case t: java.lang.Throwable => - `in₂`.revertToPos(mark) - throw co.blocke.scalajack.json.JsonParseError.apply("Unsuccessful attempt to read Try type with failure: ".+(t.getMessage()), `in₂`) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_TryHolder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.misc.TryHolder[scala.Int][scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.TryHolder[scala.Int]] { - def encodeValue(`in₃`: co.blocke.scalajack.json.misc.TryHolder[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.TryHolder[scala.Int] = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.TryHolder[scala.Int]]) -} -Codec: { - val __co_blocke_scalajack_json_misc_TryHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.misc.TryHolder[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - in.a match { - case scala.util.Failure(v) => - { - out.label("a") - out.value("Try Failure with msg: ".+(v.getMessage())) - } - case _ => - { - out.label("a") - out.value(in.a.get) - } - } - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.TryHolder[scala.Int] = { - var _a: scala.util.Try[scala.Int] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_TryHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.TryHolder[scala.Int]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = { - val mark: scala.Int = `in₂`.pos - try if (`in₂`.expectNull()) null else scala.util.Success.apply[scala.Int](`in₂`.expectInt()) catch { - case t: java.lang.Throwable => - `in₂`.revertToPos(mark) - throw co.blocke.scalajack.json.JsonParseError.apply("Unsuccessful attempt to read Try type with failure: ".+(t.getMessage()), `in₂`) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_TryHolder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.misc.TryHolder[scala.Int][scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.TryHolder[scala.Int]] { - def encodeValue(`in₃`: co.blocke.scalajack.json.misc.TryHolder[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.TryHolder[scala.Int] = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.TryHolder[scala.Int]]) -} -Codec: { - val __co_blocke_scalajack_json_misc_TryHolder_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.misc.TryHolder[scala.Int], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - in.a match { - case scala.util.Failure(v) => - throw v - case _ => - { - out.label("a") - out.value(in.a.get) - } - } - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.TryHolder[scala.Int] = { - var _a: scala.util.Try[scala.Int] = null - var required: scala.Int = 1 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_misc_TryHolder_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.TryHolder[scala.Int]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = { - val mark: scala.Int = `in₂`.pos - try if (`in₂`.expectNull()) null else scala.util.Success.apply[scala.Int](`in₂`.expectInt()) catch { - case t: java.lang.Throwable => - `in₂`.revertToPos(mark) - throw co.blocke.scalajack.json.JsonParseError.apply("Unsuccessful attempt to read Try type with failure: ".+(t.getMessage()), `in₂`) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_misc_TryHolder_fields) - } - if (required.&(1).==(0)) new co.blocke.scalajack.json.misc.TryHolder[scala.Int][scala.Int](_a) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(1)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.TryHolder[scala.Int]] { - def encodeValue(`in₃`: co.blocke.scalajack.json.misc.TryHolder[scala.Int], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.TryHolder[scala.Int] = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.TryHolder[scala.Int]]) -} -Codec: { - val __co_blocke_scalajack_json_misc_TryHolder2_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("a", "b")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w1(in: scala.collection.immutable.Seq[scala.util.Try[scala.Int]], out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startArray() - in.foreach[scala.Unit](((i: scala.util.Try[scala.Int]) => if (i.==(null)) out.burpNull() else i match { - case scala.util.Success(v) => - out.value(v) - case scala.util.Failure(v) => - out.burpNull() - })) - out.endArray() - } - def w2(`in₂`: scala.Tuple2[scala.util.Try[scala.Int], scala.util.Try[scala.Int]], `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₂`.==(null)) `out₂`.burpNull() else { - `out₂`.startArray() - if (`in₂`._1.==(null)) `out₂`.burpNull() else `in₂`._1 match { - case scala.util.Success(v) => - `out₂`.value(`v₂`) - case scala.util.Failure(v) => - `out₂`.burpNull() - } - if (`in₂`._2.==(null)) `out₂`.burpNull() else `in₂`._2 match { - case scala.util.Success(v) => - `out₂`.value(`v₃`) - case scala.util.Failure(v) => - `out₂`.burpNull() - } - `out₂`.endArray() - } - def w0(`in₃`: co.blocke.scalajack.json.misc.TryHolder2[scala.Int], `out₃`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (`in₃`.==(null)) `out₃`.burpNull() else { - `out₃`.startObject() - `out₃`.label("a") - w1(`in₃`.a, `out₃`) - `out₃`.label("b") - w2(`in₃`.b, `out₃`) - `out₃`.endObject() - } - def r0(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.TryHolder2[scala.Int] = { - var _a: scala.collection.immutable.Seq[scala.util.Try[scala.Int]] = null - var _b: scala.Tuple2[scala.util.Try[scala.Int], scala.util.Try[scala.Int]] = null - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₄`.expectFirstObjectField(__co_blocke_scalajack_json_misc_TryHolder2_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.misc.TryHolder2[scala.Int]] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _a = { - val parsedArray: scala.collection.mutable.ListBuffer[scala.util.Try[scala.Int]] = `in₄`.expectArray[scala.util.Try[scala.Int]]((() => { - val mark: scala.Int = `in₄`.pos - try if (`in₄`.expectNull()) null else scala.util.Success.apply[scala.Int](`in₄`.expectInt()) catch { - case t: java.lang.Throwable => - `in₄`.revertToPos(mark) - throw co.blocke.scalajack.json.JsonParseError.apply("Unsuccessful attempt to read Try type with failure: ".+(t.getMessage()), `in₄`) - } - })) - - (if (parsedArray.!=(null)) parsedArray.toSeq else null: scala.collection.immutable.Seq[scala.util.Try[scala.Int]]) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("a"), `in₄`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _b = if (`in₄`.expectNull()) null else { - `in₄`.expectToken('[') - val tv: scala.Tuple2[scala.util.Try[scala.Int], scala.util.Try[scala.Int]] = new scala.Tuple2[scala.util.Try[scala.Int], scala.util.Try[scala.Int]][scala.util.Try[scala.Int], scala.util.Try[scala.Int]]({ - val `mark₂`: scala.Int = `in₄`.pos - try if (`in₄`.expectNull()) null else scala.util.Success.apply[scala.Int](`in₄`.expectInt()) catch { - case t: java.lang.Throwable => - `in₄`.revertToPos(`mark₂`) - throw co.blocke.scalajack.json.JsonParseError.apply("Unsuccessful attempt to read Try type with failure: ".+(`t₂`.getMessage()), `in₄`) - } - }, { - `in₄`.expectToken(',') - val `mark₃`: scala.Int = `in₄`.pos - try if (`in₄`.expectNull()) null else scala.util.Success.apply[scala.Int](`in₄`.expectInt()) catch { - case t: java.lang.Throwable => - `in₄`.revertToPos(`mark₃`) - throw co.blocke.scalajack.json.JsonParseError.apply("Unsuccessful attempt to read Try type with failure: ".+(`t₃`.getMessage()), `in₄`) - } - }) - `in₄`.expectToken(']') - - (tv: scala.Tuple2[scala.util.Try[scala.Int], scala.util.Try[scala.Int]]) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b"), `in₄`) - case _ => - `in₄`.skipValue() - } - maybeFieldNum = `in₄`.expectObjectField(__co_blocke_scalajack_json_misc_TryHolder2_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.misc.TryHolder2[scala.Int][scala.Int](_a, _b) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("a", "b")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₄`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.TryHolder2[scala.Int]] { - def encodeValue(`in₅`: co.blocke.scalajack.json.misc.TryHolder2[scala.Int], `out₄`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₅`, `out₄`) - def decodeValue(`in₆`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.misc.TryHolder2[scala.Int] = r0(`in₆`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.misc.TryHolder2[scala.Int]]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleJBigDecimal_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("bd1", "bd2", "bd3", "bd4", "bd5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleJBigDecimal, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("bd1") - out.value(in.bd1) - out.label("bd2") - out.value(in.bd2) - out.label("bd3") - out.value(in.bd3) - out.label("bd4") - out.value(in.bd4) - out.label("bd5") - out.value(in.bd5) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJBigDecimal = { - var _bd1: java.math.BigDecimal = new java.math.BigDecimal(0.0) - var _bd2: java.math.BigDecimal = new java.math.BigDecimal(0.0) - var _bd3: java.math.BigDecimal = new java.math.BigDecimal(0.0) - var _bd4: java.math.BigDecimal = new java.math.BigDecimal(0.0) - var _bd5: java.math.BigDecimal = new java.math.BigDecimal(0.0) - var required: scala.Int = 31 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJBigDecimal_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJBigDecimal] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _bd1 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - new java.math.BigDecimal(n) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _bd2 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - new java.math.BigDecimal(`n₂`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _bd3 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - new java.math.BigDecimal(`n₃`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _bd4 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - new java.math.BigDecimal(`n₄`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _bd5 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - new java.math.BigDecimal(`n₅`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd5"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJBigDecimal_fields) - } - if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJBigDecimal(_bd1, _bd2, _bd3, _bd4, _bd5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("bd1", "bd2", "bd3", "bd4", "bd5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJBigDecimal] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJBigDecimal, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJBigDecimal = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJBigDecimal]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleJBigDecimal_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("bd1", "bd2", "bd3", "bd4", "bd5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleJBigDecimal, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("bd1") - out.value(in.bd1) - out.label("bd2") - out.value(in.bd2) - out.label("bd3") - out.value(in.bd3) - out.label("bd4") - out.value(in.bd4) - out.label("bd5") - out.value(in.bd5) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJBigDecimal = { - var _bd1: java.math.BigDecimal = new java.math.BigDecimal(0.0) - var _bd2: java.math.BigDecimal = new java.math.BigDecimal(0.0) - var _bd3: java.math.BigDecimal = new java.math.BigDecimal(0.0) - var _bd4: java.math.BigDecimal = new java.math.BigDecimal(0.0) - var _bd5: java.math.BigDecimal = new java.math.BigDecimal(0.0) - var required: scala.Int = 31 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJBigDecimal_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJBigDecimal] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _bd1 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - new java.math.BigDecimal(n) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _bd2 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - new java.math.BigDecimal(`n₂`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _bd3 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - new java.math.BigDecimal(`n₃`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _bd4 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - new java.math.BigDecimal(`n₄`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _bd5 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - new java.math.BigDecimal(`n₅`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd5"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJBigDecimal_fields) - } - if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJBigDecimal(_bd1, _bd2, _bd3, _bd4, _bd5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("bd1", "bd2", "bd3", "bd4", "bd5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJBigDecimal] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJBigDecimal, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJBigDecimal = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJBigDecimal]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleJBigInteger_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("bi1", "bi2", "bi3", "bi4", "bi5", "bi6", "bi7")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleJBigInteger, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("bi1") - out.value(in.bi1) - out.label("bi2") - out.value(in.bi2) - out.label("bi3") - out.value(in.bi3) - out.label("bi4") - out.value(in.bi4) - out.label("bi5") - out.value(in.bi5) - out.label("bi6") - out.value(in.bi6) - out.label("bi7") - out.value(in.bi7) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJBigInteger = { - var _bi1: java.math.BigInteger = java.math.BigInteger.valueOf(0L) - var _bi2: java.math.BigInteger = java.math.BigInteger.valueOf(0L) - var _bi3: java.math.BigInteger = java.math.BigInteger.valueOf(0L) - var _bi4: java.math.BigInteger = java.math.BigInteger.valueOf(0L) - var _bi5: java.math.BigInteger = java.math.BigInteger.valueOf(0L) - var _bi6: java.math.BigInteger = java.math.BigInteger.valueOf(0L) - var _bi7: java.math.BigInteger = java.math.BigInteger.valueOf(0L) - var required: scala.Int = 127 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJBigInteger_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJBigInteger] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _bi1 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - new java.math.BigInteger(n) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _bi2 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - new java.math.BigInteger(`n₂`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _bi3 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - new java.math.BigInteger(`n₃`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _bi4 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - new java.math.BigInteger(`n₄`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _bi5 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - new java.math.BigInteger(`n₅`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi5"), `in₂`) - case 5 => - if (required.&(32).!=(0)) { - required = required.^(32) - _bi6 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - new java.math.BigInteger(`n₆`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi6"), `in₂`) - case 6 => - if (required.&(64).!=(0)) { - required = required.^(64) - _bi7 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - new java.math.BigInteger(`n₇`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi7"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJBigInteger_fields) - } - if (required.&(127).==(0)) new co.blocke.scalajack.json.primitives.SampleJBigInteger(_bi1, _bi2, _bi3, _bi4, _bi5, _bi6, _bi7) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("bi1", "bi2", "bi3", "bi4", "bi5", "bi6", "bi7")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(127)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJBigInteger] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJBigInteger, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJBigInteger = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJBigInteger]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleJBigInteger_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("bi1", "bi2", "bi3", "bi4", "bi5", "bi6", "bi7")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleJBigInteger, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("bi1") - out.value(in.bi1) - out.label("bi2") - out.value(in.bi2) - out.label("bi3") - out.value(in.bi3) - out.label("bi4") - out.value(in.bi4) - out.label("bi5") - out.value(in.bi5) - out.label("bi6") - out.value(in.bi6) - out.label("bi7") - out.value(in.bi7) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJBigInteger = { - var _bi1: java.math.BigInteger = java.math.BigInteger.valueOf(0L) - var _bi2: java.math.BigInteger = java.math.BigInteger.valueOf(0L) - var _bi3: java.math.BigInteger = java.math.BigInteger.valueOf(0L) - var _bi4: java.math.BigInteger = java.math.BigInteger.valueOf(0L) - var _bi5: java.math.BigInteger = java.math.BigInteger.valueOf(0L) - var _bi6: java.math.BigInteger = java.math.BigInteger.valueOf(0L) - var _bi7: java.math.BigInteger = java.math.BigInteger.valueOf(0L) - var required: scala.Int = 127 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJBigInteger_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJBigInteger] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _bi1 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - new java.math.BigInteger(n) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _bi2 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - new java.math.BigInteger(`n₂`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _bi3 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - new java.math.BigInteger(`n₃`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _bi4 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - new java.math.BigInteger(`n₄`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _bi5 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - new java.math.BigInteger(`n₅`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi5"), `in₂`) - case 5 => - if (required.&(32).!=(0)) { - required = required.^(32) - _bi6 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - new java.math.BigInteger(`n₆`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi6"), `in₂`) - case 6 => - if (required.&(64).!=(0)) { - required = required.^(64) - _bi7 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - new java.math.BigInteger(`n₇`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi7"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJBigInteger_fields) - } - if (required.&(127).==(0)) new co.blocke.scalajack.json.primitives.SampleJBigInteger(_bi1, _bi2, _bi3, _bi4, _bi5, _bi6, _bi7) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("bi1", "bi2", "bi3", "bi4", "bi5", "bi6", "bi7")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(127)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJBigInteger] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJBigInteger, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJBigInteger = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJBigInteger]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleJBoolean_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("bool1", "bool2", "bool3", "bool4", "bool5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleJBoolean, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("bool1") - out.value(in.bool1) - out.label("bool2") - out.value(in.bool2) - out.label("bool3") - out.value(in.bool3) - out.label("bool4") - out.value(in.bool4) - out.label("bool5") - out.value(in.bool5) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJBoolean = { - var _bool1: java.lang.Boolean = java.lang.Boolean.FALSE - var _bool2: java.lang.Boolean = java.lang.Boolean.FALSE - var _bool3: java.lang.Boolean = java.lang.Boolean.FALSE - var _bool4: java.lang.Boolean = java.lang.Boolean.FALSE - var _bool5: java.lang.Boolean = java.lang.Boolean.FALSE - var required: scala.Int = 31 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJBoolean_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJBoolean] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _bool1 = `in₂`.expectJavaBoolean() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bool1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _bool2 = `in₂`.expectJavaBoolean() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bool2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _bool3 = `in₂`.expectJavaBoolean() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bool3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _bool4 = `in₂`.expectJavaBoolean() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bool4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _bool5 = `in₂`.expectJavaBoolean() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bool5"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJBoolean_fields) - } - if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJBoolean(_bool1, _bool2, _bool3, _bool4, _bool5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("bool1", "bool2", "bool3", "bool4", "bool5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJBoolean] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJBoolean, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJBoolean = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJBoolean]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleJBoolean_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("bool1", "bool2", "bool3", "bool4", "bool5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleJBoolean, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("bool1") - out.value(in.bool1) - out.label("bool2") - out.value(in.bool2) - out.label("bool3") - out.value(in.bool3) - out.label("bool4") - out.value(in.bool4) - out.label("bool5") - out.value(in.bool5) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJBoolean = { - var _bool1: java.lang.Boolean = java.lang.Boolean.FALSE - var _bool2: java.lang.Boolean = java.lang.Boolean.FALSE - var _bool3: java.lang.Boolean = java.lang.Boolean.FALSE - var _bool4: java.lang.Boolean = java.lang.Boolean.FALSE - var _bool5: java.lang.Boolean = java.lang.Boolean.FALSE - var required: scala.Int = 31 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJBoolean_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJBoolean] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _bool1 = `in₂`.expectJavaBoolean() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bool1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _bool2 = `in₂`.expectJavaBoolean() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bool2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _bool3 = `in₂`.expectJavaBoolean() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bool3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _bool4 = `in₂`.expectJavaBoolean() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bool4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _bool5 = `in₂`.expectJavaBoolean() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bool5"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJBoolean_fields) - } - if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJBoolean(_bool1, _bool2, _bool3, _bool4, _bool5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("bool1", "bool2", "bool3", "bool4", "bool5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJBoolean] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJBoolean, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJBoolean = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJBoolean]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleJByte_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("b1", "b2", "b3", "b4", "b5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleJByte, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("b1") - out.value(in.b1) - out.label("b2") - out.value(in.b2) - out.label("b3") - out.value(in.b3) - out.label("b4") - out.value(in.b4) - out.label("b5") - out.value(in.b5) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJByte = { - var _b1: java.lang.Byte = scala.Predef.byte2Byte(java.lang.Byte.MIN_VALUE) - var _b2: java.lang.Byte = scala.Predef.byte2Byte(java.lang.Byte.MIN_VALUE) - var _b3: java.lang.Byte = scala.Predef.byte2Byte(java.lang.Byte.MIN_VALUE) - var _b4: java.lang.Byte = scala.Predef.byte2Byte(java.lang.Byte.MIN_VALUE) - var _b5: java.lang.Byte = scala.Predef.byte2Byte(java.lang.Byte.MIN_VALUE) - var required: scala.Int = 31 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJByte_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJByte] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _b1 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Byte.valueOf(n) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _b2 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Byte.valueOf(`n₂`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _b3 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Byte.valueOf(`n₃`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _b4 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Byte.valueOf(`n₄`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _b5 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Byte.valueOf(`n₅`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b5"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJByte_fields) - } - if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJByte(_b1, _b2, _b3, _b4, _b5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("b1", "b2", "b3", "b4", "b5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJByte] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJByte, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJByte = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJByte]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleJByte_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("b1", "b2", "b3", "b4", "b5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleJByte, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("b1") - out.value(in.b1) - out.label("b2") - out.value(in.b2) - out.label("b3") - out.value(in.b3) - out.label("b4") - out.value(in.b4) - out.label("b5") - out.value(in.b5) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJByte = { - var _b1: java.lang.Byte = scala.Predef.byte2Byte(java.lang.Byte.MIN_VALUE) - var _b2: java.lang.Byte = scala.Predef.byte2Byte(java.lang.Byte.MIN_VALUE) - var _b3: java.lang.Byte = scala.Predef.byte2Byte(java.lang.Byte.MIN_VALUE) - var _b4: java.lang.Byte = scala.Predef.byte2Byte(java.lang.Byte.MIN_VALUE) - var _b5: java.lang.Byte = scala.Predef.byte2Byte(java.lang.Byte.MIN_VALUE) - var required: scala.Int = 31 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJByte_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJByte] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _b1 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Byte.valueOf(n) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _b2 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Byte.valueOf(`n₂`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _b3 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Byte.valueOf(`n₃`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _b4 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Byte.valueOf(`n₄`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _b5 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Byte.valueOf(`n₅`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b5"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJByte_fields) - } - if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJByte(_b1, _b2, _b3, _b4, _b5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("b1", "b2", "b3", "b4", "b5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJByte] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJByte, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJByte = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJByte]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleJChar_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("c1", "c2", "c3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleJChar, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("c1") - out.value(in.c1) - out.label("c2") - out.value(in.c2) - out.label("c3") - out.value(in.c3) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJChar = { - var _c1: java.lang.Character = java.lang.Character.valueOf('x') - var _c2: java.lang.Character = java.lang.Character.valueOf('x') - var _c3: java.lang.Character = java.lang.Character.valueOf('x') - var required: scala.Int = 7 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJChar_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJChar] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _c1 = { - val c: java.lang.String = `in₂`.expectString() - if (c.==(null)) null else if (c.length().==(0)) { - `in₂`.backspace() - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Character value expected but empty string found in json", `in₂`) - } else java.lang.Character.valueOf(c.charAt(0)) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _c2 = { - val `c₂`: java.lang.String = `in₂`.expectString() - if (`c₂`.==(null)) null else if (`c₂`.length().==(0)) { - `in₂`.backspace() - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Character value expected but empty string found in json", `in₂`) - } else java.lang.Character.valueOf(`c₂`.charAt(0)) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _c3 = { - val `c₃`: java.lang.String = `in₂`.expectString() - if (`c₃`.==(null)) null else if (`c₃`.length().==(0)) { - `in₂`.backspace() - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Character value expected but empty string found in json", `in₂`) - } else java.lang.Character.valueOf(`c₃`.charAt(0)) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c3"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJChar_fields) - } - if (required.&(7).==(0)) new co.blocke.scalajack.json.primitives.SampleJChar(_c1, _c2, _c3) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("c1", "c2", "c3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(7)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJChar] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJChar, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJChar = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJChar]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleJChar_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("c1", "c2", "c3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleJChar, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("c1") - out.value(in.c1) - out.label("c2") - out.value(in.c2) - out.label("c3") - out.value(in.c3) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJChar = { - var _c1: java.lang.Character = java.lang.Character.valueOf('x') - var _c2: java.lang.Character = java.lang.Character.valueOf('x') - var _c3: java.lang.Character = java.lang.Character.valueOf('x') - var required: scala.Int = 7 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJChar_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJChar] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _c1 = { - val c: java.lang.String = `in₂`.expectString() - if (c.==(null)) null else if (c.length().==(0)) { - `in₂`.backspace() - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Character value expected but empty string found in json", `in₂`) - } else java.lang.Character.valueOf(c.charAt(0)) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _c2 = { - val `c₂`: java.lang.String = `in₂`.expectString() - if (`c₂`.==(null)) null else if (`c₂`.length().==(0)) { - `in₂`.backspace() - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Character value expected but empty string found in json", `in₂`) - } else java.lang.Character.valueOf(`c₂`.charAt(0)) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _c3 = { - val `c₃`: java.lang.String = `in₂`.expectString() - if (`c₃`.==(null)) null else if (`c₃`.length().==(0)) { - `in₂`.backspace() - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Character value expected but empty string found in json", `in₂`) - } else java.lang.Character.valueOf(`c₃`.charAt(0)) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c3"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJChar_fields) - } - if (required.&(7).==(0)) new co.blocke.scalajack.json.primitives.SampleJChar(_c1, _c2, _c3) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("c1", "c2", "c3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(7)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJChar] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJChar, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJChar = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJChar]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleJDouble_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4", "d5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleJDouble, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("d1") - out.value(in.d1) - out.label("d2") - out.value(in.d2) - out.label("d3") - out.value(in.d3) - out.label("d4") - out.value(in.d4) - out.label("d5") - out.value(in.d5) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJDouble = { - var _d1: java.lang.Double = java.lang.Double.valueOf(0.0) - var _d2: java.lang.Double = java.lang.Double.valueOf(0.0) - var _d3: java.lang.Double = java.lang.Double.valueOf(0.0) - var _d4: java.lang.Double = java.lang.Double.valueOf(0.0) - var _d5: java.lang.Double = java.lang.Double.valueOf(0.0) - var required: scala.Int = 31 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJDouble_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJDouble] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _d1 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Double.valueOf(n) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _d2 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Double.valueOf(`n₂`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _d3 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Double.valueOf(`n₃`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _d4 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Double.valueOf(`n₄`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _d5 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Double.valueOf(`n₅`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d5"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJDouble_fields) - } - if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJDouble(_d1, _d2, _d3, _d4, _d5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4", "d5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJDouble] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJDouble, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJDouble = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJDouble]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleJDouble_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4", "d5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleJDouble, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("d1") - out.value(in.d1) - out.label("d2") - out.value(in.d2) - out.label("d3") - out.value(in.d3) - out.label("d4") - out.value(in.d4) - out.label("d5") - out.value(in.d5) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJDouble = { - var _d1: java.lang.Double = java.lang.Double.valueOf(0.0) - var _d2: java.lang.Double = java.lang.Double.valueOf(0.0) - var _d3: java.lang.Double = java.lang.Double.valueOf(0.0) - var _d4: java.lang.Double = java.lang.Double.valueOf(0.0) - var _d5: java.lang.Double = java.lang.Double.valueOf(0.0) - var required: scala.Int = 31 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJDouble_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJDouble] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _d1 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Double.valueOf(n) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _d2 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Double.valueOf(`n₂`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _d3 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Double.valueOf(`n₃`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _d4 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Double.valueOf(`n₄`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _d5 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Double.valueOf(`n₅`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d5"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJDouble_fields) - } - if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJDouble(_d1, _d2, _d3, _d4, _d5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4", "d5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJDouble] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJDouble, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJDouble = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJDouble]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleJFloat_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("f1", "f2", "f3", "f4", "f5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleJFloat, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("f1") - out.value(in.f1) - out.label("f2") - out.value(in.f2) - out.label("f3") - out.value(in.f3) - out.label("f4") - out.value(in.f4) - out.label("f5") - out.value(in.f5) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJFloat = { - var _f1: java.lang.Float = java.lang.Float.valueOf(0.0f) - var _f2: java.lang.Float = java.lang.Float.valueOf(0.0f) - var _f3: java.lang.Float = java.lang.Float.valueOf(0.0f) - var _f4: java.lang.Float = java.lang.Float.valueOf(0.0f) - var _f5: java.lang.Float = java.lang.Float.valueOf(0.0f) - var required: scala.Int = 31 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJFloat_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJFloat] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _f1 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Float.valueOf(n) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _f2 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Float.valueOf(`n₂`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _f3 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Float.valueOf(`n₃`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _f4 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Float.valueOf(`n₄`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _f5 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Float.valueOf(`n₅`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f5"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJFloat_fields) - } - if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJFloat(_f1, _f2, _f3, _f4, _f5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("f1", "f2", "f3", "f4", "f5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJFloat] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJFloat, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJFloat = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJFloat]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleJFloat_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("f1", "f2", "f3", "f4", "f5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleJFloat, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("f1") - out.value(in.f1) - out.label("f2") - out.value(in.f2) - out.label("f3") - out.value(in.f3) - out.label("f4") - out.value(in.f4) - out.label("f5") - out.value(in.f5) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJFloat = { - var _f1: java.lang.Float = java.lang.Float.valueOf(0.0f) - var _f2: java.lang.Float = java.lang.Float.valueOf(0.0f) - var _f3: java.lang.Float = java.lang.Float.valueOf(0.0f) - var _f4: java.lang.Float = java.lang.Float.valueOf(0.0f) - var _f5: java.lang.Float = java.lang.Float.valueOf(0.0f) - var required: scala.Int = 31 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJFloat_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJFloat] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _f1 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Float.valueOf(n) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _f2 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Float.valueOf(`n₂`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _f3 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Float.valueOf(`n₃`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _f4 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Float.valueOf(`n₄`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _f5 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Float.valueOf(`n₅`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f5"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJFloat_fields) - } - if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJFloat(_f1, _f2, _f3, _f4, _f5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("f1", "f2", "f3", "f4", "f5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJFloat] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJFloat, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJFloat = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJFloat]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleJInt_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("i1", "i2", "i3", "i4", "i5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleJInt, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("i1") - out.value(in.i1) - out.label("i2") - out.value(in.i2) - out.label("i3") - out.value(in.i3) - out.label("i4") - out.value(in.i4) - out.label("i5") - out.value(in.i5) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJInt = { - var _i1: java.lang.Integer = java.lang.Integer.valueOf(0) - var _i2: java.lang.Integer = java.lang.Integer.valueOf(0) - var _i3: java.lang.Integer = java.lang.Integer.valueOf(0) - var _i4: java.lang.Integer = java.lang.Integer.valueOf(0) - var _i5: java.lang.Integer = java.lang.Integer.valueOf(0) - var required: scala.Int = 31 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJInt_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJInt] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _i1 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Integer.valueOf(n) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _i2 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Integer.valueOf(`n₂`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _i3 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Integer.valueOf(`n₃`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _i4 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Integer.valueOf(`n₄`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _i5 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Integer.valueOf(`n₅`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i5"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJInt_fields) - } - if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJInt(_i1, _i2, _i3, _i4, _i5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("i1", "i2", "i3", "i4", "i5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJInt] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJInt, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJInt = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJInt]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleJInt_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("i1", "i2", "i3", "i4", "i5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleJInt, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("i1") - out.value(in.i1) - out.label("i2") - out.value(in.i2) - out.label("i3") - out.value(in.i3) - out.label("i4") - out.value(in.i4) - out.label("i5") - out.value(in.i5) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJInt = { - var _i1: java.lang.Integer = java.lang.Integer.valueOf(0) - var _i2: java.lang.Integer = java.lang.Integer.valueOf(0) - var _i3: java.lang.Integer = java.lang.Integer.valueOf(0) - var _i4: java.lang.Integer = java.lang.Integer.valueOf(0) - var _i5: java.lang.Integer = java.lang.Integer.valueOf(0) - var required: scala.Int = 31 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJInt_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJInt] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _i1 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Integer.valueOf(n) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _i2 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Integer.valueOf(`n₂`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _i3 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Integer.valueOf(`n₃`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _i4 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Integer.valueOf(`n₄`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _i5 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Integer.valueOf(`n₅`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i5"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJInt_fields) - } - if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJInt(_i1, _i2, _i3, _i4, _i5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("i1", "i2", "i3", "i4", "i5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJInt] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJInt, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJInt = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJInt]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleJLong_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("l1", "l2", "l3", "l4", "l5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleJLong, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("l1") - out.value(in.l1) - out.label("l2") - out.value(in.l2) - out.label("l3") - out.value(in.l3) - out.label("l4") - out.value(in.l4) - out.label("l5") - out.value(in.l5) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJLong = { - var _l1: java.lang.Long = java.lang.Long.valueOf(0L) - var _l2: java.lang.Long = java.lang.Long.valueOf(0L) - var _l3: java.lang.Long = java.lang.Long.valueOf(0L) - var _l4: java.lang.Long = java.lang.Long.valueOf(0L) - var _l5: java.lang.Long = java.lang.Long.valueOf(0L) - var required: scala.Int = 31 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJLong_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJLong] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _l1 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Long.valueOf(n) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _l2 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Long.valueOf(`n₂`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _l3 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Long.valueOf(`n₃`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _l4 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Long.valueOf(`n₄`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _l5 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Long.valueOf(`n₅`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l5"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJLong_fields) - } - if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJLong(_l1, _l2, _l3, _l4, _l5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("l1", "l2", "l3", "l4", "l5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJLong] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJLong, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJLong = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJLong]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleJLong_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("l1", "l2", "l3", "l4", "l5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleJLong, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("l1") - out.value(in.l1) - out.label("l2") - out.value(in.l2) - out.label("l3") - out.value(in.l3) - out.label("l4") - out.value(in.l4) - out.label("l5") - out.value(in.l5) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJLong = { - var _l1: java.lang.Long = java.lang.Long.valueOf(0L) - var _l2: java.lang.Long = java.lang.Long.valueOf(0L) - var _l3: java.lang.Long = java.lang.Long.valueOf(0L) - var _l4: java.lang.Long = java.lang.Long.valueOf(0L) - var _l5: java.lang.Long = java.lang.Long.valueOf(0L) - var required: scala.Int = 31 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJLong_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJLong] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _l1 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Long.valueOf(n) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _l2 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Long.valueOf(`n₂`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _l3 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Long.valueOf(`n₃`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _l4 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Long.valueOf(`n₄`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _l5 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Long.valueOf(`n₅`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l5"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJLong_fields) - } - if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJLong(_l1, _l2, _l3, _l4, _l5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("l1", "l2", "l3", "l4", "l5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJLong] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJLong, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJLong = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJLong]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleJNumber_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("n1", "n2", "n3", "n4", "n5", "n6", "n7", "n8", "n9", "n10", "n11", "n12", "n13", "n14", "n15", "n16", "n17")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleJNumber, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("n1") - out.value(in.n1) - out.label("n2") - out.value(in.n2) - out.label("n3") - out.value(in.n3) - out.label("n4") - out.value(in.n4) - out.label("n5") - out.value(in.n5) - out.label("n6") - out.value(in.n6) - out.label("n7") - out.value(in.n7) - out.label("n8") - out.value(in.n8) - out.label("n9") - out.value(in.n9) - out.label("n10") - out.value(in.n10) - out.label("n11") - out.value(in.n11) - out.label("n12") - out.value(in.n12) - out.label("n13") - out.value(in.n13) - out.label("n14") - out.value(in.n14) - out.label("n15") - out.value(in.n15) - out.label("n16") - out.value(in.n16) - out.label("n17") - out.value(in.n17) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJNumber = { - var _n1: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n2: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n3: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n4: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n5: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n6: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n7: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n8: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n9: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n10: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n11: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n12: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n13: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n14: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n15: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n16: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n17: java.lang.Number = null.asInstanceOf[java.lang.Number] - var required: scala.Int = 131071 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJNumber_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJNumber] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _n1 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(n) match { - case d if d.isValidByte => - java.lang.Byte.valueOf(d.toByteExact) - case d if `d₂`.isValidShort => - java.lang.Short.valueOf(`d₂`.toShortExact) - case d if `d₃`.isValidInt => - java.lang.Integer.valueOf(`d₃`.toIntExact) - case d if `d₄`.isValidLong => - java.lang.Long.valueOf(`d₄`.toLongExact) - case d if `d₅`.isDecimalFloat => - java.lang.Float.valueOf(`d₅`.toFloat) - case d if `d₆`.isDecimalDouble => - java.lang.Double.valueOf(`d₆`.toDouble) - case d => - (`d₇`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _n2 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₂`) match { - case d if `d₈`.isValidByte => - java.lang.Byte.valueOf(`d₈`.toByteExact) - case d if `d₉`.isValidShort => - java.lang.Short.valueOf(`d₉`.toShortExact) - case d if `d₁₀`.isValidInt => - java.lang.Integer.valueOf(`d₁₀`.toIntExact) - case d if `d₁₁`.isValidLong => - java.lang.Long.valueOf(`d₁₁`.toLongExact) - case d if `d₁₂`.isDecimalFloat => - java.lang.Float.valueOf(`d₁₂`.toFloat) - case d if `d₁₃`.isDecimalDouble => - java.lang.Double.valueOf(`d₁₃`.toDouble) - case d => - (`d₁₄`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _n3 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₃`) match { - case d if `d₁₅`.isValidByte => - java.lang.Byte.valueOf(`d₁₅`.toByteExact) - case d if `d₁₆`.isValidShort => - java.lang.Short.valueOf(`d₁₆`.toShortExact) - case d if `d₁₇`.isValidInt => - java.lang.Integer.valueOf(`d₁₇`.toIntExact) - case d if `d₁₈`.isValidLong => - java.lang.Long.valueOf(`d₁₈`.toLongExact) - case d if `d₁₉`.isDecimalFloat => - java.lang.Float.valueOf(`d₁₉`.toFloat) - case d if `d₂₀`.isDecimalDouble => - java.lang.Double.valueOf(`d₂₀`.toDouble) - case d => - (`d₂₁`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _n4 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₄`) match { - case d if `d₂₂`.isValidByte => - java.lang.Byte.valueOf(`d₂₂`.toByteExact) - case d if `d₂₃`.isValidShort => - java.lang.Short.valueOf(`d₂₃`.toShortExact) - case d if `d₂₄`.isValidInt => - java.lang.Integer.valueOf(`d₂₄`.toIntExact) - case d if `d₂₅`.isValidLong => - java.lang.Long.valueOf(`d₂₅`.toLongExact) - case d if `d₂₆`.isDecimalFloat => - java.lang.Float.valueOf(`d₂₆`.toFloat) - case d if `d₂₇`.isDecimalDouble => - java.lang.Double.valueOf(`d₂₇`.toDouble) - case d => - (`d₂₈`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _n5 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₅`) match { - case d if `d₂₉`.isValidByte => - java.lang.Byte.valueOf(`d₂₉`.toByteExact) - case d if `d₃₀`.isValidShort => - java.lang.Short.valueOf(`d₃₀`.toShortExact) - case d if `d₃₁`.isValidInt => - java.lang.Integer.valueOf(`d₃₁`.toIntExact) - case d if `d₃₂`.isValidLong => - java.lang.Long.valueOf(`d₃₂`.toLongExact) - case d if `d₃₃`.isDecimalFloat => - java.lang.Float.valueOf(`d₃₃`.toFloat) - case d if `d₃₄`.isDecimalDouble => - java.lang.Double.valueOf(`d₃₄`.toDouble) - case d => - (`d₃₅`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n5"), `in₂`) - case 5 => - if (required.&(32).!=(0)) { - required = required.^(32) - _n6 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₆`) match { - case d if `d₃₆`.isValidByte => - java.lang.Byte.valueOf(`d₃₆`.toByteExact) - case d if `d₃₇`.isValidShort => - java.lang.Short.valueOf(`d₃₇`.toShortExact) - case d if `d₃₈`.isValidInt => - java.lang.Integer.valueOf(`d₃₈`.toIntExact) - case d if `d₃₉`.isValidLong => - java.lang.Long.valueOf(`d₃₉`.toLongExact) - case d if `d₄₀`.isDecimalFloat => - java.lang.Float.valueOf(`d₄₀`.toFloat) - case d if `d₄₁`.isDecimalDouble => - java.lang.Double.valueOf(`d₄₁`.toDouble) - case d => - (`d₄₂`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n6"), `in₂`) - case 6 => - if (required.&(64).!=(0)) { - required = required.^(64) - _n7 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₇`) match { - case d if `d₄₃`.isValidByte => - java.lang.Byte.valueOf(`d₄₃`.toByteExact) - case d if `d₄₄`.isValidShort => - java.lang.Short.valueOf(`d₄₄`.toShortExact) - case d if `d₄₅`.isValidInt => - java.lang.Integer.valueOf(`d₄₅`.toIntExact) - case d if `d₄₆`.isValidLong => - java.lang.Long.valueOf(`d₄₆`.toLongExact) - case d if `d₄₇`.isDecimalFloat => - java.lang.Float.valueOf(`d₄₇`.toFloat) - case d if `d₄₈`.isDecimalDouble => - java.lang.Double.valueOf(`d₄₈`.toDouble) - case d => - (`d₄₉`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n7"), `in₂`) - case 7 => - if (required.&(128).!=(0)) { - required = required.^(128) - _n8 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₈`) match { - case d if `d₅₀`.isValidByte => - java.lang.Byte.valueOf(`d₅₀`.toByteExact) - case d if `d₅₁`.isValidShort => - java.lang.Short.valueOf(`d₅₁`.toShortExact) - case d if `d₅₂`.isValidInt => - java.lang.Integer.valueOf(`d₅₂`.toIntExact) - case d if `d₅₃`.isValidLong => - java.lang.Long.valueOf(`d₅₃`.toLongExact) - case d if `d₅₄`.isDecimalFloat => - java.lang.Float.valueOf(`d₅₄`.toFloat) - case d if `d₅₅`.isDecimalDouble => - java.lang.Double.valueOf(`d₅₅`.toDouble) - case d => - (`d₅₆`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n8"), `in₂`) - case 8 => - if (required.&(256).!=(0)) { - required = required.^(256) - _n9 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₉`) match { - case d if `d₅₇`.isValidByte => - java.lang.Byte.valueOf(`d₅₇`.toByteExact) - case d if `d₅₈`.isValidShort => - java.lang.Short.valueOf(`d₅₈`.toShortExact) - case d if `d₅₉`.isValidInt => - java.lang.Integer.valueOf(`d₅₉`.toIntExact) - case d if `d₆₀`.isValidLong => - java.lang.Long.valueOf(`d₆₀`.toLongExact) - case d if `d₆₁`.isDecimalFloat => - java.lang.Float.valueOf(`d₆₁`.toFloat) - case d if `d₆₂`.isDecimalDouble => - java.lang.Double.valueOf(`d₆₂`.toDouble) - case d => - (`d₆₃`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n9"), `in₂`) - case 9 => - if (required.&(512).!=(0)) { - required = required.^(512) - _n10 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₁₀`) match { - case d if `d₆₄`.isValidByte => - java.lang.Byte.valueOf(`d₆₄`.toByteExact) - case d if `d₆₅`.isValidShort => - java.lang.Short.valueOf(`d₆₅`.toShortExact) - case d if `d₆₆`.isValidInt => - java.lang.Integer.valueOf(`d₆₆`.toIntExact) - case d if `d₆₇`.isValidLong => - java.lang.Long.valueOf(`d₆₇`.toLongExact) - case d if `d₆₈`.isDecimalFloat => - java.lang.Float.valueOf(`d₆₈`.toFloat) - case d if `d₆₉`.isDecimalDouble => - java.lang.Double.valueOf(`d₆₉`.toDouble) - case d => - (`d₇₀`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n10"), `in₂`) - case 10 => - if (required.&(1024).!=(0)) { - required = required.^(1024) - _n11 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₁₁`) match { - case d if `d₇₁`.isValidByte => - java.lang.Byte.valueOf(`d₇₁`.toByteExact) - case d if `d₇₂`.isValidShort => - java.lang.Short.valueOf(`d₇₂`.toShortExact) - case d if `d₇₃`.isValidInt => - java.lang.Integer.valueOf(`d₇₃`.toIntExact) - case d if `d₇₄`.isValidLong => - java.lang.Long.valueOf(`d₇₄`.toLongExact) - case d if `d₇₅`.isDecimalFloat => - java.lang.Float.valueOf(`d₇₅`.toFloat) - case d if `d₇₆`.isDecimalDouble => - java.lang.Double.valueOf(`d₇₆`.toDouble) - case d => - (`d₇₇`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n11"), `in₂`) - case 11 => - if (required.&(2048).!=(0)) { - required = required.^(2048) - _n12 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₁₂`) match { - case d if `d₇₈`.isValidByte => - java.lang.Byte.valueOf(`d₇₈`.toByteExact) - case d if `d₇₉`.isValidShort => - java.lang.Short.valueOf(`d₇₉`.toShortExact) - case d if `d₈₀`.isValidInt => - java.lang.Integer.valueOf(`d₈₀`.toIntExact) - case d if `d₈₁`.isValidLong => - java.lang.Long.valueOf(`d₈₁`.toLongExact) - case d if `d₈₂`.isDecimalFloat => - java.lang.Float.valueOf(`d₈₂`.toFloat) - case d if `d₈₃`.isDecimalDouble => - java.lang.Double.valueOf(`d₈₃`.toDouble) - case d => - (`d₈₄`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n12"), `in₂`) - case 12 => - if (required.&(4096).!=(0)) { - required = required.^(4096) - _n13 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₁₃`) match { - case d if `d₈₅`.isValidByte => - java.lang.Byte.valueOf(`d₈₅`.toByteExact) - case d if `d₈₆`.isValidShort => - java.lang.Short.valueOf(`d₈₆`.toShortExact) - case d if `d₈₇`.isValidInt => - java.lang.Integer.valueOf(`d₈₇`.toIntExact) - case d if `d₈₈`.isValidLong => - java.lang.Long.valueOf(`d₈₈`.toLongExact) - case d if `d₈₉`.isDecimalFloat => - java.lang.Float.valueOf(`d₈₉`.toFloat) - case d if `d₉₀`.isDecimalDouble => - java.lang.Double.valueOf(`d₉₀`.toDouble) - case d => - (`d₉₁`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n13"), `in₂`) - case 13 => - if (required.&(8192).!=(0)) { - required = required.^(8192) - _n14 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₁₄`) match { - case d if `d₉₂`.isValidByte => - java.lang.Byte.valueOf(`d₉₂`.toByteExact) - case d if `d₉₃`.isValidShort => - java.lang.Short.valueOf(`d₉₃`.toShortExact) - case d if `d₉₄`.isValidInt => - java.lang.Integer.valueOf(`d₉₄`.toIntExact) - case d if `d₉₅`.isValidLong => - java.lang.Long.valueOf(`d₉₅`.toLongExact) - case d if `d₉₆`.isDecimalFloat => - java.lang.Float.valueOf(`d₉₆`.toFloat) - case d if `d₉₇`.isDecimalDouble => - java.lang.Double.valueOf(`d₉₇`.toDouble) - case d => - (`d₉₈`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n14"), `in₂`) - case 14 => - if (required.&(16384).!=(0)) { - required = required.^(16384) - _n15 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₁₅`) match { - case d if `d₉₉`.isValidByte => - java.lang.Byte.valueOf(`d₉₉`.toByteExact) - case d if `d₁₀₀`.isValidShort => - java.lang.Short.valueOf(`d₁₀₀`.toShortExact) - case d if `d₁₀₁`.isValidInt => - java.lang.Integer.valueOf(`d₁₀₁`.toIntExact) - case d if `d₁₀₂`.isValidLong => - java.lang.Long.valueOf(`d₁₀₂`.toLongExact) - case d if `d₁₀₃`.isDecimalFloat => - java.lang.Float.valueOf(`d₁₀₃`.toFloat) - case d if `d₁₀₄`.isDecimalDouble => - java.lang.Double.valueOf(`d₁₀₄`.toDouble) - case d => - (`d₁₀₅`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n15"), `in₂`) - case 15 => - if (required.&(32768).!=(0)) { - required = required.^(32768) - _n16 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₁₆`) match { - case d if `d₁₀₆`.isValidByte => - java.lang.Byte.valueOf(`d₁₀₆`.toByteExact) - case d if `d₁₀₇`.isValidShort => - java.lang.Short.valueOf(`d₁₀₇`.toShortExact) - case d if `d₁₀₈`.isValidInt => - java.lang.Integer.valueOf(`d₁₀₈`.toIntExact) - case d if `d₁₀₉`.isValidLong => - java.lang.Long.valueOf(`d₁₀₉`.toLongExact) - case d if `d₁₁₀`.isDecimalFloat => - java.lang.Float.valueOf(`d₁₁₀`.toFloat) - case d if `d₁₁₁`.isDecimalDouble => - java.lang.Double.valueOf(`d₁₁₁`.toDouble) - case d => - (`d₁₁₂`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n16"), `in₂`) - case 16 => - if (required.&(65536).!=(0)) { - required = required.^(65536) - _n17 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₁₇`) match { - case d if `d₁₁₃`.isValidByte => - java.lang.Byte.valueOf(`d₁₁₃`.toByteExact) - case d if `d₁₁₄`.isValidShort => - java.lang.Short.valueOf(`d₁₁₄`.toShortExact) - case d if `d₁₁₅`.isValidInt => - java.lang.Integer.valueOf(`d₁₁₅`.toIntExact) - case d if `d₁₁₆`.isValidLong => - java.lang.Long.valueOf(`d₁₁₆`.toLongExact) - case d if `d₁₁₇`.isDecimalFloat => - java.lang.Float.valueOf(`d₁₁₇`.toFloat) - case d if `d₁₁₈`.isDecimalDouble => - java.lang.Double.valueOf(`d₁₁₈`.toDouble) - case d => - (`d₁₁₉`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n17"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJNumber_fields) - } - if (required.&(131071).==(0)) new co.blocke.scalajack.json.primitives.SampleJNumber(_n1, _n2, _n3, _n4, _n5, _n6, _n7, _n8, _n9, _n10, _n11, _n12, _n13, _n14, _n15, _n16, _n17) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("n1", "n2", "n3", "n4", "n5", "n6", "n7", "n8", "n9", "n10", "n11", "n12", "n13", "n14", "n15", "n16", "n17")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(131071)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJNumber] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJNumber, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJNumber = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJNumber]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleJNumber_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("n1", "n2", "n3", "n4", "n5", "n6", "n7", "n8", "n9", "n10", "n11", "n12", "n13", "n14", "n15", "n16", "n17")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleJNumber, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("n1") - out.value(in.n1) - out.label("n2") - out.value(in.n2) - out.label("n3") - out.value(in.n3) - out.label("n4") - out.value(in.n4) - out.label("n5") - out.value(in.n5) - out.label("n6") - out.value(in.n6) - out.label("n7") - out.value(in.n7) - out.label("n8") - out.value(in.n8) - out.label("n9") - out.value(in.n9) - out.label("n10") - out.value(in.n10) - out.label("n11") - out.value(in.n11) - out.label("n12") - out.value(in.n12) - out.label("n13") - out.value(in.n13) - out.label("n14") - out.value(in.n14) - out.label("n15") - out.value(in.n15) - out.label("n16") - out.value(in.n16) - out.label("n17") - out.value(in.n17) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJNumber = { - var _n1: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n2: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n3: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n4: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n5: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n6: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n7: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n8: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n9: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n10: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n11: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n12: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n13: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n14: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n15: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n16: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n17: java.lang.Number = null.asInstanceOf[java.lang.Number] - var required: scala.Int = 131071 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJNumber_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJNumber] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _n1 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(n) match { - case d if d.isValidByte => - java.lang.Byte.valueOf(d.toByteExact) - case d if `d₂`.isValidShort => - java.lang.Short.valueOf(`d₂`.toShortExact) - case d if `d₃`.isValidInt => - java.lang.Integer.valueOf(`d₃`.toIntExact) - case d if `d₄`.isValidLong => - java.lang.Long.valueOf(`d₄`.toLongExact) - case d if `d₅`.isDecimalFloat => - java.lang.Float.valueOf(`d₅`.toFloat) - case d if `d₆`.isDecimalDouble => - java.lang.Double.valueOf(`d₆`.toDouble) - case d => - (`d₇`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _n2 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₂`) match { - case d if `d₈`.isValidByte => - java.lang.Byte.valueOf(`d₈`.toByteExact) - case d if `d₉`.isValidShort => - java.lang.Short.valueOf(`d₉`.toShortExact) - case d if `d₁₀`.isValidInt => - java.lang.Integer.valueOf(`d₁₀`.toIntExact) - case d if `d₁₁`.isValidLong => - java.lang.Long.valueOf(`d₁₁`.toLongExact) - case d if `d₁₂`.isDecimalFloat => - java.lang.Float.valueOf(`d₁₂`.toFloat) - case d if `d₁₃`.isDecimalDouble => - java.lang.Double.valueOf(`d₁₃`.toDouble) - case d => - (`d₁₄`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _n3 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₃`) match { - case d if `d₁₅`.isValidByte => - java.lang.Byte.valueOf(`d₁₅`.toByteExact) - case d if `d₁₆`.isValidShort => - java.lang.Short.valueOf(`d₁₆`.toShortExact) - case d if `d₁₇`.isValidInt => - java.lang.Integer.valueOf(`d₁₇`.toIntExact) - case d if `d₁₈`.isValidLong => - java.lang.Long.valueOf(`d₁₈`.toLongExact) - case d if `d₁₉`.isDecimalFloat => - java.lang.Float.valueOf(`d₁₉`.toFloat) - case d if `d₂₀`.isDecimalDouble => - java.lang.Double.valueOf(`d₂₀`.toDouble) - case d => - (`d₂₁`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _n4 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₄`) match { - case d if `d₂₂`.isValidByte => - java.lang.Byte.valueOf(`d₂₂`.toByteExact) - case d if `d₂₃`.isValidShort => - java.lang.Short.valueOf(`d₂₃`.toShortExact) - case d if `d₂₄`.isValidInt => - java.lang.Integer.valueOf(`d₂₄`.toIntExact) - case d if `d₂₅`.isValidLong => - java.lang.Long.valueOf(`d₂₅`.toLongExact) - case d if `d₂₆`.isDecimalFloat => - java.lang.Float.valueOf(`d₂₆`.toFloat) - case d if `d₂₇`.isDecimalDouble => - java.lang.Double.valueOf(`d₂₇`.toDouble) - case d => - (`d₂₈`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _n5 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₅`) match { - case d if `d₂₉`.isValidByte => - java.lang.Byte.valueOf(`d₂₉`.toByteExact) - case d if `d₃₀`.isValidShort => - java.lang.Short.valueOf(`d₃₀`.toShortExact) - case d if `d₃₁`.isValidInt => - java.lang.Integer.valueOf(`d₃₁`.toIntExact) - case d if `d₃₂`.isValidLong => - java.lang.Long.valueOf(`d₃₂`.toLongExact) - case d if `d₃₃`.isDecimalFloat => - java.lang.Float.valueOf(`d₃₃`.toFloat) - case d if `d₃₄`.isDecimalDouble => - java.lang.Double.valueOf(`d₃₄`.toDouble) - case d => - (`d₃₅`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n5"), `in₂`) - case 5 => - if (required.&(32).!=(0)) { - required = required.^(32) - _n6 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₆`) match { - case d if `d₃₆`.isValidByte => - java.lang.Byte.valueOf(`d₃₆`.toByteExact) - case d if `d₃₇`.isValidShort => - java.lang.Short.valueOf(`d₃₇`.toShortExact) - case d if `d₃₈`.isValidInt => - java.lang.Integer.valueOf(`d₃₈`.toIntExact) - case d if `d₃₉`.isValidLong => - java.lang.Long.valueOf(`d₃₉`.toLongExact) - case d if `d₄₀`.isDecimalFloat => - java.lang.Float.valueOf(`d₄₀`.toFloat) - case d if `d₄₁`.isDecimalDouble => - java.lang.Double.valueOf(`d₄₁`.toDouble) - case d => - (`d₄₂`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n6"), `in₂`) - case 6 => - if (required.&(64).!=(0)) { - required = required.^(64) - _n7 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₇`) match { - case d if `d₄₃`.isValidByte => - java.lang.Byte.valueOf(`d₄₃`.toByteExact) - case d if `d₄₄`.isValidShort => - java.lang.Short.valueOf(`d₄₄`.toShortExact) - case d if `d₄₅`.isValidInt => - java.lang.Integer.valueOf(`d₄₅`.toIntExact) - case d if `d₄₆`.isValidLong => - java.lang.Long.valueOf(`d₄₆`.toLongExact) - case d if `d₄₇`.isDecimalFloat => - java.lang.Float.valueOf(`d₄₇`.toFloat) - case d if `d₄₈`.isDecimalDouble => - java.lang.Double.valueOf(`d₄₈`.toDouble) - case d => - (`d₄₉`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n7"), `in₂`) - case 7 => - if (required.&(128).!=(0)) { - required = required.^(128) - _n8 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₈`) match { - case d if `d₅₀`.isValidByte => - java.lang.Byte.valueOf(`d₅₀`.toByteExact) - case d if `d₅₁`.isValidShort => - java.lang.Short.valueOf(`d₅₁`.toShortExact) - case d if `d₅₂`.isValidInt => - java.lang.Integer.valueOf(`d₅₂`.toIntExact) - case d if `d₅₃`.isValidLong => - java.lang.Long.valueOf(`d₅₃`.toLongExact) - case d if `d₅₄`.isDecimalFloat => - java.lang.Float.valueOf(`d₅₄`.toFloat) - case d if `d₅₅`.isDecimalDouble => - java.lang.Double.valueOf(`d₅₅`.toDouble) - case d => - (`d₅₆`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n8"), `in₂`) - case 8 => - if (required.&(256).!=(0)) { - required = required.^(256) - _n9 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₉`) match { - case d if `d₅₇`.isValidByte => - java.lang.Byte.valueOf(`d₅₇`.toByteExact) - case d if `d₅₈`.isValidShort => - java.lang.Short.valueOf(`d₅₈`.toShortExact) - case d if `d₅₉`.isValidInt => - java.lang.Integer.valueOf(`d₅₉`.toIntExact) - case d if `d₆₀`.isValidLong => - java.lang.Long.valueOf(`d₆₀`.toLongExact) - case d if `d₆₁`.isDecimalFloat => - java.lang.Float.valueOf(`d₆₁`.toFloat) - case d if `d₆₂`.isDecimalDouble => - java.lang.Double.valueOf(`d₆₂`.toDouble) - case d => - (`d₆₃`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n9"), `in₂`) - case 9 => - if (required.&(512).!=(0)) { - required = required.^(512) - _n10 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₁₀`) match { - case d if `d₆₄`.isValidByte => - java.lang.Byte.valueOf(`d₆₄`.toByteExact) - case d if `d₆₅`.isValidShort => - java.lang.Short.valueOf(`d₆₅`.toShortExact) - case d if `d₆₆`.isValidInt => - java.lang.Integer.valueOf(`d₆₆`.toIntExact) - case d if `d₆₇`.isValidLong => - java.lang.Long.valueOf(`d₆₇`.toLongExact) - case d if `d₆₈`.isDecimalFloat => - java.lang.Float.valueOf(`d₆₈`.toFloat) - case d if `d₆₉`.isDecimalDouble => - java.lang.Double.valueOf(`d₆₉`.toDouble) - case d => - (`d₇₀`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n10"), `in₂`) - case 10 => - if (required.&(1024).!=(0)) { - required = required.^(1024) - _n11 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₁₁`) match { - case d if `d₇₁`.isValidByte => - java.lang.Byte.valueOf(`d₇₁`.toByteExact) - case d if `d₇₂`.isValidShort => - java.lang.Short.valueOf(`d₇₂`.toShortExact) - case d if `d₇₃`.isValidInt => - java.lang.Integer.valueOf(`d₇₃`.toIntExact) - case d if `d₇₄`.isValidLong => - java.lang.Long.valueOf(`d₇₄`.toLongExact) - case d if `d₇₅`.isDecimalFloat => - java.lang.Float.valueOf(`d₇₅`.toFloat) - case d if `d₇₆`.isDecimalDouble => - java.lang.Double.valueOf(`d₇₆`.toDouble) - case d => - (`d₇₇`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n11"), `in₂`) - case 11 => - if (required.&(2048).!=(0)) { - required = required.^(2048) - _n12 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₁₂`) match { - case d if `d₇₈`.isValidByte => - java.lang.Byte.valueOf(`d₇₈`.toByteExact) - case d if `d₇₉`.isValidShort => - java.lang.Short.valueOf(`d₇₉`.toShortExact) - case d if `d₈₀`.isValidInt => - java.lang.Integer.valueOf(`d₈₀`.toIntExact) - case d if `d₈₁`.isValidLong => - java.lang.Long.valueOf(`d₈₁`.toLongExact) - case d if `d₈₂`.isDecimalFloat => - java.lang.Float.valueOf(`d₈₂`.toFloat) - case d if `d₈₃`.isDecimalDouble => - java.lang.Double.valueOf(`d₈₃`.toDouble) - case d => - (`d₈₄`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n12"), `in₂`) - case 12 => - if (required.&(4096).!=(0)) { - required = required.^(4096) - _n13 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₁₃`) match { - case d if `d₈₅`.isValidByte => - java.lang.Byte.valueOf(`d₈₅`.toByteExact) - case d if `d₈₆`.isValidShort => - java.lang.Short.valueOf(`d₈₆`.toShortExact) - case d if `d₈₇`.isValidInt => - java.lang.Integer.valueOf(`d₈₇`.toIntExact) - case d if `d₈₈`.isValidLong => - java.lang.Long.valueOf(`d₈₈`.toLongExact) - case d if `d₈₉`.isDecimalFloat => - java.lang.Float.valueOf(`d₈₉`.toFloat) - case d if `d₉₀`.isDecimalDouble => - java.lang.Double.valueOf(`d₉₀`.toDouble) - case d => - (`d₉₁`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n13"), `in₂`) - case 13 => - if (required.&(8192).!=(0)) { - required = required.^(8192) - _n14 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₁₄`) match { - case d if `d₉₂`.isValidByte => - java.lang.Byte.valueOf(`d₉₂`.toByteExact) - case d if `d₉₃`.isValidShort => - java.lang.Short.valueOf(`d₉₃`.toShortExact) - case d if `d₉₄`.isValidInt => - java.lang.Integer.valueOf(`d₉₄`.toIntExact) - case d if `d₉₅`.isValidLong => - java.lang.Long.valueOf(`d₉₅`.toLongExact) - case d if `d₉₆`.isDecimalFloat => - java.lang.Float.valueOf(`d₉₆`.toFloat) - case d if `d₉₇`.isDecimalDouble => - java.lang.Double.valueOf(`d₉₇`.toDouble) - case d => - (`d₉₈`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n14"), `in₂`) - case 14 => - if (required.&(16384).!=(0)) { - required = required.^(16384) - _n15 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₁₅`) match { - case d if `d₉₉`.isValidByte => - java.lang.Byte.valueOf(`d₉₉`.toByteExact) - case d if `d₁₀₀`.isValidShort => - java.lang.Short.valueOf(`d₁₀₀`.toShortExact) - case d if `d₁₀₁`.isValidInt => - java.lang.Integer.valueOf(`d₁₀₁`.toIntExact) - case d if `d₁₀₂`.isValidLong => - java.lang.Long.valueOf(`d₁₀₂`.toLongExact) - case d if `d₁₀₃`.isDecimalFloat => - java.lang.Float.valueOf(`d₁₀₃`.toFloat) - case d if `d₁₀₄`.isDecimalDouble => - java.lang.Double.valueOf(`d₁₀₄`.toDouble) - case d => - (`d₁₀₅`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n15"), `in₂`) - case 15 => - if (required.&(32768).!=(0)) { - required = required.^(32768) - _n16 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₁₆`) match { - case d if `d₁₀₆`.isValidByte => - java.lang.Byte.valueOf(`d₁₀₆`.toByteExact) - case d if `d₁₀₇`.isValidShort => - java.lang.Short.valueOf(`d₁₀₇`.toShortExact) - case d if `d₁₀₈`.isValidInt => - java.lang.Integer.valueOf(`d₁₀₈`.toIntExact) - case d if `d₁₀₉`.isValidLong => - java.lang.Long.valueOf(`d₁₀₉`.toLongExact) - case d if `d₁₁₀`.isDecimalFloat => - java.lang.Float.valueOf(`d₁₁₀`.toFloat) - case d if `d₁₁₁`.isDecimalDouble => - java.lang.Double.valueOf(`d₁₁₁`.toDouble) - case d => - (`d₁₁₂`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n16"), `in₂`) - case 16 => - if (required.&(65536).!=(0)) { - required = required.^(65536) - _n17 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₁₇`) match { - case d if `d₁₁₃`.isValidByte => - java.lang.Byte.valueOf(`d₁₁₃`.toByteExact) - case d if `d₁₁₄`.isValidShort => - java.lang.Short.valueOf(`d₁₁₄`.toShortExact) - case d if `d₁₁₅`.isValidInt => - java.lang.Integer.valueOf(`d₁₁₅`.toIntExact) - case d if `d₁₁₆`.isValidLong => - java.lang.Long.valueOf(`d₁₁₆`.toLongExact) - case d if `d₁₁₇`.isDecimalFloat => - java.lang.Float.valueOf(`d₁₁₇`.toFloat) - case d if `d₁₁₈`.isDecimalDouble => - java.lang.Double.valueOf(`d₁₁₈`.toDouble) - case d => - (`d₁₁₉`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n17"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJNumber_fields) - } - if (required.&(131071).==(0)) new co.blocke.scalajack.json.primitives.SampleJNumber(_n1, _n2, _n3, _n4, _n5, _n6, _n7, _n8, _n9, _n10, _n11, _n12, _n13, _n14, _n15, _n16, _n17) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("n1", "n2", "n3", "n4", "n5", "n6", "n7", "n8", "n9", "n10", "n11", "n12", "n13", "n14", "n15", "n16", "n17")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(131071)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJNumber] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJNumber, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJNumber = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJNumber]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleJShort_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("s1", "s2", "s3", "s4", "s5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleJShort, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("s1") - out.value(in.s1) - out.label("s2") - out.value(in.s2) - out.label("s3") - out.value(in.s3) - out.label("s4") - out.value(in.s4) - out.label("s5") - out.value(in.s5) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJShort = { - var _s1: java.lang.Short = java.lang.Short.valueOf(0.toShort) - var _s2: java.lang.Short = java.lang.Short.valueOf(0.toShort) - var _s3: java.lang.Short = java.lang.Short.valueOf(0.toShort) - var _s4: java.lang.Short = java.lang.Short.valueOf(0.toShort) - var _s5: java.lang.Short = java.lang.Short.valueOf(0.toShort) - var required: scala.Int = 31 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJShort_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJShort] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _s1 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Short.valueOf(n) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _s2 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Short.valueOf(`n₂`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _s3 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Short.valueOf(`n₃`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _s4 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Short.valueOf(`n₄`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _s5 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Short.valueOf(`n₅`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s5"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJShort_fields) - } - if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJShort(_s1, _s2, _s3, _s4, _s5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("s1", "s2", "s3", "s4", "s5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJShort] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJShort, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJShort = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJShort]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleJShort_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("s1", "s2", "s3", "s4", "s5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleJShort, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("s1") - out.value(in.s1) - out.label("s2") - out.value(in.s2) - out.label("s3") - out.value(in.s3) - out.label("s4") - out.value(in.s4) - out.label("s5") - out.value(in.s5) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJShort = { - var _s1: java.lang.Short = java.lang.Short.valueOf(0.toShort) - var _s2: java.lang.Short = java.lang.Short.valueOf(0.toShort) - var _s3: java.lang.Short = java.lang.Short.valueOf(0.toShort) - var _s4: java.lang.Short = java.lang.Short.valueOf(0.toShort) - var _s5: java.lang.Short = java.lang.Short.valueOf(0.toShort) - var required: scala.Int = 31 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJShort_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJShort] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _s1 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Short.valueOf(n) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _s2 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Short.valueOf(`n₂`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _s3 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Short.valueOf(`n₃`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _s4 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Short.valueOf(`n₄`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _s5 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Short.valueOf(`n₅`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s5"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJShort_fields) - } - if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJShort(_s1, _s2, _s3, _s4, _s5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("s1", "s2", "s3", "s4", "s5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJShort] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJShort, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJShort = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJShort]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleJBigDecimal_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("bd1", "bd2", "bd3", "bd4", "bd5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleJBigDecimal, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("bd1") - out.value(in.bd1) - out.label("bd2") - out.value(in.bd2) - out.label("bd3") - out.value(in.bd3) - out.label("bd4") - out.value(in.bd4) - out.label("bd5") - out.value(in.bd5) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJBigDecimal = { - var _bd1: java.math.BigDecimal = new java.math.BigDecimal(0.0) - var _bd2: java.math.BigDecimal = new java.math.BigDecimal(0.0) - var _bd3: java.math.BigDecimal = new java.math.BigDecimal(0.0) - var _bd4: java.math.BigDecimal = new java.math.BigDecimal(0.0) - var _bd5: java.math.BigDecimal = new java.math.BigDecimal(0.0) - var required: scala.Int = 31 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJBigDecimal_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJBigDecimal] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _bd1 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - new java.math.BigDecimal(n) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _bd2 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - new java.math.BigDecimal(`n₂`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _bd3 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - new java.math.BigDecimal(`n₃`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _bd4 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - new java.math.BigDecimal(`n₄`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _bd5 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - new java.math.BigDecimal(`n₅`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd5"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJBigDecimal_fields) - } - if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJBigDecimal(_bd1, _bd2, _bd3, _bd4, _bd5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("bd1", "bd2", "bd3", "bd4", "bd5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJBigDecimal] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJBigDecimal, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJBigDecimal = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJBigDecimal]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleJBigInteger_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("bi1", "bi2", "bi3", "bi4", "bi5", "bi6", "bi7")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleJBigInteger, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("bi1") - out.value(in.bi1) - out.label("bi2") - out.value(in.bi2) - out.label("bi3") - out.value(in.bi3) - out.label("bi4") - out.value(in.bi4) - out.label("bi5") - out.value(in.bi5) - out.label("bi6") - out.value(in.bi6) - out.label("bi7") - out.value(in.bi7) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJBigInteger = { - var _bi1: java.math.BigInteger = java.math.BigInteger.valueOf(0L) - var _bi2: java.math.BigInteger = java.math.BigInteger.valueOf(0L) - var _bi3: java.math.BigInteger = java.math.BigInteger.valueOf(0L) - var _bi4: java.math.BigInteger = java.math.BigInteger.valueOf(0L) - var _bi5: java.math.BigInteger = java.math.BigInteger.valueOf(0L) - var _bi6: java.math.BigInteger = java.math.BigInteger.valueOf(0L) - var _bi7: java.math.BigInteger = java.math.BigInteger.valueOf(0L) - var required: scala.Int = 127 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJBigInteger_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJBigInteger] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _bi1 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - new java.math.BigInteger(n) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _bi2 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - new java.math.BigInteger(`n₂`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _bi3 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - new java.math.BigInteger(`n₃`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _bi4 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - new java.math.BigInteger(`n₄`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _bi5 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - new java.math.BigInteger(`n₅`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi5"), `in₂`) - case 5 => - if (required.&(32).!=(0)) { - required = required.^(32) - _bi6 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - new java.math.BigInteger(`n₆`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi6"), `in₂`) - case 6 => - if (required.&(64).!=(0)) { - required = required.^(64) - _bi7 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - new java.math.BigInteger(`n₇`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi7"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJBigInteger_fields) - } - if (required.&(127).==(0)) new co.blocke.scalajack.json.primitives.SampleJBigInteger(_bi1, _bi2, _bi3, _bi4, _bi5, _bi6, _bi7) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("bi1", "bi2", "bi3", "bi4", "bi5", "bi6", "bi7")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(127)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJBigInteger] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJBigInteger, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJBigInteger = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJBigInteger]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleJBoolean_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("bool1", "bool2", "bool3", "bool4", "bool5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleJBoolean, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("bool1") - out.value(in.bool1) - out.label("bool2") - out.value(in.bool2) - out.label("bool3") - out.value(in.bool3) - out.label("bool4") - out.value(in.bool4) - out.label("bool5") - out.value(in.bool5) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJBoolean = { - var _bool1: java.lang.Boolean = java.lang.Boolean.FALSE - var _bool2: java.lang.Boolean = java.lang.Boolean.FALSE - var _bool3: java.lang.Boolean = java.lang.Boolean.FALSE - var _bool4: java.lang.Boolean = java.lang.Boolean.FALSE - var _bool5: java.lang.Boolean = java.lang.Boolean.FALSE - var required: scala.Int = 31 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJBoolean_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJBoolean] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _bool1 = `in₂`.expectJavaBoolean() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bool1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _bool2 = `in₂`.expectJavaBoolean() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bool2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _bool3 = `in₂`.expectJavaBoolean() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bool3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _bool4 = `in₂`.expectJavaBoolean() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bool4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _bool5 = `in₂`.expectJavaBoolean() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bool5"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJBoolean_fields) - } - if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJBoolean(_bool1, _bool2, _bool3, _bool4, _bool5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("bool1", "bool2", "bool3", "bool4", "bool5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJBoolean] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJBoolean, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJBoolean = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJBoolean]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleJByte_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("b1", "b2", "b3", "b4", "b5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleJByte, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("b1") - out.value(in.b1) - out.label("b2") - out.value(in.b2) - out.label("b3") - out.value(in.b3) - out.label("b4") - out.value(in.b4) - out.label("b5") - out.value(in.b5) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJByte = { - var _b1: java.lang.Byte = scala.Predef.byte2Byte(java.lang.Byte.MIN_VALUE) - var _b2: java.lang.Byte = scala.Predef.byte2Byte(java.lang.Byte.MIN_VALUE) - var _b3: java.lang.Byte = scala.Predef.byte2Byte(java.lang.Byte.MIN_VALUE) - var _b4: java.lang.Byte = scala.Predef.byte2Byte(java.lang.Byte.MIN_VALUE) - var _b5: java.lang.Byte = scala.Predef.byte2Byte(java.lang.Byte.MIN_VALUE) - var required: scala.Int = 31 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJByte_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJByte] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _b1 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Byte.valueOf(n) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _b2 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Byte.valueOf(`n₂`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _b3 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Byte.valueOf(`n₃`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _b4 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Byte.valueOf(`n₄`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _b5 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Byte.valueOf(`n₅`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b5"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJByte_fields) - } - if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJByte(_b1, _b2, _b3, _b4, _b5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("b1", "b2", "b3", "b4", "b5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJByte] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJByte, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJByte = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJByte]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleJChar_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("c1", "c2", "c3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleJChar, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("c1") - out.value(in.c1) - out.label("c2") - out.value(in.c2) - out.label("c3") - out.value(in.c3) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJChar = { - var _c1: java.lang.Character = java.lang.Character.valueOf('x') - var _c2: java.lang.Character = java.lang.Character.valueOf('x') - var _c3: java.lang.Character = java.lang.Character.valueOf('x') - var required: scala.Int = 7 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJChar_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJChar] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _c1 = { - val c: java.lang.String = `in₂`.expectString() - if (c.==(null)) null else if (c.length().==(0)) { - `in₂`.backspace() - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Character value expected but empty string found in json", `in₂`) - } else java.lang.Character.valueOf(c.charAt(0)) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _c2 = { - val `c₂`: java.lang.String = `in₂`.expectString() - if (`c₂`.==(null)) null else if (`c₂`.length().==(0)) { - `in₂`.backspace() - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Character value expected but empty string found in json", `in₂`) - } else java.lang.Character.valueOf(`c₂`.charAt(0)) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _c3 = { - val `c₃`: java.lang.String = `in₂`.expectString() - if (`c₃`.==(null)) null else if (`c₃`.length().==(0)) { - `in₂`.backspace() - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Character value expected but empty string found in json", `in₂`) - } else java.lang.Character.valueOf(`c₃`.charAt(0)) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c3"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJChar_fields) - } - if (required.&(7).==(0)) new co.blocke.scalajack.json.primitives.SampleJChar(_c1, _c2, _c3) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("c1", "c2", "c3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(7)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJChar] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJChar, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJChar = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJChar]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleJChar_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("c1", "c2", "c3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleJChar, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("c1") - out.value(in.c1) - out.label("c2") - out.value(in.c2) - out.label("c3") - out.value(in.c3) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJChar = { - var _c1: java.lang.Character = java.lang.Character.valueOf('x') - var _c2: java.lang.Character = java.lang.Character.valueOf('x') - var _c3: java.lang.Character = java.lang.Character.valueOf('x') - var required: scala.Int = 7 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJChar_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJChar] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _c1 = { - val c: java.lang.String = `in₂`.expectString() - if (c.==(null)) null else if (c.length().==(0)) { - `in₂`.backspace() - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Character value expected but empty string found in json", `in₂`) - } else java.lang.Character.valueOf(c.charAt(0)) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _c2 = { - val `c₂`: java.lang.String = `in₂`.expectString() - if (`c₂`.==(null)) null else if (`c₂`.length().==(0)) { - `in₂`.backspace() - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Character value expected but empty string found in json", `in₂`) - } else java.lang.Character.valueOf(`c₂`.charAt(0)) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _c3 = { - val `c₃`: java.lang.String = `in₂`.expectString() - if (`c₃`.==(null)) null else if (`c₃`.length().==(0)) { - `in₂`.backspace() - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Character value expected but empty string found in json", `in₂`) - } else java.lang.Character.valueOf(`c₃`.charAt(0)) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c3"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJChar_fields) - } - if (required.&(7).==(0)) new co.blocke.scalajack.json.primitives.SampleJChar(_c1, _c2, _c3) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("c1", "c2", "c3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(7)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJChar] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJChar, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJChar = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJChar]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleJChar_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("c1", "c2", "c3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleJChar, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("c1") - out.value(in.c1) - out.label("c2") - out.value(in.c2) - out.label("c3") - out.value(in.c3) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJChar = { - var _c1: java.lang.Character = java.lang.Character.valueOf('x') - var _c2: java.lang.Character = java.lang.Character.valueOf('x') - var _c3: java.lang.Character = java.lang.Character.valueOf('x') - var required: scala.Int = 7 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJChar_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJChar] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _c1 = { - val c: java.lang.String = `in₂`.expectString() - if (c.==(null)) null else if (c.length().==(0)) { - `in₂`.backspace() - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Character value expected but empty string found in json", `in₂`) - } else java.lang.Character.valueOf(c.charAt(0)) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _c2 = { - val `c₂`: java.lang.String = `in₂`.expectString() - if (`c₂`.==(null)) null else if (`c₂`.length().==(0)) { - `in₂`.backspace() - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Character value expected but empty string found in json", `in₂`) - } else java.lang.Character.valueOf(`c₂`.charAt(0)) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _c3 = { - val `c₃`: java.lang.String = `in₂`.expectString() - if (`c₃`.==(null)) null else if (`c₃`.length().==(0)) { - `in₂`.backspace() - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Character value expected but empty string found in json", `in₂`) - } else java.lang.Character.valueOf(`c₃`.charAt(0)) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c3"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJChar_fields) - } - if (required.&(7).==(0)) new co.blocke.scalajack.json.primitives.SampleJChar(_c1, _c2, _c3) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("c1", "c2", "c3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(7)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJChar] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJChar, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJChar = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJChar]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleJDouble_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4", "d5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleJDouble, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("d1") - out.value(in.d1) - out.label("d2") - out.value(in.d2) - out.label("d3") - out.value(in.d3) - out.label("d4") - out.value(in.d4) - out.label("d5") - out.value(in.d5) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJDouble = { - var _d1: java.lang.Double = java.lang.Double.valueOf(0.0) - var _d2: java.lang.Double = java.lang.Double.valueOf(0.0) - var _d3: java.lang.Double = java.lang.Double.valueOf(0.0) - var _d4: java.lang.Double = java.lang.Double.valueOf(0.0) - var _d5: java.lang.Double = java.lang.Double.valueOf(0.0) - var required: scala.Int = 31 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJDouble_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJDouble] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _d1 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Double.valueOf(n) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _d2 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Double.valueOf(`n₂`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _d3 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Double.valueOf(`n₃`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _d4 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Double.valueOf(`n₄`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _d5 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Double.valueOf(`n₅`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d5"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJDouble_fields) - } - if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJDouble(_d1, _d2, _d3, _d4, _d5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4", "d5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJDouble] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJDouble, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJDouble = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJDouble]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleJFloat_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("f1", "f2", "f3", "f4", "f5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleJFloat, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("f1") - out.value(in.f1) - out.label("f2") - out.value(in.f2) - out.label("f3") - out.value(in.f3) - out.label("f4") - out.value(in.f4) - out.label("f5") - out.value(in.f5) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJFloat = { - var _f1: java.lang.Float = java.lang.Float.valueOf(0.0f) - var _f2: java.lang.Float = java.lang.Float.valueOf(0.0f) - var _f3: java.lang.Float = java.lang.Float.valueOf(0.0f) - var _f4: java.lang.Float = java.lang.Float.valueOf(0.0f) - var _f5: java.lang.Float = java.lang.Float.valueOf(0.0f) - var required: scala.Int = 31 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJFloat_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJFloat] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _f1 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Float.valueOf(n) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _f2 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Float.valueOf(`n₂`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _f3 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Float.valueOf(`n₃`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _f4 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Float.valueOf(`n₄`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _f5 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Float.valueOf(`n₅`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f5"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJFloat_fields) - } - if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJFloat(_f1, _f2, _f3, _f4, _f5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("f1", "f2", "f3", "f4", "f5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJFloat] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJFloat, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJFloat = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJFloat]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleJInt_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("i1", "i2", "i3", "i4", "i5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleJInt, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("i1") - out.value(in.i1) - out.label("i2") - out.value(in.i2) - out.label("i3") - out.value(in.i3) - out.label("i4") - out.value(in.i4) - out.label("i5") - out.value(in.i5) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJInt = { - var _i1: java.lang.Integer = java.lang.Integer.valueOf(0) - var _i2: java.lang.Integer = java.lang.Integer.valueOf(0) - var _i3: java.lang.Integer = java.lang.Integer.valueOf(0) - var _i4: java.lang.Integer = java.lang.Integer.valueOf(0) - var _i5: java.lang.Integer = java.lang.Integer.valueOf(0) - var required: scala.Int = 31 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJInt_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJInt] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _i1 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Integer.valueOf(n) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _i2 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Integer.valueOf(`n₂`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _i3 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Integer.valueOf(`n₃`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _i4 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Integer.valueOf(`n₄`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _i5 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Integer.valueOf(`n₅`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i5"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJInt_fields) - } - if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJInt(_i1, _i2, _i3, _i4, _i5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("i1", "i2", "i3", "i4", "i5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJInt] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJInt, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJInt = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJInt]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleJInt_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("i1", "i2", "i3", "i4", "i5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleJInt, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("i1") - out.value(in.i1) - out.label("i2") - out.value(in.i2) - out.label("i3") - out.value(in.i3) - out.label("i4") - out.value(in.i4) - out.label("i5") - out.value(in.i5) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJInt = { - var _i1: java.lang.Integer = java.lang.Integer.valueOf(0) - var _i2: java.lang.Integer = java.lang.Integer.valueOf(0) - var _i3: java.lang.Integer = java.lang.Integer.valueOf(0) - var _i4: java.lang.Integer = java.lang.Integer.valueOf(0) - var _i5: java.lang.Integer = java.lang.Integer.valueOf(0) - var required: scala.Int = 31 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJInt_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJInt] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _i1 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Integer.valueOf(n) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _i2 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Integer.valueOf(`n₂`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _i3 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Integer.valueOf(`n₃`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _i4 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Integer.valueOf(`n₄`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _i5 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Integer.valueOf(`n₅`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i5"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJInt_fields) - } - if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJInt(_i1, _i2, _i3, _i4, _i5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("i1", "i2", "i3", "i4", "i5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJInt] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJInt, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJInt = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJInt]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleJLong_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("l1", "l2", "l3", "l4", "l5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleJLong, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("l1") - out.value(in.l1) - out.label("l2") - out.value(in.l2) - out.label("l3") - out.value(in.l3) - out.label("l4") - out.value(in.l4) - out.label("l5") - out.value(in.l5) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJLong = { - var _l1: java.lang.Long = java.lang.Long.valueOf(0L) - var _l2: java.lang.Long = java.lang.Long.valueOf(0L) - var _l3: java.lang.Long = java.lang.Long.valueOf(0L) - var _l4: java.lang.Long = java.lang.Long.valueOf(0L) - var _l5: java.lang.Long = java.lang.Long.valueOf(0L) - var required: scala.Int = 31 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJLong_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJLong] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _l1 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Long.valueOf(n) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _l2 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Long.valueOf(`n₂`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _l3 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Long.valueOf(`n₃`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _l4 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Long.valueOf(`n₄`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _l5 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Long.valueOf(`n₅`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l5"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJLong_fields) - } - if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJLong(_l1, _l2, _l3, _l4, _l5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("l1", "l2", "l3", "l4", "l5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJLong] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJLong, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJLong = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJLong]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleJLong_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("l1", "l2", "l3", "l4", "l5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleJLong, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("l1") - out.value(in.l1) - out.label("l2") - out.value(in.l2) - out.label("l3") - out.value(in.l3) - out.label("l4") - out.value(in.l4) - out.label("l5") - out.value(in.l5) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJLong = { - var _l1: java.lang.Long = java.lang.Long.valueOf(0L) - var _l2: java.lang.Long = java.lang.Long.valueOf(0L) - var _l3: java.lang.Long = java.lang.Long.valueOf(0L) - var _l4: java.lang.Long = java.lang.Long.valueOf(0L) - var _l5: java.lang.Long = java.lang.Long.valueOf(0L) - var required: scala.Int = 31 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJLong_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJLong] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _l1 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Long.valueOf(n) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _l2 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Long.valueOf(`n₂`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _l3 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Long.valueOf(`n₃`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _l4 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Long.valueOf(`n₄`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _l5 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Long.valueOf(`n₅`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l5"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJLong_fields) - } - if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJLong(_l1, _l2, _l3, _l4, _l5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("l1", "l2", "l3", "l4", "l5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJLong] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJLong, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJLong = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJLong]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleJNumber_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("n1", "n2", "n3", "n4", "n5", "n6", "n7", "n8", "n9", "n10", "n11", "n12", "n13", "n14", "n15", "n16", "n17")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleJNumber, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("n1") - out.value(in.n1) - out.label("n2") - out.value(in.n2) - out.label("n3") - out.value(in.n3) - out.label("n4") - out.value(in.n4) - out.label("n5") - out.value(in.n5) - out.label("n6") - out.value(in.n6) - out.label("n7") - out.value(in.n7) - out.label("n8") - out.value(in.n8) - out.label("n9") - out.value(in.n9) - out.label("n10") - out.value(in.n10) - out.label("n11") - out.value(in.n11) - out.label("n12") - out.value(in.n12) - out.label("n13") - out.value(in.n13) - out.label("n14") - out.value(in.n14) - out.label("n15") - out.value(in.n15) - out.label("n16") - out.value(in.n16) - out.label("n17") - out.value(in.n17) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJNumber = { - var _n1: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n2: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n3: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n4: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n5: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n6: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n7: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n8: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n9: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n10: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n11: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n12: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n13: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n14: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n15: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n16: java.lang.Number = null.asInstanceOf[java.lang.Number] - var _n17: java.lang.Number = null.asInstanceOf[java.lang.Number] - var required: scala.Int = 131071 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJNumber_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJNumber] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _n1 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(n) match { - case d if d.isValidByte => - java.lang.Byte.valueOf(d.toByteExact) - case d if `d₂`.isValidShort => - java.lang.Short.valueOf(`d₂`.toShortExact) - case d if `d₃`.isValidInt => - java.lang.Integer.valueOf(`d₃`.toIntExact) - case d if `d₄`.isValidLong => - java.lang.Long.valueOf(`d₄`.toLongExact) - case d if `d₅`.isDecimalFloat => - java.lang.Float.valueOf(`d₅`.toFloat) - case d if `d₆`.isDecimalDouble => - java.lang.Double.valueOf(`d₆`.toDouble) - case d => - (`d₇`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _n2 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₂`) match { - case d if `d₈`.isValidByte => - java.lang.Byte.valueOf(`d₈`.toByteExact) - case d if `d₉`.isValidShort => - java.lang.Short.valueOf(`d₉`.toShortExact) - case d if `d₁₀`.isValidInt => - java.lang.Integer.valueOf(`d₁₀`.toIntExact) - case d if `d₁₁`.isValidLong => - java.lang.Long.valueOf(`d₁₁`.toLongExact) - case d if `d₁₂`.isDecimalFloat => - java.lang.Float.valueOf(`d₁₂`.toFloat) - case d if `d₁₃`.isDecimalDouble => - java.lang.Double.valueOf(`d₁₃`.toDouble) - case d => - (`d₁₄`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _n3 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₃`) match { - case d if `d₁₅`.isValidByte => - java.lang.Byte.valueOf(`d₁₅`.toByteExact) - case d if `d₁₆`.isValidShort => - java.lang.Short.valueOf(`d₁₆`.toShortExact) - case d if `d₁₇`.isValidInt => - java.lang.Integer.valueOf(`d₁₇`.toIntExact) - case d if `d₁₈`.isValidLong => - java.lang.Long.valueOf(`d₁₈`.toLongExact) - case d if `d₁₉`.isDecimalFloat => - java.lang.Float.valueOf(`d₁₉`.toFloat) - case d if `d₂₀`.isDecimalDouble => - java.lang.Double.valueOf(`d₂₀`.toDouble) - case d => - (`d₂₁`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _n4 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₄`) match { - case d if `d₂₂`.isValidByte => - java.lang.Byte.valueOf(`d₂₂`.toByteExact) - case d if `d₂₃`.isValidShort => - java.lang.Short.valueOf(`d₂₃`.toShortExact) - case d if `d₂₄`.isValidInt => - java.lang.Integer.valueOf(`d₂₄`.toIntExact) - case d if `d₂₅`.isValidLong => - java.lang.Long.valueOf(`d₂₅`.toLongExact) - case d if `d₂₆`.isDecimalFloat => - java.lang.Float.valueOf(`d₂₆`.toFloat) - case d if `d₂₇`.isDecimalDouble => - java.lang.Double.valueOf(`d₂₇`.toDouble) - case d => - (`d₂₈`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _n5 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₅`) match { - case d if `d₂₉`.isValidByte => - java.lang.Byte.valueOf(`d₂₉`.toByteExact) - case d if `d₃₀`.isValidShort => - java.lang.Short.valueOf(`d₃₀`.toShortExact) - case d if `d₃₁`.isValidInt => - java.lang.Integer.valueOf(`d₃₁`.toIntExact) - case d if `d₃₂`.isValidLong => - java.lang.Long.valueOf(`d₃₂`.toLongExact) - case d if `d₃₃`.isDecimalFloat => - java.lang.Float.valueOf(`d₃₃`.toFloat) - case d if `d₃₄`.isDecimalDouble => - java.lang.Double.valueOf(`d₃₄`.toDouble) - case d => - (`d₃₅`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n5"), `in₂`) - case 5 => - if (required.&(32).!=(0)) { - required = required.^(32) - _n6 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₆`) match { - case d if `d₃₆`.isValidByte => - java.lang.Byte.valueOf(`d₃₆`.toByteExact) - case d if `d₃₇`.isValidShort => - java.lang.Short.valueOf(`d₃₇`.toShortExact) - case d if `d₃₈`.isValidInt => - java.lang.Integer.valueOf(`d₃₈`.toIntExact) - case d if `d₃₉`.isValidLong => - java.lang.Long.valueOf(`d₃₉`.toLongExact) - case d if `d₄₀`.isDecimalFloat => - java.lang.Float.valueOf(`d₄₀`.toFloat) - case d if `d₄₁`.isDecimalDouble => - java.lang.Double.valueOf(`d₄₁`.toDouble) - case d => - (`d₄₂`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n6"), `in₂`) - case 6 => - if (required.&(64).!=(0)) { - required = required.^(64) - _n7 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₇`) match { - case d if `d₄₃`.isValidByte => - java.lang.Byte.valueOf(`d₄₃`.toByteExact) - case d if `d₄₄`.isValidShort => - java.lang.Short.valueOf(`d₄₄`.toShortExact) - case d if `d₄₅`.isValidInt => - java.lang.Integer.valueOf(`d₄₅`.toIntExact) - case d if `d₄₆`.isValidLong => - java.lang.Long.valueOf(`d₄₆`.toLongExact) - case d if `d₄₇`.isDecimalFloat => - java.lang.Float.valueOf(`d₄₇`.toFloat) - case d if `d₄₈`.isDecimalDouble => - java.lang.Double.valueOf(`d₄₈`.toDouble) - case d => - (`d₄₉`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n7"), `in₂`) - case 7 => - if (required.&(128).!=(0)) { - required = required.^(128) - _n8 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₈`) match { - case d if `d₅₀`.isValidByte => - java.lang.Byte.valueOf(`d₅₀`.toByteExact) - case d if `d₅₁`.isValidShort => - java.lang.Short.valueOf(`d₅₁`.toShortExact) - case d if `d₅₂`.isValidInt => - java.lang.Integer.valueOf(`d₅₂`.toIntExact) - case d if `d₅₃`.isValidLong => - java.lang.Long.valueOf(`d₅₃`.toLongExact) - case d if `d₅₄`.isDecimalFloat => - java.lang.Float.valueOf(`d₅₄`.toFloat) - case d if `d₅₅`.isDecimalDouble => - java.lang.Double.valueOf(`d₅₅`.toDouble) - case d => - (`d₅₆`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n8"), `in₂`) - case 8 => - if (required.&(256).!=(0)) { - required = required.^(256) - _n9 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₉`) match { - case d if `d₅₇`.isValidByte => - java.lang.Byte.valueOf(`d₅₇`.toByteExact) - case d if `d₅₈`.isValidShort => - java.lang.Short.valueOf(`d₅₈`.toShortExact) - case d if `d₅₉`.isValidInt => - java.lang.Integer.valueOf(`d₅₉`.toIntExact) - case d if `d₆₀`.isValidLong => - java.lang.Long.valueOf(`d₆₀`.toLongExact) - case d if `d₆₁`.isDecimalFloat => - java.lang.Float.valueOf(`d₆₁`.toFloat) - case d if `d₆₂`.isDecimalDouble => - java.lang.Double.valueOf(`d₆₂`.toDouble) - case d => - (`d₆₃`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n9"), `in₂`) - case 9 => - if (required.&(512).!=(0)) { - required = required.^(512) - _n10 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₁₀`) match { - case d if `d₆₄`.isValidByte => - java.lang.Byte.valueOf(`d₆₄`.toByteExact) - case d if `d₆₅`.isValidShort => - java.lang.Short.valueOf(`d₆₅`.toShortExact) - case d if `d₆₆`.isValidInt => - java.lang.Integer.valueOf(`d₆₆`.toIntExact) - case d if `d₆₇`.isValidLong => - java.lang.Long.valueOf(`d₆₇`.toLongExact) - case d if `d₆₈`.isDecimalFloat => - java.lang.Float.valueOf(`d₆₈`.toFloat) - case d if `d₆₉`.isDecimalDouble => - java.lang.Double.valueOf(`d₆₉`.toDouble) - case d => - (`d₇₀`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n10"), `in₂`) - case 10 => - if (required.&(1024).!=(0)) { - required = required.^(1024) - _n11 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₁₁`) match { - case d if `d₇₁`.isValidByte => - java.lang.Byte.valueOf(`d₇₁`.toByteExact) - case d if `d₇₂`.isValidShort => - java.lang.Short.valueOf(`d₇₂`.toShortExact) - case d if `d₇₃`.isValidInt => - java.lang.Integer.valueOf(`d₇₃`.toIntExact) - case d if `d₇₄`.isValidLong => - java.lang.Long.valueOf(`d₇₄`.toLongExact) - case d if `d₇₅`.isDecimalFloat => - java.lang.Float.valueOf(`d₇₅`.toFloat) - case d if `d₇₆`.isDecimalDouble => - java.lang.Double.valueOf(`d₇₆`.toDouble) - case d => - (`d₇₇`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n11"), `in₂`) - case 11 => - if (required.&(2048).!=(0)) { - required = required.^(2048) - _n12 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₁₂`) match { - case d if `d₇₈`.isValidByte => - java.lang.Byte.valueOf(`d₇₈`.toByteExact) - case d if `d₇₉`.isValidShort => - java.lang.Short.valueOf(`d₇₉`.toShortExact) - case d if `d₈₀`.isValidInt => - java.lang.Integer.valueOf(`d₈₀`.toIntExact) - case d if `d₈₁`.isValidLong => - java.lang.Long.valueOf(`d₈₁`.toLongExact) - case d if `d₈₂`.isDecimalFloat => - java.lang.Float.valueOf(`d₈₂`.toFloat) - case d if `d₈₃`.isDecimalDouble => - java.lang.Double.valueOf(`d₈₃`.toDouble) - case d => - (`d₈₄`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n12"), `in₂`) - case 12 => - if (required.&(4096).!=(0)) { - required = required.^(4096) - _n13 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₁₃`) match { - case d if `d₈₅`.isValidByte => - java.lang.Byte.valueOf(`d₈₅`.toByteExact) - case d if `d₈₆`.isValidShort => - java.lang.Short.valueOf(`d₈₆`.toShortExact) - case d if `d₈₇`.isValidInt => - java.lang.Integer.valueOf(`d₈₇`.toIntExact) - case d if `d₈₈`.isValidLong => - java.lang.Long.valueOf(`d₈₈`.toLongExact) - case d if `d₈₉`.isDecimalFloat => - java.lang.Float.valueOf(`d₈₉`.toFloat) - case d if `d₉₀`.isDecimalDouble => - java.lang.Double.valueOf(`d₉₀`.toDouble) - case d => - (`d₉₁`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n13"), `in₂`) - case 13 => - if (required.&(8192).!=(0)) { - required = required.^(8192) - _n14 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₁₄`) match { - case d if `d₉₂`.isValidByte => - java.lang.Byte.valueOf(`d₉₂`.toByteExact) - case d if `d₉₃`.isValidShort => - java.lang.Short.valueOf(`d₉₃`.toShortExact) - case d if `d₉₄`.isValidInt => - java.lang.Integer.valueOf(`d₉₄`.toIntExact) - case d if `d₉₅`.isValidLong => - java.lang.Long.valueOf(`d₉₅`.toLongExact) - case d if `d₉₆`.isDecimalFloat => - java.lang.Float.valueOf(`d₉₆`.toFloat) - case d if `d₉₇`.isDecimalDouble => - java.lang.Double.valueOf(`d₉₇`.toDouble) - case d => - (`d₉₈`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n14"), `in₂`) - case 14 => - if (required.&(16384).!=(0)) { - required = required.^(16384) - _n15 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₁₅`) match { - case d if `d₉₉`.isValidByte => - java.lang.Byte.valueOf(`d₉₉`.toByteExact) - case d if `d₁₀₀`.isValidShort => - java.lang.Short.valueOf(`d₁₀₀`.toShortExact) - case d if `d₁₀₁`.isValidInt => - java.lang.Integer.valueOf(`d₁₀₁`.toIntExact) - case d if `d₁₀₂`.isValidLong => - java.lang.Long.valueOf(`d₁₀₂`.toLongExact) - case d if `d₁₀₃`.isDecimalFloat => - java.lang.Float.valueOf(`d₁₀₃`.toFloat) - case d if `d₁₀₄`.isDecimalDouble => - java.lang.Double.valueOf(`d₁₀₄`.toDouble) - case d => - (`d₁₀₅`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n15"), `in₂`) - case 15 => - if (required.&(32768).!=(0)) { - required = required.^(32768) - _n16 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₁₆`) match { - case d if `d₁₀₆`.isValidByte => - java.lang.Byte.valueOf(`d₁₀₆`.toByteExact) - case d if `d₁₀₇`.isValidShort => - java.lang.Short.valueOf(`d₁₀₇`.toShortExact) - case d if `d₁₀₈`.isValidInt => - java.lang.Integer.valueOf(`d₁₀₈`.toIntExact) - case d if `d₁₀₉`.isValidLong => - java.lang.Long.valueOf(`d₁₀₉`.toLongExact) - case d if `d₁₁₀`.isDecimalFloat => - java.lang.Float.valueOf(`d₁₁₀`.toFloat) - case d if `d₁₁₁`.isDecimalDouble => - java.lang.Double.valueOf(`d₁₁₁`.toDouble) - case d => - (`d₁₁₂`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n16"), `in₂`) - case 16 => - if (required.&(65536).!=(0)) { - required = required.^(65536) - _n17 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - scala.math.BigDecimal.apply(`n₁₇`) match { - case d if `d₁₁₃`.isValidByte => - java.lang.Byte.valueOf(`d₁₁₃`.toByteExact) - case d if `d₁₁₄`.isValidShort => - java.lang.Short.valueOf(`d₁₁₄`.toShortExact) - case d if `d₁₁₅`.isValidInt => - java.lang.Integer.valueOf(`d₁₁₅`.toIntExact) - case d if `d₁₁₆`.isValidLong => - java.lang.Long.valueOf(`d₁₁₆`.toLongExact) - case d if `d₁₁₇`.isDecimalFloat => - java.lang.Float.valueOf(`d₁₁₇`.toFloat) - case d if `d₁₁₈`.isDecimalDouble => - java.lang.Double.valueOf(`d₁₁₈`.toDouble) - case d => - (`d₁₁₉`: scala.math.BigDecimal) - } - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("n17"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJNumber_fields) - } - if (required.&(131071).==(0)) new co.blocke.scalajack.json.primitives.SampleJNumber(_n1, _n2, _n3, _n4, _n5, _n6, _n7, _n8, _n9, _n10, _n11, _n12, _n13, _n14, _n15, _n16, _n17) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("n1", "n2", "n3", "n4", "n5", "n6", "n7", "n8", "n9", "n10", "n11", "n12", "n13", "n14", "n15", "n16", "n17")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(131071)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJNumber] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJNumber, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJNumber = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJNumber]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleJByte_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("b1", "b2", "b3", "b4", "b5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleJByte, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("b1") - out.value(in.b1) - out.label("b2") - out.value(in.b2) - out.label("b3") - out.value(in.b3) - out.label("b4") - out.value(in.b4) - out.label("b5") - out.value(in.b5) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJByte = { - var _b1: java.lang.Byte = scala.Predef.byte2Byte(java.lang.Byte.MIN_VALUE) - var _b2: java.lang.Byte = scala.Predef.byte2Byte(java.lang.Byte.MIN_VALUE) - var _b3: java.lang.Byte = scala.Predef.byte2Byte(java.lang.Byte.MIN_VALUE) - var _b4: java.lang.Byte = scala.Predef.byte2Byte(java.lang.Byte.MIN_VALUE) - var _b5: java.lang.Byte = scala.Predef.byte2Byte(java.lang.Byte.MIN_VALUE) - var required: scala.Int = 31 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJByte_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJByte] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _b1 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Byte.valueOf(n) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _b2 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Byte.valueOf(`n₂`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _b3 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Byte.valueOf(`n₃`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _b4 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Byte.valueOf(`n₄`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _b5 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Byte.valueOf(`n₅`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b5"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJByte_fields) - } - if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJByte(_b1, _b2, _b3, _b4, _b5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("b1", "b2", "b3", "b4", "b5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJByte] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJByte, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJByte = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJByte]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleJShort_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("s1", "s2", "s3", "s4", "s5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleJShort, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("s1") - out.value(in.s1) - out.label("s2") - out.value(in.s2) - out.label("s3") - out.value(in.s3) - out.label("s4") - out.value(in.s4) - out.label("s5") - out.value(in.s5) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJShort = { - var _s1: java.lang.Short = java.lang.Short.valueOf(0.toShort) - var _s2: java.lang.Short = java.lang.Short.valueOf(0.toShort) - var _s3: java.lang.Short = java.lang.Short.valueOf(0.toShort) - var _s4: java.lang.Short = java.lang.Short.valueOf(0.toShort) - var _s5: java.lang.Short = java.lang.Short.valueOf(0.toShort) - var required: scala.Int = 31 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJShort_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJShort] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _s1 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Short.valueOf(n) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _s2 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Short.valueOf(`n₂`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _s3 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Short.valueOf(`n₃`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _s4 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Short.valueOf(`n₄`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _s5 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Short.valueOf(`n₅`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s5"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJShort_fields) - } - if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJShort(_s1, _s2, _s3, _s4, _s5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("s1", "s2", "s3", "s4", "s5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJShort] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJShort, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJShort = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJShort]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleJShort_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("s1", "s2", "s3", "s4", "s5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleJShort, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("s1") - out.value(in.s1) - out.label("s2") - out.value(in.s2) - out.label("s3") - out.value(in.s3) - out.label("s4") - out.value(in.s4) - out.label("s5") - out.value(in.s5) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJShort = { - var _s1: java.lang.Short = java.lang.Short.valueOf(0.toShort) - var _s2: java.lang.Short = java.lang.Short.valueOf(0.toShort) - var _s3: java.lang.Short = java.lang.Short.valueOf(0.toShort) - var _s4: java.lang.Short = java.lang.Short.valueOf(0.toShort) - var _s5: java.lang.Short = java.lang.Short.valueOf(0.toShort) - var required: scala.Int = 31 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleJShort_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleJShort] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _s1 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Short.valueOf(n) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _s2 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Short.valueOf(`n₂`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _s3 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Short.valueOf(`n₃`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _s4 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Short.valueOf(`n₄`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _s5 = `in₂`.expectNumberOrNull() match { - case null => - null - case n => - java.lang.Short.valueOf(`n₅`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s5"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleJShort_fields) - } - if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleJShort(_s1, _s2, _s3, _s4, _s5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("s1", "s2", "s3", "s4", "s5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJShort] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleJShort, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleJShort = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleJShort]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleBigDecimal_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("bd1", "bd2", "bd3", "bd4", "bd5", "bd6")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleBigDecimal, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("bd1") - out.value(in.bd1) - out.label("bd2") - out.value(in.bd2) - out.label("bd3") - out.value(in.bd3) - out.label("bd4") - out.value(in.bd4) - out.label("bd5") - out.value(in.bd5) - out.label("bd6") - out.value(in.bd6) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleBigDecimal = { - var _bd1: scala.math.BigDecimal = scala.math.BigDecimal.apply(0) - var _bd2: scala.math.BigDecimal = scala.math.BigDecimal.apply(0) - var _bd3: scala.math.BigDecimal = scala.math.BigDecimal.apply(0) - var _bd4: scala.math.BigDecimal = scala.math.BigDecimal.apply(0) - var _bd5: scala.math.BigDecimal = scala.math.BigDecimal.apply(0) - var _bd6: scala.math.BigDecimal = scala.math.BigDecimal.apply(0) - var required: scala.Int = 63 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleBigDecimal_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleBigDecimal] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _bd1 = `in₂`.expectNumberOrNull() match { - case null => - null - case s => - scala.math.BigDecimal.apply(s) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _bd2 = `in₂`.expectNumberOrNull() match { - case null => - null - case s => - scala.math.BigDecimal.apply(`s₂`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _bd3 = `in₂`.expectNumberOrNull() match { - case null => - null - case s => - scala.math.BigDecimal.apply(`s₃`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _bd4 = `in₂`.expectNumberOrNull() match { - case null => - null - case s => - scala.math.BigDecimal.apply(`s₄`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _bd5 = `in₂`.expectNumberOrNull() match { - case null => - null - case s => - scala.math.BigDecimal.apply(`s₅`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd5"), `in₂`) - case 5 => - if (required.&(32).!=(0)) { - required = required.^(32) - _bd6 = `in₂`.expectNumberOrNull() match { - case null => - null - case s => - scala.math.BigDecimal.apply(`s₆`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd6"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleBigDecimal_fields) - } - if (required.&(63).==(0)) new co.blocke.scalajack.json.primitives.SampleBigDecimal(_bd1, _bd2, _bd3, _bd4, _bd5, _bd6) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("bd1", "bd2", "bd3", "bd4", "bd5", "bd6")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(63)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleBigDecimal] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleBigDecimal, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleBigDecimal = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleBigDecimal]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleBigInt_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("bi1", "bi2", "bi3", "bi4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleBigInt, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("bi1") - out.value(in.bi1) - out.label("bi2") - out.value(in.bi2) - out.label("bi3") - out.value(in.bi3) - out.label("bi4") - out.value(in.bi4) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleBigInt = { - var _bi1: scala.math.BigInt = scala.math.BigInt.apply(0) - var _bi2: scala.math.BigInt = scala.math.BigInt.apply(0) - var _bi3: scala.math.BigInt = scala.math.BigInt.apply(0) - var _bi4: scala.math.BigInt = scala.math.BigInt.apply(0) - var required: scala.Int = 15 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleBigInt_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleBigInt] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _bi1 = `in₂`.expectNumberOrNull() match { - case null => - null - case s => - scala.math.BigInt.apply(s) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _bi2 = `in₂`.expectNumberOrNull() match { - case null => - null - case s => - scala.math.BigInt.apply(`s₂`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _bi3 = `in₂`.expectNumberOrNull() match { - case null => - null - case s => - scala.math.BigInt.apply(`s₃`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _bi4 = `in₂`.expectNumberOrNull() match { - case null => - null - case s => - scala.math.BigInt.apply(`s₄`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi4"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleBigInt_fields) - } - if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleBigInt(_bi1, _bi2, _bi3, _bi4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("bi1", "bi2", "bi3", "bi4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleBigInt] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleBigInt, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleBigInt = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleBigInt]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleBoolean_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("bool1", "bool2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleBoolean, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("bool1") - out.value(in.bool1) - out.label("bool2") - out.value(in.bool2) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleBoolean = { - var _bool1: scala.Boolean = false - var _bool2: scala.Boolean = false - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleBoolean_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleBoolean] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _bool1 = `in₂`.expectBoolean() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bool1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _bool2 = `in₂`.expectBoolean() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bool2"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleBoolean_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleBoolean(_bool1, _bool2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("bool1", "bool2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleBoolean] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleBoolean, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleBoolean = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleBoolean]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleByte_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("b1", "b2", "b3", "b4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleByte, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("b1") - out.value(in.b1) - out.label("b2") - out.value(in.b2) - out.label("b3") - out.value(in.b3) - out.label("b4") - out.value(in.b4) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleByte = { - var _b1: scala.Byte = 0.toByte - var _b2: scala.Byte = 0.toByte - var _b3: scala.Byte = 0.toByte - var _b4: scala.Byte = 0.toByte - var required: scala.Int = 15 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleByte_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleByte] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _b1 = `in₂`.expectInt().toByte - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _b2 = `in₂`.expectInt().toByte - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _b3 = `in₂`.expectInt().toByte - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _b4 = `in₂`.expectInt().toByte - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b4"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleByte_fields) - } - if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleByte(_b1, _b2, _b3, _b4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("b1", "b2", "b3", "b4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleByte] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleByte, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleByte = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleByte]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleChar_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("c1", "c2", "c3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleChar, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("c1") - out.value(in.c1) - out.label("c2") - out.value(in.c2) - out.label("c3") - out.value(in.c3) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleChar = { - var _c1: scala.Char = 0.toChar - var _c2: scala.Char = 0.toChar - var _c3: scala.Char = 0.toChar - var required: scala.Int = 7 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleChar_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleChar] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _c1 = `in₂`.expectString() match { - case null => - `in₂`.backspace() - `in₂`.backspace() - `in₂`.backspace() - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Char value cannot be null", `in₂`) - case "" => - `in₂`.backspace() - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Char value expected but empty string found in json", `in₂`) - case c => - c.charAt(0) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _c2 = `in₂`.expectString() match { - case null => - `in₂`.backspace() - `in₂`.backspace() - `in₂`.backspace() - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Char value cannot be null", `in₂`) - case "" => - `in₂`.backspace() - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Char value expected but empty string found in json", `in₂`) - case c => - `c₂`.charAt(0) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _c3 = `in₂`.expectString() match { - case null => - `in₂`.backspace() - `in₂`.backspace() - `in₂`.backspace() - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Char value cannot be null", `in₂`) - case "" => - `in₂`.backspace() - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Char value expected but empty string found in json", `in₂`) - case c => - `c₃`.charAt(0) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c3"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleChar_fields) - } - if (required.&(7).==(0)) new co.blocke.scalajack.json.primitives.SampleChar(_c1, _c2, _c3) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("c1", "c2", "c3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(7)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleChar] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleChar, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleChar = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleChar]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleDouble_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleDouble, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("d1") - out.value(in.d1) - out.label("d2") - out.value(in.d2) - out.label("d3") - out.value(in.d3) - out.label("d4") - out.value(in.d4) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleDouble = { - var _d1: scala.Double = 0.0 - var _d2: scala.Double = 0.0 - var _d3: scala.Double = 0.0 - var _d4: scala.Double = 0.0 - var required: scala.Int = 15 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleDouble_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleDouble] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _d1 = `in₂`.expectDouble() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _d2 = `in₂`.expectDouble() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _d3 = `in₂`.expectDouble() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _d4 = `in₂`.expectDouble() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d4"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleDouble_fields) - } - if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleDouble(_d1, _d2, _d3, _d4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleDouble] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleDouble, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleDouble = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleDouble]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleFloat_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("f1", "f2", "f3", "f4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleFloat, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("f1") - out.value(in.f1) - out.label("f2") - out.value(in.f2) - out.label("f3") - out.value(in.f3) - out.label("f4") - out.value(in.f4) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleFloat = { - var _f1: scala.Float = 0.0f - var _f2: scala.Float = 0.0f - var _f3: scala.Float = 0.0f - var _f4: scala.Float = 0.0f - var required: scala.Int = 15 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleFloat_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleFloat] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _f1 = `in₂`.expectFloat() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _f2 = `in₂`.expectFloat() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _f3 = `in₂`.expectFloat() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _f4 = `in₂`.expectFloat() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f4"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleFloat_fields) - } - if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleFloat(_f1, _f2, _f3, _f4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("f1", "f2", "f3", "f4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleFloat] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleFloat, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleFloat = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleFloat]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleInt_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("i1", "i2", "i3", "i4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleInt, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("i1") - out.value(in.i1) - out.label("i2") - out.value(in.i2) - out.label("i3") - out.value(in.i3) - out.label("i4") - out.value(in.i4) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleInt = { - var _i1: scala.Int = 0 - var _i2: scala.Int = 0 - var _i3: scala.Int = 0 - var _i4: scala.Int = 0 - var required: scala.Int = 15 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleInt_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleInt] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _i1 = `in₂`.expectInt() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _i2 = `in₂`.expectInt() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _i3 = `in₂`.expectInt() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _i4 = `in₂`.expectInt() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i4"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleInt_fields) - } - if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleInt(_i1, _i2, _i3, _i4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("i1", "i2", "i3", "i4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleInt] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleInt, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleInt = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleInt]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleLong_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("l1", "l2", "l3", "l4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleLong, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("l1") - out.value(in.l1) - out.label("l2") - out.value(in.l2) - out.label("l3") - out.value(in.l3) - out.label("l4") - out.value(in.l4) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLong = { - var _l1: scala.Long = 0L - var _l2: scala.Long = 0L - var _l3: scala.Long = 0L - var _l4: scala.Long = 0L - var required: scala.Int = 15 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleLong_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleLong] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _l1 = `in₂`.expectLong() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _l2 = `in₂`.expectLong() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _l3 = `in₂`.expectLong() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _l4 = `in₂`.expectLong() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l4"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleLong_fields) - } - if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleLong(_l1, _l2, _l3, _l4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("l1", "l2", "l3", "l4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLong] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleLong, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLong = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLong]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleShort_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("s1", "s2", "s3", "s4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleShort, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("s1") - out.value(in.s1) - out.label("s2") - out.value(in.s2) - out.label("s3") - out.value(in.s3) - out.label("s4") - out.value(in.s4) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleShort = { - var _s1: scala.Short = 0.toShort - var _s2: scala.Short = 0.toShort - var _s3: scala.Short = 0.toShort - var _s4: scala.Short = 0.toShort - var required: scala.Int = 15 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleShort_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleShort] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _s1 = `in₂`.expectInt().toShort - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _s2 = `in₂`.expectInt().toShort - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _s3 = `in₂`.expectInt().toShort - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _s4 = `in₂`.expectInt().toShort - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s4"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleShort_fields) - } - if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleShort(_s1, _s2, _s3, _s4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("s1", "s2", "s3", "s4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleShort] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleShort, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleShort = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleShort]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleString_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("s1", "s2", "s3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleString, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("s1") - out.valueEscaped(in.s1) - out.label("s2") - out.valueEscaped(in.s2) - out.label("s3") - out.valueEscaped(in.s3) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleString = { - var _s1: java.lang.String = "" - var _s2: java.lang.String = "" - var _s3: java.lang.String = "" - var required: scala.Int = 7 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleString_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleString] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _s1 = `in₂`.expectString() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _s2 = `in₂`.expectString() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _s3 = `in₂`.expectString() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s3"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleString_fields) - } - if (required.&(7).==(0)) new co.blocke.scalajack.json.primitives.SampleString(_s1, _s2, _s3) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("s1", "s2", "s3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(7)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleString] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleString, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleString = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleString]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleBigDecimal_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("bd1", "bd2", "bd3", "bd4", "bd5", "bd6")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleBigDecimal, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("bd1") - out.value(in.bd1) - out.label("bd2") - out.value(in.bd2) - out.label("bd3") - out.value(in.bd3) - out.label("bd4") - out.value(in.bd4) - out.label("bd5") - out.value(in.bd5) - out.label("bd6") - out.value(in.bd6) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleBigDecimal = { - var _bd1: scala.math.BigDecimal = scala.math.BigDecimal.apply(0) - var _bd2: scala.math.BigDecimal = scala.math.BigDecimal.apply(0) - var _bd3: scala.math.BigDecimal = scala.math.BigDecimal.apply(0) - var _bd4: scala.math.BigDecimal = scala.math.BigDecimal.apply(0) - var _bd5: scala.math.BigDecimal = scala.math.BigDecimal.apply(0) - var _bd6: scala.math.BigDecimal = scala.math.BigDecimal.apply(0) - var required: scala.Int = 63 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleBigDecimal_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleBigDecimal] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _bd1 = `in₂`.expectNumberOrNull() match { - case null => - null - case s => - scala.math.BigDecimal.apply(s) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _bd2 = `in₂`.expectNumberOrNull() match { - case null => - null - case s => - scala.math.BigDecimal.apply(`s₂`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _bd3 = `in₂`.expectNumberOrNull() match { - case null => - null - case s => - scala.math.BigDecimal.apply(`s₃`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _bd4 = `in₂`.expectNumberOrNull() match { - case null => - null - case s => - scala.math.BigDecimal.apply(`s₄`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _bd5 = `in₂`.expectNumberOrNull() match { - case null => - null - case s => - scala.math.BigDecimal.apply(`s₅`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd5"), `in₂`) - case 5 => - if (required.&(32).!=(0)) { - required = required.^(32) - _bd6 = `in₂`.expectNumberOrNull() match { - case null => - null - case s => - scala.math.BigDecimal.apply(`s₆`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bd6"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleBigDecimal_fields) - } - if (required.&(63).==(0)) new co.blocke.scalajack.json.primitives.SampleBigDecimal(_bd1, _bd2, _bd3, _bd4, _bd5, _bd6) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("bd1", "bd2", "bd3", "bd4", "bd5", "bd6")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(63)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleBigDecimal] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleBigDecimal, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleBigDecimal = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleBigDecimal]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleBigInt_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("bi1", "bi2", "bi3", "bi4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleBigInt, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("bi1") - out.value(in.bi1) - out.label("bi2") - out.value(in.bi2) - out.label("bi3") - out.value(in.bi3) - out.label("bi4") - out.value(in.bi4) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleBigInt = { - var _bi1: scala.math.BigInt = scala.math.BigInt.apply(0) - var _bi2: scala.math.BigInt = scala.math.BigInt.apply(0) - var _bi3: scala.math.BigInt = scala.math.BigInt.apply(0) - var _bi4: scala.math.BigInt = scala.math.BigInt.apply(0) - var required: scala.Int = 15 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleBigInt_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleBigInt] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _bi1 = `in₂`.expectNumberOrNull() match { - case null => - null - case s => - scala.math.BigInt.apply(s) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _bi2 = `in₂`.expectNumberOrNull() match { - case null => - null - case s => - scala.math.BigInt.apply(`s₂`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _bi3 = `in₂`.expectNumberOrNull() match { - case null => - null - case s => - scala.math.BigInt.apply(`s₃`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _bi4 = `in₂`.expectNumberOrNull() match { - case null => - null - case s => - scala.math.BigInt.apply(`s₄`) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bi4"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleBigInt_fields) - } - if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleBigInt(_bi1, _bi2, _bi3, _bi4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("bi1", "bi2", "bi3", "bi4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleBigInt] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleBigInt, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleBigInt = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleBigInt]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleBoolean_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("bool1", "bool2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleBoolean, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("bool1") - out.value(in.bool1) - out.label("bool2") - out.value(in.bool2) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleBoolean = { - var _bool1: scala.Boolean = false - var _bool2: scala.Boolean = false - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleBoolean_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleBoolean] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _bool1 = `in₂`.expectBoolean() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bool1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _bool2 = `in₂`.expectBoolean() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("bool2"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleBoolean_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleBoolean(_bool1, _bool2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("bool1", "bool2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleBoolean] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleBoolean, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleBoolean = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleBoolean]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleByte_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("b1", "b2", "b3", "b4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleByte, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("b1") - out.value(in.b1) - out.label("b2") - out.value(in.b2) - out.label("b3") - out.value(in.b3) - out.label("b4") - out.value(in.b4) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleByte = { - var _b1: scala.Byte = 0.toByte - var _b2: scala.Byte = 0.toByte - var _b3: scala.Byte = 0.toByte - var _b4: scala.Byte = 0.toByte - var required: scala.Int = 15 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleByte_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleByte] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _b1 = `in₂`.expectInt().toByte - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _b2 = `in₂`.expectInt().toByte - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _b3 = `in₂`.expectInt().toByte - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _b4 = `in₂`.expectInt().toByte - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("b4"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleByte_fields) - } - if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleByte(_b1, _b2, _b3, _b4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("b1", "b2", "b3", "b4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleByte] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleByte, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleByte = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleByte]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleChar_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("c1", "c2", "c3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleChar, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("c1") - out.value(in.c1) - out.label("c2") - out.value(in.c2) - out.label("c3") - out.value(in.c3) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleChar = { - var _c1: scala.Char = 0.toChar - var _c2: scala.Char = 0.toChar - var _c3: scala.Char = 0.toChar - var required: scala.Int = 7 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleChar_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleChar] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _c1 = `in₂`.expectString() match { - case null => - `in₂`.backspace() - `in₂`.backspace() - `in₂`.backspace() - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Char value cannot be null", `in₂`) - case "" => - `in₂`.backspace() - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Char value expected but empty string found in json", `in₂`) - case c => - c.charAt(0) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _c2 = `in₂`.expectString() match { - case null => - `in₂`.backspace() - `in₂`.backspace() - `in₂`.backspace() - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Char value cannot be null", `in₂`) - case "" => - `in₂`.backspace() - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Char value expected but empty string found in json", `in₂`) - case c => - `c₂`.charAt(0) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _c3 = `in₂`.expectString() match { - case null => - `in₂`.backspace() - `in₂`.backspace() - `in₂`.backspace() - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Char value cannot be null", `in₂`) - case "" => - `in₂`.backspace() - `in₂`.backspace() - throw co.blocke.scalajack.json.JsonParseError.apply("Char value expected but empty string found in json", `in₂`) - case c => - `c₃`.charAt(0) - } - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("c3"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleChar_fields) - } - if (required.&(7).==(0)) new co.blocke.scalajack.json.primitives.SampleChar(_c1, _c2, _c3) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("c1", "c2", "c3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(7)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleChar] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleChar, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleChar = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleChar]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleDouble_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleDouble, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("d1") - out.value(in.d1) - out.label("d2") - out.value(in.d2) - out.label("d3") - out.value(in.d3) - out.label("d4") - out.value(in.d4) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleDouble = { - var _d1: scala.Double = 0.0 - var _d2: scala.Double = 0.0 - var _d3: scala.Double = 0.0 - var _d4: scala.Double = 0.0 - var required: scala.Int = 15 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleDouble_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleDouble] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _d1 = `in₂`.expectDouble() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _d2 = `in₂`.expectDouble() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _d3 = `in₂`.expectDouble() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _d4 = `in₂`.expectDouble() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d4"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleDouble_fields) - } - if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleDouble(_d1, _d2, _d3, _d4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleDouble] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleDouble, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleDouble = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleDouble]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleFloat_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("f1", "f2", "f3", "f4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleFloat, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("f1") - out.value(in.f1) - out.label("f2") - out.value(in.f2) - out.label("f3") - out.value(in.f3) - out.label("f4") - out.value(in.f4) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleFloat = { - var _f1: scala.Float = 0.0f - var _f2: scala.Float = 0.0f - var _f3: scala.Float = 0.0f - var _f4: scala.Float = 0.0f - var required: scala.Int = 15 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleFloat_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleFloat] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _f1 = `in₂`.expectFloat() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _f2 = `in₂`.expectFloat() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _f3 = `in₂`.expectFloat() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _f4 = `in₂`.expectFloat() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("f4"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleFloat_fields) - } - if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleFloat(_f1, _f2, _f3, _f4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("f1", "f2", "f3", "f4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleFloat] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleFloat, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleFloat = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleFloat]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleInt_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("i1", "i2", "i3", "i4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleInt, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("i1") - out.value(in.i1) - out.label("i2") - out.value(in.i2) - out.label("i3") - out.value(in.i3) - out.label("i4") - out.value(in.i4) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleInt = { - var _i1: scala.Int = 0 - var _i2: scala.Int = 0 - var _i3: scala.Int = 0 - var _i4: scala.Int = 0 - var required: scala.Int = 15 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleInt_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleInt] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _i1 = `in₂`.expectInt() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _i2 = `in₂`.expectInt() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _i3 = `in₂`.expectInt() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _i4 = `in₂`.expectInt() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i4"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleInt_fields) - } - if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleInt(_i1, _i2, _i3, _i4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("i1", "i2", "i3", "i4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleInt] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleInt, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleInt = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleInt]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleLong_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("l1", "l2", "l3", "l4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleLong, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("l1") - out.value(in.l1) - out.label("l2") - out.value(in.l2) - out.label("l3") - out.value(in.l3) - out.label("l4") - out.value(in.l4) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLong = { - var _l1: scala.Long = 0L - var _l2: scala.Long = 0L - var _l3: scala.Long = 0L - var _l4: scala.Long = 0L - var required: scala.Int = 15 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleLong_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleLong] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _l1 = `in₂`.expectLong() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _l2 = `in₂`.expectLong() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _l3 = `in₂`.expectLong() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _l4 = `in₂`.expectLong() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("l4"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleLong_fields) - } - if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleLong(_l1, _l2, _l3, _l4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("l1", "l2", "l3", "l4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLong] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleLong, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLong = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLong]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleShort_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("s1", "s2", "s3", "s4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleShort, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("s1") - out.value(in.s1) - out.label("s2") - out.value(in.s2) - out.label("s3") - out.value(in.s3) - out.label("s4") - out.value(in.s4) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleShort = { - var _s1: scala.Short = 0.toShort - var _s2: scala.Short = 0.toShort - var _s3: scala.Short = 0.toShort - var _s4: scala.Short = 0.toShort - var required: scala.Int = 15 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleShort_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleShort] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _s1 = `in₂`.expectInt().toShort - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _s2 = `in₂`.expectInt().toShort - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _s3 = `in₂`.expectInt().toShort - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _s4 = `in₂`.expectInt().toShort - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s4"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleShort_fields) - } - if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleShort(_s1, _s2, _s3, _s4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("s1", "s2", "s3", "s4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleShort] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleShort, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleShort = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleShort]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleString_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("s1", "s2", "s3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleString, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("s1") - out.valueEscaped(in.s1) - out.label("s2") - out.valueEscaped(in.s2) - out.label("s3") - out.valueEscaped(in.s3) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleString = { - var _s1: java.lang.String = "" - var _s2: java.lang.String = "" - var _s3: java.lang.String = "" - var required: scala.Int = 7 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleString_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleString] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _s1 = `in₂`.expectString() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _s2 = `in₂`.expectString() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _s3 = `in₂`.expectString() - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("s3"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleString_fields) - } - if (required.&(7).==(0)) new co.blocke.scalajack.json.primitives.SampleString(_s1, _s2, _s3) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("s1", "s2", "s3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(7)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleString] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleString, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleString = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleString]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleDuration_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("d1", "d2", "d3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleDuration, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("d1") - out.value(in.d1) - out.label("d2") - out.value(in.d2) - out.label("d3") - out.value(in.d3) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleDuration = { - var _d1: java.time.Duration = null - var _d2: java.time.Duration = null - var _d3: java.time.Duration = null - var required: scala.Int = 7 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleDuration_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleDuration] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _d1 = `in₂`.expectString[java.time.Duration](((x$0: java.lang.CharSequence) => java.time.Duration.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _d2 = `in₂`.expectString[java.time.Duration](((`x$0₂`: java.lang.CharSequence) => java.time.Duration.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _d3 = `in₂`.expectString[java.time.Duration](((`x$0₃`: java.lang.CharSequence) => java.time.Duration.parse(`x$0₃`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d3"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleDuration_fields) - } - if (required.&(7).==(0)) new co.blocke.scalajack.json.primitives.SampleDuration(_d1, _d2, _d3) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("d1", "d2", "d3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(7)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleDuration] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleDuration, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleDuration = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleDuration]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleDuration_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("d1", "d2", "d3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleDuration, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("d1") - out.value(in.d1) - out.label("d2") - out.value(in.d2) - out.label("d3") - out.value(in.d3) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleDuration = { - var _d1: java.time.Duration = null - var _d2: java.time.Duration = null - var _d3: java.time.Duration = null - var required: scala.Int = 7 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleDuration_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleDuration] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _d1 = `in₂`.expectString[java.time.Duration](((x$0: java.lang.CharSequence) => java.time.Duration.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _d2 = `in₂`.expectString[java.time.Duration](((`x$0₂`: java.lang.CharSequence) => java.time.Duration.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _d3 = `in₂`.expectString[java.time.Duration](((`x$0₃`: java.lang.CharSequence) => java.time.Duration.parse(`x$0₃`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d3"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleDuration_fields) - } - if (required.&(7).==(0)) new co.blocke.scalajack.json.primitives.SampleDuration(_d1, _d2, _d3) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("d1", "d2", "d3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(7)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleDuration] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleDuration, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleDuration = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleDuration]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleInstant_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("i1", "i2", "i3", "i4", "i5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleInstant, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("i1") - out.value(in.i1) - out.label("i2") - out.value(in.i2) - out.label("i3") - out.value(in.i3) - out.label("i4") - out.value(in.i4) - out.label("i5") - out.value(in.i5) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleInstant = { - var _i1: java.time.Instant = null - var _i2: java.time.Instant = null - var _i3: java.time.Instant = null - var _i4: java.time.Instant = null - var _i5: java.time.Instant = null - var required: scala.Int = 31 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleInstant_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleInstant] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _i1 = `in₂`.expectString[java.time.Instant](((x$0: java.lang.CharSequence) => java.time.Instant.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _i2 = `in₂`.expectString[java.time.Instant](((`x$0₂`: java.lang.CharSequence) => java.time.Instant.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _i3 = `in₂`.expectString[java.time.Instant](((`x$0₃`: java.lang.CharSequence) => java.time.Instant.parse(`x$0₃`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _i4 = `in₂`.expectString[java.time.Instant](((`x$0₄`: java.lang.CharSequence) => java.time.Instant.parse(`x$0₄`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _i5 = `in₂`.expectString[java.time.Instant](((`x$0₅`: java.lang.CharSequence) => java.time.Instant.parse(`x$0₅`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i5"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleInstant_fields) - } - if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleInstant(_i1, _i2, _i3, _i4, _i5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("i1", "i2", "i3", "i4", "i5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleInstant] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleInstant, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleInstant = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleInstant]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleInstant_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("i1", "i2", "i3", "i4", "i5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleInstant, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("i1") - out.value(in.i1) - out.label("i2") - out.value(in.i2) - out.label("i3") - out.value(in.i3) - out.label("i4") - out.value(in.i4) - out.label("i5") - out.value(in.i5) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleInstant = { - var _i1: java.time.Instant = null - var _i2: java.time.Instant = null - var _i3: java.time.Instant = null - var _i4: java.time.Instant = null - var _i5: java.time.Instant = null - var required: scala.Int = 31 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleInstant_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleInstant] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _i1 = `in₂`.expectString[java.time.Instant](((x$0: java.lang.CharSequence) => java.time.Instant.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _i2 = `in₂`.expectString[java.time.Instant](((`x$0₂`: java.lang.CharSequence) => java.time.Instant.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _i3 = `in₂`.expectString[java.time.Instant](((`x$0₃`: java.lang.CharSequence) => java.time.Instant.parse(`x$0₃`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _i4 = `in₂`.expectString[java.time.Instant](((`x$0₄`: java.lang.CharSequence) => java.time.Instant.parse(`x$0₄`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _i5 = `in₂`.expectString[java.time.Instant](((`x$0₅`: java.lang.CharSequence) => java.time.Instant.parse(`x$0₅`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i5"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleInstant_fields) - } - if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleInstant(_i1, _i2, _i3, _i4, _i5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("i1", "i2", "i3", "i4", "i5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleInstant] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleInstant, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleInstant = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleInstant]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleLocalDateTime_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleLocalDateTime, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("d1") - out.value(in.d1) - out.label("d2") - out.value(in.d2) - out.label("d3") - out.value(in.d3) - out.label("d4") - out.value(in.d4) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalDateTime = { - var _d1: java.time.LocalDateTime = null - var _d2: java.time.LocalDateTime = null - var _d3: java.time.LocalDateTime = null - var _d4: java.time.LocalDateTime = null - var required: scala.Int = 15 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleLocalDateTime_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleLocalDateTime] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _d1 = `in₂`.expectString[java.time.LocalDateTime](((x$0: java.lang.CharSequence) => java.time.LocalDateTime.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _d2 = `in₂`.expectString[java.time.LocalDateTime](((`x$0₂`: java.lang.CharSequence) => java.time.LocalDateTime.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _d3 = `in₂`.expectString[java.time.LocalDateTime](((`x$0₃`: java.lang.CharSequence) => java.time.LocalDateTime.parse(`x$0₃`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _d4 = `in₂`.expectString[java.time.LocalDateTime](((`x$0₄`: java.lang.CharSequence) => java.time.LocalDateTime.parse(`x$0₄`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d4"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleLocalDateTime_fields) - } - if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleLocalDateTime(_d1, _d2, _d3, _d4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalDateTime] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleLocalDateTime, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalDateTime = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalDateTime]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleLocalDateTime_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleLocalDateTime, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("d1") - out.value(in.d1) - out.label("d2") - out.value(in.d2) - out.label("d3") - out.value(in.d3) - out.label("d4") - out.value(in.d4) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalDateTime = { - var _d1: java.time.LocalDateTime = null - var _d2: java.time.LocalDateTime = null - var _d3: java.time.LocalDateTime = null - var _d4: java.time.LocalDateTime = null - var required: scala.Int = 15 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleLocalDateTime_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleLocalDateTime] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _d1 = `in₂`.expectString[java.time.LocalDateTime](((x$0: java.lang.CharSequence) => java.time.LocalDateTime.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _d2 = `in₂`.expectString[java.time.LocalDateTime](((`x$0₂`: java.lang.CharSequence) => java.time.LocalDateTime.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _d3 = `in₂`.expectString[java.time.LocalDateTime](((`x$0₃`: java.lang.CharSequence) => java.time.LocalDateTime.parse(`x$0₃`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _d4 = `in₂`.expectString[java.time.LocalDateTime](((`x$0₄`: java.lang.CharSequence) => java.time.LocalDateTime.parse(`x$0₄`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d4"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleLocalDateTime_fields) - } - if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleLocalDateTime(_d1, _d2, _d3, _d4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalDateTime] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleLocalDateTime, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalDateTime = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalDateTime]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleLocalDate_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleLocalDate, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("d1") - out.value(in.d1) - out.label("d2") - out.value(in.d2) - out.label("d3") - out.value(in.d3) - out.label("d4") - out.value(in.d4) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalDate = { - var _d1: java.time.LocalDate = null - var _d2: java.time.LocalDate = null - var _d3: java.time.LocalDate = null - var _d4: java.time.LocalDate = null - var required: scala.Int = 15 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleLocalDate_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleLocalDate] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _d1 = `in₂`.expectString[java.time.LocalDate](((x$0: java.lang.CharSequence) => java.time.LocalDate.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _d2 = `in₂`.expectString[java.time.LocalDate](((`x$0₂`: java.lang.CharSequence) => java.time.LocalDate.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _d3 = `in₂`.expectString[java.time.LocalDate](((`x$0₃`: java.lang.CharSequence) => java.time.LocalDate.parse(`x$0₃`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _d4 = `in₂`.expectString[java.time.LocalDate](((`x$0₄`: java.lang.CharSequence) => java.time.LocalDate.parse(`x$0₄`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d4"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleLocalDate_fields) - } - if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleLocalDate(_d1, _d2, _d3, _d4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalDate] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleLocalDate, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalDate = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalDate]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleLocalDate_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleLocalDate, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("d1") - out.value(in.d1) - out.label("d2") - out.value(in.d2) - out.label("d3") - out.value(in.d3) - out.label("d4") - out.value(in.d4) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalDate = { - var _d1: java.time.LocalDate = null - var _d2: java.time.LocalDate = null - var _d3: java.time.LocalDate = null - var _d4: java.time.LocalDate = null - var required: scala.Int = 15 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleLocalDate_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleLocalDate] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _d1 = `in₂`.expectString[java.time.LocalDate](((x$0: java.lang.CharSequence) => java.time.LocalDate.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _d2 = `in₂`.expectString[java.time.LocalDate](((`x$0₂`: java.lang.CharSequence) => java.time.LocalDate.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _d3 = `in₂`.expectString[java.time.LocalDate](((`x$0₃`: java.lang.CharSequence) => java.time.LocalDate.parse(`x$0₃`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _d4 = `in₂`.expectString[java.time.LocalDate](((`x$0₄`: java.lang.CharSequence) => java.time.LocalDate.parse(`x$0₄`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d4"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleLocalDate_fields) - } - if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleLocalDate(_d1, _d2, _d3, _d4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalDate] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleLocalDate, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalDate = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalDate]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleLocalTime_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4", "d5", "d6")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleLocalTime, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("d1") - out.value(in.d1) - out.label("d2") - out.value(in.d2) - out.label("d3") - out.value(in.d3) - out.label("d4") - out.value(in.d4) - out.label("d5") - out.value(in.d5) - out.label("d6") - out.value(in.d6) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalTime = { - var _d1: java.time.LocalTime = null - var _d2: java.time.LocalTime = null - var _d3: java.time.LocalTime = null - var _d4: java.time.LocalTime = null - var _d5: java.time.LocalTime = null - var _d6: java.time.LocalTime = null - var required: scala.Int = 63 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleLocalTime_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleLocalTime] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _d1 = `in₂`.expectString[java.time.LocalTime](((x$0: java.lang.CharSequence) => java.time.LocalTime.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _d2 = `in₂`.expectString[java.time.LocalTime](((`x$0₂`: java.lang.CharSequence) => java.time.LocalTime.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _d3 = `in₂`.expectString[java.time.LocalTime](((`x$0₃`: java.lang.CharSequence) => java.time.LocalTime.parse(`x$0₃`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _d4 = `in₂`.expectString[java.time.LocalTime](((`x$0₄`: java.lang.CharSequence) => java.time.LocalTime.parse(`x$0₄`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _d5 = `in₂`.expectString[java.time.LocalTime](((`x$0₅`: java.lang.CharSequence) => java.time.LocalTime.parse(`x$0₅`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d5"), `in₂`) - case 5 => - if (required.&(32).!=(0)) { - required = required.^(32) - _d6 = `in₂`.expectString[java.time.LocalTime](((`x$0₆`: java.lang.CharSequence) => java.time.LocalTime.parse(`x$0₆`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d6"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleLocalTime_fields) - } - if (required.&(63).==(0)) new co.blocke.scalajack.json.primitives.SampleLocalTime(_d1, _d2, _d3, _d4, _d5, _d6) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4", "d5", "d6")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(63)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalTime] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleLocalTime, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalTime = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalTime]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleLocalTime_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4", "d5", "d6")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleLocalTime, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("d1") - out.value(in.d1) - out.label("d2") - out.value(in.d2) - out.label("d3") - out.value(in.d3) - out.label("d4") - out.value(in.d4) - out.label("d5") - out.value(in.d5) - out.label("d6") - out.value(in.d6) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalTime = { - var _d1: java.time.LocalTime = null - var _d2: java.time.LocalTime = null - var _d3: java.time.LocalTime = null - var _d4: java.time.LocalTime = null - var _d5: java.time.LocalTime = null - var _d6: java.time.LocalTime = null - var required: scala.Int = 63 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleLocalTime_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleLocalTime] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _d1 = `in₂`.expectString[java.time.LocalTime](((x$0: java.lang.CharSequence) => java.time.LocalTime.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _d2 = `in₂`.expectString[java.time.LocalTime](((`x$0₂`: java.lang.CharSequence) => java.time.LocalTime.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _d3 = `in₂`.expectString[java.time.LocalTime](((`x$0₃`: java.lang.CharSequence) => java.time.LocalTime.parse(`x$0₃`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _d4 = `in₂`.expectString[java.time.LocalTime](((`x$0₄`: java.lang.CharSequence) => java.time.LocalTime.parse(`x$0₄`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _d5 = `in₂`.expectString[java.time.LocalTime](((`x$0₅`: java.lang.CharSequence) => java.time.LocalTime.parse(`x$0₅`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d5"), `in₂`) - case 5 => - if (required.&(32).!=(0)) { - required = required.^(32) - _d6 = `in₂`.expectString[java.time.LocalTime](((`x$0₆`: java.lang.CharSequence) => java.time.LocalTime.parse(`x$0₆`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d6"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleLocalTime_fields) - } - if (required.&(63).==(0)) new co.blocke.scalajack.json.primitives.SampleLocalTime(_d1, _d2, _d3, _d4, _d5, _d6) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4", "d5", "d6")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(63)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalTime] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleLocalTime, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalTime = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalTime]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleMonthDay_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("m1", "m2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleMonthDay, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("m1") - out.value(in.m1) - out.label("m2") - out.value(in.m2) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleMonthDay = { - var _m1: java.time.MonthDay = null - var _m2: java.time.MonthDay = null - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleMonthDay_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleMonthDay] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _m1 = `in₂`.expectString[java.time.MonthDay](((x$0: java.lang.CharSequence) => java.time.MonthDay.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("m1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _m2 = `in₂`.expectString[java.time.MonthDay](((`x$0₂`: java.lang.CharSequence) => java.time.MonthDay.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("m2"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleMonthDay_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleMonthDay(_m1, _m2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("m1", "m2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleMonthDay] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleMonthDay, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleMonthDay = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleMonthDay]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleMonthDay_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("m1", "m2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleMonthDay, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("m1") - out.value(in.m1) - out.label("m2") - out.value(in.m2) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleMonthDay = { - var _m1: java.time.MonthDay = null - var _m2: java.time.MonthDay = null - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleMonthDay_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleMonthDay] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _m1 = `in₂`.expectString[java.time.MonthDay](((x$0: java.lang.CharSequence) => java.time.MonthDay.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("m1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _m2 = `in₂`.expectString[java.time.MonthDay](((`x$0₂`: java.lang.CharSequence) => java.time.MonthDay.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("m2"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleMonthDay_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleMonthDay(_m1, _m2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("m1", "m2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleMonthDay] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleMonthDay, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleMonthDay = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleMonthDay]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleOffsetDateTime_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("o1", "o2", "o3", "o4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleOffsetDateTime, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("o1") - out.value(in.o1) - out.label("o2") - out.value(in.o2) - out.label("o3") - out.value(in.o3) - out.label("o4") - out.value(in.o4) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleOffsetDateTime = { - var _o1: java.time.OffsetDateTime = null - var _o2: java.time.OffsetDateTime = null - var _o3: java.time.OffsetDateTime = null - var _o4: java.time.OffsetDateTime = null - var required: scala.Int = 15 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleOffsetDateTime_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleOffsetDateTime] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _o1 = `in₂`.expectString[java.time.OffsetDateTime](((x$0: java.lang.CharSequence) => java.time.OffsetDateTime.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _o2 = `in₂`.expectString[java.time.OffsetDateTime](((`x$0₂`: java.lang.CharSequence) => java.time.OffsetDateTime.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _o3 = `in₂`.expectString[java.time.OffsetDateTime](((`x$0₃`: java.lang.CharSequence) => java.time.OffsetDateTime.parse(`x$0₃`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _o4 = `in₂`.expectString[java.time.OffsetDateTime](((`x$0₄`: java.lang.CharSequence) => java.time.OffsetDateTime.parse(`x$0₄`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o4"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleOffsetDateTime_fields) - } - if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleOffsetDateTime(_o1, _o2, _o3, _o4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("o1", "o2", "o3", "o4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleOffsetDateTime] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleOffsetDateTime, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleOffsetDateTime = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleOffsetDateTime]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleOffsetDateTime_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("o1", "o2", "o3", "o4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleOffsetDateTime, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("o1") - out.value(in.o1) - out.label("o2") - out.value(in.o2) - out.label("o3") - out.value(in.o3) - out.label("o4") - out.value(in.o4) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleOffsetDateTime = { - var _o1: java.time.OffsetDateTime = null - var _o2: java.time.OffsetDateTime = null - var _o3: java.time.OffsetDateTime = null - var _o4: java.time.OffsetDateTime = null - var required: scala.Int = 15 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleOffsetDateTime_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleOffsetDateTime] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _o1 = `in₂`.expectString[java.time.OffsetDateTime](((x$0: java.lang.CharSequence) => java.time.OffsetDateTime.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _o2 = `in₂`.expectString[java.time.OffsetDateTime](((`x$0₂`: java.lang.CharSequence) => java.time.OffsetDateTime.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _o3 = `in₂`.expectString[java.time.OffsetDateTime](((`x$0₃`: java.lang.CharSequence) => java.time.OffsetDateTime.parse(`x$0₃`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _o4 = `in₂`.expectString[java.time.OffsetDateTime](((`x$0₄`: java.lang.CharSequence) => java.time.OffsetDateTime.parse(`x$0₄`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o4"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleOffsetDateTime_fields) - } - if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleOffsetDateTime(_o1, _o2, _o3, _o4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("o1", "o2", "o3", "o4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleOffsetDateTime] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleOffsetDateTime, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleOffsetDateTime = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleOffsetDateTime]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleOffsetTime_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("o1", "o2", "o3", "o4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleOffsetTime, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("o1") - out.value(in.o1) - out.label("o2") - out.value(in.o2) - out.label("o3") - out.value(in.o3) - out.label("o4") - out.value(in.o4) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleOffsetTime = { - var _o1: java.time.OffsetTime = null - var _o2: java.time.OffsetTime = null - var _o3: java.time.OffsetTime = null - var _o4: java.time.OffsetTime = null - var required: scala.Int = 15 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleOffsetTime_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleOffsetTime] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _o1 = `in₂`.expectString[java.time.OffsetTime](((x$0: java.lang.CharSequence) => java.time.OffsetTime.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _o2 = `in₂`.expectString[java.time.OffsetTime](((`x$0₂`: java.lang.CharSequence) => java.time.OffsetTime.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _o3 = `in₂`.expectString[java.time.OffsetTime](((`x$0₃`: java.lang.CharSequence) => java.time.OffsetTime.parse(`x$0₃`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _o4 = `in₂`.expectString[java.time.OffsetTime](((`x$0₄`: java.lang.CharSequence) => java.time.OffsetTime.parse(`x$0₄`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o4"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleOffsetTime_fields) - } - if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleOffsetTime(_o1, _o2, _o3, _o4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("o1", "o2", "o3", "o4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleOffsetTime] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleOffsetTime, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleOffsetTime = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleOffsetTime]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleOffsetTime_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("o1", "o2", "o3", "o4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleOffsetTime, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("o1") - out.value(in.o1) - out.label("o2") - out.value(in.o2) - out.label("o3") - out.value(in.o3) - out.label("o4") - out.value(in.o4) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleOffsetTime = { - var _o1: java.time.OffsetTime = null - var _o2: java.time.OffsetTime = null - var _o3: java.time.OffsetTime = null - var _o4: java.time.OffsetTime = null - var required: scala.Int = 15 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleOffsetTime_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleOffsetTime] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _o1 = `in₂`.expectString[java.time.OffsetTime](((x$0: java.lang.CharSequence) => java.time.OffsetTime.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _o2 = `in₂`.expectString[java.time.OffsetTime](((`x$0₂`: java.lang.CharSequence) => java.time.OffsetTime.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _o3 = `in₂`.expectString[java.time.OffsetTime](((`x$0₃`: java.lang.CharSequence) => java.time.OffsetTime.parse(`x$0₃`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _o4 = `in₂`.expectString[java.time.OffsetTime](((`x$0₄`: java.lang.CharSequence) => java.time.OffsetTime.parse(`x$0₄`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o4"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleOffsetTime_fields) - } - if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleOffsetTime(_o1, _o2, _o3, _o4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("o1", "o2", "o3", "o4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleOffsetTime] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleOffsetTime, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleOffsetTime = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleOffsetTime]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SamplePeriod_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("p1", "p2", "p3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SamplePeriod, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("p1") - out.value(in.p1) - out.label("p2") - out.value(in.p2) - out.label("p3") - out.value(in.p3) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SamplePeriod = { - var _p1: java.time.Period = null - var _p2: java.time.Period = null - var _p3: java.time.Period = null - var required: scala.Int = 7 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SamplePeriod_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SamplePeriod] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _p1 = `in₂`.expectString[java.time.Period](((x$0: java.lang.CharSequence) => java.time.Period.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("p1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _p2 = `in₂`.expectString[java.time.Period](((`x$0₂`: java.lang.CharSequence) => java.time.Period.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("p2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _p3 = `in₂`.expectString[java.time.Period](((`x$0₃`: java.lang.CharSequence) => java.time.Period.parse(`x$0₃`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("p3"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SamplePeriod_fields) - } - if (required.&(7).==(0)) new co.blocke.scalajack.json.primitives.SamplePeriod(_p1, _p2, _p3) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("p1", "p2", "p3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(7)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SamplePeriod] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SamplePeriod, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SamplePeriod = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SamplePeriod]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SamplePeriod_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("p1", "p2", "p3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SamplePeriod, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("p1") - out.value(in.p1) - out.label("p2") - out.value(in.p2) - out.label("p3") - out.value(in.p3) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SamplePeriod = { - var _p1: java.time.Period = null - var _p2: java.time.Period = null - var _p3: java.time.Period = null - var required: scala.Int = 7 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SamplePeriod_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SamplePeriod] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _p1 = `in₂`.expectString[java.time.Period](((x$0: java.lang.CharSequence) => java.time.Period.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("p1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _p2 = `in₂`.expectString[java.time.Period](((`x$0₂`: java.lang.CharSequence) => java.time.Period.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("p2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _p3 = `in₂`.expectString[java.time.Period](((`x$0₃`: java.lang.CharSequence) => java.time.Period.parse(`x$0₃`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("p3"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SamplePeriod_fields) - } - if (required.&(7).==(0)) new co.blocke.scalajack.json.primitives.SamplePeriod(_p1, _p2, _p3) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("p1", "p2", "p3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(7)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SamplePeriod] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SamplePeriod, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SamplePeriod = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SamplePeriod]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleYear_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("y1", "y2", "y3", "y4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleYear, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("y1") - out.value(in.y1) - out.label("y2") - out.value(in.y2) - out.label("y3") - out.value(in.y3) - out.label("y4") - out.value(in.y4) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleYear = { - var _y1: java.time.Year = null - var _y2: java.time.Year = null - var _y3: java.time.Year = null - var _y4: java.time.Year = null - var required: scala.Int = 15 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleYear_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleYear] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _y1 = `in₂`.expectString[java.time.Year](((x$0: java.lang.CharSequence) => java.time.Year.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _y2 = `in₂`.expectString[java.time.Year](((`x$0₂`: java.lang.CharSequence) => java.time.Year.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _y3 = `in₂`.expectString[java.time.Year](((`x$0₃`: java.lang.CharSequence) => java.time.Year.parse(`x$0₃`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _y4 = `in₂`.expectString[java.time.Year](((`x$0₄`: java.lang.CharSequence) => java.time.Year.parse(`x$0₄`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y4"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleYear_fields) - } - if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleYear(_y1, _y2, _y3, _y4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("y1", "y2", "y3", "y4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleYear] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleYear, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleYear = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleYear]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleYear_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("y1", "y2", "y3", "y4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleYear, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("y1") - out.value(in.y1) - out.label("y2") - out.value(in.y2) - out.label("y3") - out.value(in.y3) - out.label("y4") - out.value(in.y4) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleYear = { - var _y1: java.time.Year = null - var _y2: java.time.Year = null - var _y3: java.time.Year = null - var _y4: java.time.Year = null - var required: scala.Int = 15 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleYear_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleYear] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _y1 = `in₂`.expectString[java.time.Year](((x$0: java.lang.CharSequence) => java.time.Year.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _y2 = `in₂`.expectString[java.time.Year](((`x$0₂`: java.lang.CharSequence) => java.time.Year.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _y3 = `in₂`.expectString[java.time.Year](((`x$0₃`: java.lang.CharSequence) => java.time.Year.parse(`x$0₃`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _y4 = `in₂`.expectString[java.time.Year](((`x$0₄`: java.lang.CharSequence) => java.time.Year.parse(`x$0₄`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y4"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleYear_fields) - } - if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleYear(_y1, _y2, _y3, _y4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("y1", "y2", "y3", "y4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleYear] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleYear, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleYear = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleYear]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleYearMonth_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("y1", "y2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleYearMonth, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("y1") - out.value(in.y1) - out.label("y2") - out.value(in.y2) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleYearMonth = { - var _y1: java.time.YearMonth = null - var _y2: java.time.YearMonth = null - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleYearMonth_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleYearMonth] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _y1 = `in₂`.expectString[java.time.YearMonth](((x$0: java.lang.CharSequence) => java.time.YearMonth.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _y2 = `in₂`.expectString[java.time.YearMonth](((`x$0₂`: java.lang.CharSequence) => java.time.YearMonth.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y2"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleYearMonth_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleYearMonth(_y1, _y2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("y1", "y2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleYearMonth] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleYearMonth, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleYearMonth = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleYearMonth]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleYearMonth_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("y1", "y2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleYearMonth, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("y1") - out.value(in.y1) - out.label("y2") - out.value(in.y2) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleYearMonth = { - var _y1: java.time.YearMonth = null - var _y2: java.time.YearMonth = null - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleYearMonth_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleYearMonth] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _y1 = `in₂`.expectString[java.time.YearMonth](((x$0: java.lang.CharSequence) => java.time.YearMonth.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _y2 = `in₂`.expectString[java.time.YearMonth](((`x$0₂`: java.lang.CharSequence) => java.time.YearMonth.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y2"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleYearMonth_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleYearMonth(_y1, _y2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("y1", "y2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleYearMonth] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleYearMonth, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleYearMonth = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleYearMonth]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleZonedDateTime_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("o1", "o2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleZonedDateTime, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("o1") - out.value(in.o1) - out.label("o2") - out.value(in.o2) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleZonedDateTime = { - var _o1: java.time.ZonedDateTime = null - var _o2: java.time.ZonedDateTime = null - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleZonedDateTime_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleZonedDateTime] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _o1 = `in₂`.expectString[java.time.ZonedDateTime](((x$0: java.lang.CharSequence) => java.time.ZonedDateTime.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _o2 = `in₂`.expectString[java.time.ZonedDateTime](((`x$0₂`: java.lang.CharSequence) => java.time.ZonedDateTime.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o2"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleZonedDateTime_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleZonedDateTime(_o1, _o2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("o1", "o2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleZonedDateTime] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleZonedDateTime, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleZonedDateTime = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleZonedDateTime]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleZonedDateTime_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("o1", "o2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleZonedDateTime, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("o1") - out.value(in.o1) - out.label("o2") - out.value(in.o2) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleZonedDateTime = { - var _o1: java.time.ZonedDateTime = null - var _o2: java.time.ZonedDateTime = null - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleZonedDateTime_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleZonedDateTime] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _o1 = `in₂`.expectString[java.time.ZonedDateTime](((x$0: java.lang.CharSequence) => java.time.ZonedDateTime.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _o2 = `in₂`.expectString[java.time.ZonedDateTime](((`x$0₂`: java.lang.CharSequence) => java.time.ZonedDateTime.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o2"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleZonedDateTime_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleZonedDateTime(_o1, _o2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("o1", "o2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleZonedDateTime] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleZonedDateTime, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleZonedDateTime = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleZonedDateTime]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleZoneId_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("z1", "z2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleZoneId, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("z1") - out.value(in.z1) - out.label("z2") - out.value(in.z2) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleZoneId = { - var _z1: java.time.ZoneId = null - var _z2: java.time.ZoneId = null - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleZoneId_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleZoneId] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _z1 = `in₂`.expectString[java.time.ZoneId](((x$0: java.lang.String) => java.time.ZoneId.of(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("z1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _z2 = `in₂`.expectString[java.time.ZoneId](((`x$0₂`: java.lang.String) => java.time.ZoneId.of(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("z2"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleZoneId_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleZoneId(_z1, _z2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("z1", "z2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleZoneId] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleZoneId, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleZoneId = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleZoneId]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleZoneId_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("z1", "z2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleZoneId, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("z1") - out.value(in.z1) - out.label("z2") - out.value(in.z2) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleZoneId = { - var _z1: java.time.ZoneId = null - var _z2: java.time.ZoneId = null - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleZoneId_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleZoneId] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _z1 = `in₂`.expectString[java.time.ZoneId](((x$0: java.lang.String) => java.time.ZoneId.of(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("z1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _z2 = `in₂`.expectString[java.time.ZoneId](((`x$0₂`: java.lang.String) => java.time.ZoneId.of(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("z2"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleZoneId_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleZoneId(_z1, _z2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("z1", "z2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleZoneId] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleZoneId, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleZoneId = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleZoneId]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleZoneOffset_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("z1", "z2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleZoneOffset, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("z1") - out.value(in.z1) - out.label("z2") - out.value(in.z2) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleZoneOffset = { - var _z1: java.time.ZoneOffset = null - var _z2: java.time.ZoneOffset = null - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleZoneOffset_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleZoneOffset] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _z1 = `in₂`.expectString[java.time.ZoneOffset](((x$0: java.lang.String) => java.time.ZoneOffset.of(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("z1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _z2 = `in₂`.expectString[java.time.ZoneOffset](((`x$0₂`: java.lang.String) => java.time.ZoneOffset.of(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("z2"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleZoneOffset_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleZoneOffset(_z1, _z2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("z1", "z2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleZoneOffset] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleZoneOffset, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleZoneOffset = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleZoneOffset]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleZoneOffset_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("z1", "z2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleZoneOffset, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("z1") - out.value(in.z1) - out.label("z2") - out.value(in.z2) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleZoneOffset = { - var _z1: java.time.ZoneOffset = null - var _z2: java.time.ZoneOffset = null - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleZoneOffset_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleZoneOffset] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _z1 = `in₂`.expectString[java.time.ZoneOffset](((x$0: java.lang.String) => java.time.ZoneOffset.of(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("z1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _z2 = `in₂`.expectString[java.time.ZoneOffset](((`x$0₂`: java.lang.String) => java.time.ZoneOffset.of(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("z2"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleZoneOffset_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleZoneOffset(_z1, _z2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("z1", "z2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleZoneOffset] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleZoneOffset, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleZoneOffset = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleZoneOffset]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleNet_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("u1", "u2", "u3", "u4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleNet, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("u1") - out.value(in.u1) - out.label("u2") - out.value(in.u2) - out.label("u3") - out.value(in.u3) - out.label("u4") - out.value(in.u4) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleNet = { - var _u1: java.net.URL = null - var _u2: java.net.URL = null - var _u3: java.net.URI = null - var _u4: java.net.URI = null - var required: scala.Int = 15 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleNet_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleNet] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _u1 = `in₂`.expectString[java.net.URL](((s: java.lang.String) => new java.net.URL(s))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _u2 = `in₂`.expectString[java.net.URL](((`s₂`: java.lang.String) => new java.net.URL(`s₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _u3 = `in₂`.expectString[java.net.URI](((`s₃`: java.lang.String) => new java.net.URI(`s₃`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _u4 = `in₂`.expectString[java.net.URI](((`s₄`: java.lang.String) => new java.net.URI(`s₄`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u4"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleNet_fields) - } - if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleNet(_u1, _u2, _u3, _u4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("u1", "u2", "u3", "u4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleNet] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleNet, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleNet = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleNet]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleNet_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("u1", "u2", "u3", "u4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleNet, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("u1") - out.value(in.u1) - out.label("u2") - out.value(in.u2) - out.label("u3") - out.value(in.u3) - out.label("u4") - out.value(in.u4) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleNet = { - var _u1: java.net.URL = null - var _u2: java.net.URL = null - var _u3: java.net.URI = null - var _u4: java.net.URI = null - var required: scala.Int = 15 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleNet_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleNet] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _u1 = `in₂`.expectString[java.net.URL](((s: java.lang.String) => new java.net.URL(s))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _u2 = `in₂`.expectString[java.net.URL](((`s₂`: java.lang.String) => new java.net.URL(`s₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _u3 = `in₂`.expectString[java.net.URI](((`s₃`: java.lang.String) => new java.net.URI(`s₃`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _u4 = `in₂`.expectString[java.net.URI](((`s₄`: java.lang.String) => new java.net.URI(`s₄`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u4"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleNet_fields) - } - if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleNet(_u1, _u2, _u3, _u4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("u1", "u2", "u3", "u4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleNet] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleNet, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleNet = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleNet]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleUUID_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("u1", "u2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleUUID, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("u1") - out.value(in.u1) - out.label("u2") - out.value(in.u2) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleUUID = { - var _u1: java.util.UUID = null - var _u2: java.util.UUID = null - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleUUID_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleUUID] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _u1 = `in₂`.expectString[java.util.UUID](((x$0: java.lang.String) => java.util.UUID.fromString(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _u2 = `in₂`.expectString[java.util.UUID](((`x$0₂`: java.lang.String) => java.util.UUID.fromString(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u2"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleUUID_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleUUID(_u1, _u2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("u1", "u2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleUUID] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleUUID, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleUUID = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleUUID]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleUUID_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("u1", "u2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleUUID, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("u1") - out.value(in.u1) - out.label("u2") - out.value(in.u2) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleUUID = { - var _u1: java.util.UUID = null - var _u2: java.util.UUID = null - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleUUID_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleUUID] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _u1 = `in₂`.expectString[java.util.UUID](((x$0: java.lang.String) => java.util.UUID.fromString(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _u2 = `in₂`.expectString[java.util.UUID](((`x$0₂`: java.lang.String) => java.util.UUID.fromString(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u2"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleUUID_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleUUID(_u1, _u2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("u1", "u2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleUUID] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleUUID, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleUUID = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleUUID]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleDuration_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("d1", "d2", "d3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleDuration, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("d1") - out.value(in.d1) - out.label("d2") - out.value(in.d2) - out.label("d3") - out.value(in.d3) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleDuration = { - var _d1: java.time.Duration = null - var _d2: java.time.Duration = null - var _d3: java.time.Duration = null - var required: scala.Int = 7 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleDuration_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleDuration] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _d1 = `in₂`.expectString[java.time.Duration](((x$0: java.lang.CharSequence) => java.time.Duration.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _d2 = `in₂`.expectString[java.time.Duration](((`x$0₂`: java.lang.CharSequence) => java.time.Duration.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _d3 = `in₂`.expectString[java.time.Duration](((`x$0₃`: java.lang.CharSequence) => java.time.Duration.parse(`x$0₃`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d3"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleDuration_fields) - } - if (required.&(7).==(0)) new co.blocke.scalajack.json.primitives.SampleDuration(_d1, _d2, _d3) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("d1", "d2", "d3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(7)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleDuration] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleDuration, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleDuration = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleDuration]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleDuration_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("d1", "d2", "d3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleDuration, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("d1") - out.value(in.d1) - out.label("d2") - out.value(in.d2) - out.label("d3") - out.value(in.d3) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleDuration = { - var _d1: java.time.Duration = null - var _d2: java.time.Duration = null - var _d3: java.time.Duration = null - var required: scala.Int = 7 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleDuration_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleDuration] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _d1 = `in₂`.expectString[java.time.Duration](((x$0: java.lang.CharSequence) => java.time.Duration.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _d2 = `in₂`.expectString[java.time.Duration](((`x$0₂`: java.lang.CharSequence) => java.time.Duration.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _d3 = `in₂`.expectString[java.time.Duration](((`x$0₃`: java.lang.CharSequence) => java.time.Duration.parse(`x$0₃`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d3"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleDuration_fields) - } - if (required.&(7).==(0)) new co.blocke.scalajack.json.primitives.SampleDuration(_d1, _d2, _d3) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("d1", "d2", "d3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(7)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleDuration] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleDuration, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleDuration = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleDuration]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleInstant_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("i1", "i2", "i3", "i4", "i5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleInstant, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("i1") - out.value(in.i1) - out.label("i2") - out.value(in.i2) - out.label("i3") - out.value(in.i3) - out.label("i4") - out.value(in.i4) - out.label("i5") - out.value(in.i5) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleInstant = { - var _i1: java.time.Instant = null - var _i2: java.time.Instant = null - var _i3: java.time.Instant = null - var _i4: java.time.Instant = null - var _i5: java.time.Instant = null - var required: scala.Int = 31 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleInstant_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleInstant] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _i1 = `in₂`.expectString[java.time.Instant](((x$0: java.lang.CharSequence) => java.time.Instant.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _i2 = `in₂`.expectString[java.time.Instant](((`x$0₂`: java.lang.CharSequence) => java.time.Instant.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _i3 = `in₂`.expectString[java.time.Instant](((`x$0₃`: java.lang.CharSequence) => java.time.Instant.parse(`x$0₃`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _i4 = `in₂`.expectString[java.time.Instant](((`x$0₄`: java.lang.CharSequence) => java.time.Instant.parse(`x$0₄`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _i5 = `in₂`.expectString[java.time.Instant](((`x$0₅`: java.lang.CharSequence) => java.time.Instant.parse(`x$0₅`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i5"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleInstant_fields) - } - if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleInstant(_i1, _i2, _i3, _i4, _i5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("i1", "i2", "i3", "i4", "i5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleInstant] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleInstant, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleInstant = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleInstant]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleInstant_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("i1", "i2", "i3", "i4", "i5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleInstant, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("i1") - out.value(in.i1) - out.label("i2") - out.value(in.i2) - out.label("i3") - out.value(in.i3) - out.label("i4") - out.value(in.i4) - out.label("i5") - out.value(in.i5) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleInstant = { - var _i1: java.time.Instant = null - var _i2: java.time.Instant = null - var _i3: java.time.Instant = null - var _i4: java.time.Instant = null - var _i5: java.time.Instant = null - var required: scala.Int = 31 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleInstant_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleInstant] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _i1 = `in₂`.expectString[java.time.Instant](((x$0: java.lang.CharSequence) => java.time.Instant.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _i2 = `in₂`.expectString[java.time.Instant](((`x$0₂`: java.lang.CharSequence) => java.time.Instant.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _i3 = `in₂`.expectString[java.time.Instant](((`x$0₃`: java.lang.CharSequence) => java.time.Instant.parse(`x$0₃`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _i4 = `in₂`.expectString[java.time.Instant](((`x$0₄`: java.lang.CharSequence) => java.time.Instant.parse(`x$0₄`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _i5 = `in₂`.expectString[java.time.Instant](((`x$0₅`: java.lang.CharSequence) => java.time.Instant.parse(`x$0₅`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("i5"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleInstant_fields) - } - if (required.&(31).==(0)) new co.blocke.scalajack.json.primitives.SampleInstant(_i1, _i2, _i3, _i4, _i5) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("i1", "i2", "i3", "i4", "i5")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(31)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleInstant] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleInstant, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleInstant = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleInstant]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleLocalDateTime_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleLocalDateTime, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("d1") - out.value(in.d1) - out.label("d2") - out.value(in.d2) - out.label("d3") - out.value(in.d3) - out.label("d4") - out.value(in.d4) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalDateTime = { - var _d1: java.time.LocalDateTime = null - var _d2: java.time.LocalDateTime = null - var _d3: java.time.LocalDateTime = null - var _d4: java.time.LocalDateTime = null - var required: scala.Int = 15 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleLocalDateTime_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleLocalDateTime] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _d1 = `in₂`.expectString[java.time.LocalDateTime](((x$0: java.lang.CharSequence) => java.time.LocalDateTime.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _d2 = `in₂`.expectString[java.time.LocalDateTime](((`x$0₂`: java.lang.CharSequence) => java.time.LocalDateTime.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _d3 = `in₂`.expectString[java.time.LocalDateTime](((`x$0₃`: java.lang.CharSequence) => java.time.LocalDateTime.parse(`x$0₃`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _d4 = `in₂`.expectString[java.time.LocalDateTime](((`x$0₄`: java.lang.CharSequence) => java.time.LocalDateTime.parse(`x$0₄`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d4"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleLocalDateTime_fields) - } - if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleLocalDateTime(_d1, _d2, _d3, _d4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalDateTime] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleLocalDateTime, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalDateTime = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalDateTime]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleLocalDateTime_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleLocalDateTime, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("d1") - out.value(in.d1) - out.label("d2") - out.value(in.d2) - out.label("d3") - out.value(in.d3) - out.label("d4") - out.value(in.d4) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalDateTime = { - var _d1: java.time.LocalDateTime = null - var _d2: java.time.LocalDateTime = null - var _d3: java.time.LocalDateTime = null - var _d4: java.time.LocalDateTime = null - var required: scala.Int = 15 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleLocalDateTime_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleLocalDateTime] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _d1 = `in₂`.expectString[java.time.LocalDateTime](((x$0: java.lang.CharSequence) => java.time.LocalDateTime.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _d2 = `in₂`.expectString[java.time.LocalDateTime](((`x$0₂`: java.lang.CharSequence) => java.time.LocalDateTime.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _d3 = `in₂`.expectString[java.time.LocalDateTime](((`x$0₃`: java.lang.CharSequence) => java.time.LocalDateTime.parse(`x$0₃`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _d4 = `in₂`.expectString[java.time.LocalDateTime](((`x$0₄`: java.lang.CharSequence) => java.time.LocalDateTime.parse(`x$0₄`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d4"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleLocalDateTime_fields) - } - if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleLocalDateTime(_d1, _d2, _d3, _d4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalDateTime] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleLocalDateTime, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalDateTime = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalDateTime]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleLocalDate_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleLocalDate, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("d1") - out.value(in.d1) - out.label("d2") - out.value(in.d2) - out.label("d3") - out.value(in.d3) - out.label("d4") - out.value(in.d4) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalDate = { - var _d1: java.time.LocalDate = null - var _d2: java.time.LocalDate = null - var _d3: java.time.LocalDate = null - var _d4: java.time.LocalDate = null - var required: scala.Int = 15 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleLocalDate_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleLocalDate] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _d1 = `in₂`.expectString[java.time.LocalDate](((x$0: java.lang.CharSequence) => java.time.LocalDate.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _d2 = `in₂`.expectString[java.time.LocalDate](((`x$0₂`: java.lang.CharSequence) => java.time.LocalDate.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _d3 = `in₂`.expectString[java.time.LocalDate](((`x$0₃`: java.lang.CharSequence) => java.time.LocalDate.parse(`x$0₃`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _d4 = `in₂`.expectString[java.time.LocalDate](((`x$0₄`: java.lang.CharSequence) => java.time.LocalDate.parse(`x$0₄`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d4"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleLocalDate_fields) - } - if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleLocalDate(_d1, _d2, _d3, _d4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalDate] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleLocalDate, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalDate = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalDate]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleLocalDate_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleLocalDate, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("d1") - out.value(in.d1) - out.label("d2") - out.value(in.d2) - out.label("d3") - out.value(in.d3) - out.label("d4") - out.value(in.d4) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalDate = { - var _d1: java.time.LocalDate = null - var _d2: java.time.LocalDate = null - var _d3: java.time.LocalDate = null - var _d4: java.time.LocalDate = null - var required: scala.Int = 15 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleLocalDate_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleLocalDate] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _d1 = `in₂`.expectString[java.time.LocalDate](((x$0: java.lang.CharSequence) => java.time.LocalDate.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _d2 = `in₂`.expectString[java.time.LocalDate](((`x$0₂`: java.lang.CharSequence) => java.time.LocalDate.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _d3 = `in₂`.expectString[java.time.LocalDate](((`x$0₃`: java.lang.CharSequence) => java.time.LocalDate.parse(`x$0₃`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _d4 = `in₂`.expectString[java.time.LocalDate](((`x$0₄`: java.lang.CharSequence) => java.time.LocalDate.parse(`x$0₄`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d4"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleLocalDate_fields) - } - if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleLocalDate(_d1, _d2, _d3, _d4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalDate] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleLocalDate, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalDate = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalDate]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleLocalTime_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4", "d5", "d6")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleLocalTime, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("d1") - out.value(in.d1) - out.label("d2") - out.value(in.d2) - out.label("d3") - out.value(in.d3) - out.label("d4") - out.value(in.d4) - out.label("d5") - out.value(in.d5) - out.label("d6") - out.value(in.d6) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalTime = { - var _d1: java.time.LocalTime = null - var _d2: java.time.LocalTime = null - var _d3: java.time.LocalTime = null - var _d4: java.time.LocalTime = null - var _d5: java.time.LocalTime = null - var _d6: java.time.LocalTime = null - var required: scala.Int = 63 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleLocalTime_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleLocalTime] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _d1 = `in₂`.expectString[java.time.LocalTime](((x$0: java.lang.CharSequence) => java.time.LocalTime.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _d2 = `in₂`.expectString[java.time.LocalTime](((`x$0₂`: java.lang.CharSequence) => java.time.LocalTime.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _d3 = `in₂`.expectString[java.time.LocalTime](((`x$0₃`: java.lang.CharSequence) => java.time.LocalTime.parse(`x$0₃`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _d4 = `in₂`.expectString[java.time.LocalTime](((`x$0₄`: java.lang.CharSequence) => java.time.LocalTime.parse(`x$0₄`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _d5 = `in₂`.expectString[java.time.LocalTime](((`x$0₅`: java.lang.CharSequence) => java.time.LocalTime.parse(`x$0₅`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d5"), `in₂`) - case 5 => - if (required.&(32).!=(0)) { - required = required.^(32) - _d6 = `in₂`.expectString[java.time.LocalTime](((`x$0₆`: java.lang.CharSequence) => java.time.LocalTime.parse(`x$0₆`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d6"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleLocalTime_fields) - } - if (required.&(63).==(0)) new co.blocke.scalajack.json.primitives.SampleLocalTime(_d1, _d2, _d3, _d4, _d5, _d6) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4", "d5", "d6")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(63)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalTime] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleLocalTime, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalTime = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalTime]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleLocalTime_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4", "d5", "d6")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleLocalTime, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("d1") - out.value(in.d1) - out.label("d2") - out.value(in.d2) - out.label("d3") - out.value(in.d3) - out.label("d4") - out.value(in.d4) - out.label("d5") - out.value(in.d5) - out.label("d6") - out.value(in.d6) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalTime = { - var _d1: java.time.LocalTime = null - var _d2: java.time.LocalTime = null - var _d3: java.time.LocalTime = null - var _d4: java.time.LocalTime = null - var _d5: java.time.LocalTime = null - var _d6: java.time.LocalTime = null - var required: scala.Int = 63 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleLocalTime_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleLocalTime] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _d1 = `in₂`.expectString[java.time.LocalTime](((x$0: java.lang.CharSequence) => java.time.LocalTime.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _d2 = `in₂`.expectString[java.time.LocalTime](((`x$0₂`: java.lang.CharSequence) => java.time.LocalTime.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _d3 = `in₂`.expectString[java.time.LocalTime](((`x$0₃`: java.lang.CharSequence) => java.time.LocalTime.parse(`x$0₃`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _d4 = `in₂`.expectString[java.time.LocalTime](((`x$0₄`: java.lang.CharSequence) => java.time.LocalTime.parse(`x$0₄`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d4"), `in₂`) - case 4 => - if (required.&(16).!=(0)) { - required = required.^(16) - _d5 = `in₂`.expectString[java.time.LocalTime](((`x$0₅`: java.lang.CharSequence) => java.time.LocalTime.parse(`x$0₅`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d5"), `in₂`) - case 5 => - if (required.&(32).!=(0)) { - required = required.^(32) - _d6 = `in₂`.expectString[java.time.LocalTime](((`x$0₆`: java.lang.CharSequence) => java.time.LocalTime.parse(`x$0₆`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("d6"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleLocalTime_fields) - } - if (required.&(63).==(0)) new co.blocke.scalajack.json.primitives.SampleLocalTime(_d1, _d2, _d3, _d4, _d5, _d6) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("d1", "d2", "d3", "d4", "d5", "d6")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(63)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalTime] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleLocalTime, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleLocalTime = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleLocalTime]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleMonthDay_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("m1", "m2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleMonthDay, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("m1") - out.value(in.m1) - out.label("m2") - out.value(in.m2) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleMonthDay = { - var _m1: java.time.MonthDay = null - var _m2: java.time.MonthDay = null - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleMonthDay_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleMonthDay] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _m1 = `in₂`.expectString[java.time.MonthDay](((x$0: java.lang.CharSequence) => java.time.MonthDay.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("m1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _m2 = `in₂`.expectString[java.time.MonthDay](((`x$0₂`: java.lang.CharSequence) => java.time.MonthDay.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("m2"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleMonthDay_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleMonthDay(_m1, _m2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("m1", "m2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleMonthDay] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleMonthDay, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleMonthDay = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleMonthDay]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleMonthDay_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("m1", "m2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleMonthDay, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("m1") - out.value(in.m1) - out.label("m2") - out.value(in.m2) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleMonthDay = { - var _m1: java.time.MonthDay = null - var _m2: java.time.MonthDay = null - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleMonthDay_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleMonthDay] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _m1 = `in₂`.expectString[java.time.MonthDay](((x$0: java.lang.CharSequence) => java.time.MonthDay.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("m1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _m2 = `in₂`.expectString[java.time.MonthDay](((`x$0₂`: java.lang.CharSequence) => java.time.MonthDay.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("m2"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleMonthDay_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleMonthDay(_m1, _m2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("m1", "m2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleMonthDay] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleMonthDay, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleMonthDay = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleMonthDay]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleOffsetDateTime_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("o1", "o2", "o3", "o4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleOffsetDateTime, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("o1") - out.value(in.o1) - out.label("o2") - out.value(in.o2) - out.label("o3") - out.value(in.o3) - out.label("o4") - out.value(in.o4) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleOffsetDateTime = { - var _o1: java.time.OffsetDateTime = null - var _o2: java.time.OffsetDateTime = null - var _o3: java.time.OffsetDateTime = null - var _o4: java.time.OffsetDateTime = null - var required: scala.Int = 15 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleOffsetDateTime_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleOffsetDateTime] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _o1 = `in₂`.expectString[java.time.OffsetDateTime](((x$0: java.lang.CharSequence) => java.time.OffsetDateTime.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _o2 = `in₂`.expectString[java.time.OffsetDateTime](((`x$0₂`: java.lang.CharSequence) => java.time.OffsetDateTime.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _o3 = `in₂`.expectString[java.time.OffsetDateTime](((`x$0₃`: java.lang.CharSequence) => java.time.OffsetDateTime.parse(`x$0₃`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _o4 = `in₂`.expectString[java.time.OffsetDateTime](((`x$0₄`: java.lang.CharSequence) => java.time.OffsetDateTime.parse(`x$0₄`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o4"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleOffsetDateTime_fields) - } - if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleOffsetDateTime(_o1, _o2, _o3, _o4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("o1", "o2", "o3", "o4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleOffsetDateTime] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleOffsetDateTime, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleOffsetDateTime = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleOffsetDateTime]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleOffsetDateTime_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("o1", "o2", "o3", "o4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleOffsetDateTime, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("o1") - out.value(in.o1) - out.label("o2") - out.value(in.o2) - out.label("o3") - out.value(in.o3) - out.label("o4") - out.value(in.o4) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleOffsetDateTime = { - var _o1: java.time.OffsetDateTime = null - var _o2: java.time.OffsetDateTime = null - var _o3: java.time.OffsetDateTime = null - var _o4: java.time.OffsetDateTime = null - var required: scala.Int = 15 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleOffsetDateTime_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleOffsetDateTime] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _o1 = `in₂`.expectString[java.time.OffsetDateTime](((x$0: java.lang.CharSequence) => java.time.OffsetDateTime.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _o2 = `in₂`.expectString[java.time.OffsetDateTime](((`x$0₂`: java.lang.CharSequence) => java.time.OffsetDateTime.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _o3 = `in₂`.expectString[java.time.OffsetDateTime](((`x$0₃`: java.lang.CharSequence) => java.time.OffsetDateTime.parse(`x$0₃`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _o4 = `in₂`.expectString[java.time.OffsetDateTime](((`x$0₄`: java.lang.CharSequence) => java.time.OffsetDateTime.parse(`x$0₄`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o4"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleOffsetDateTime_fields) - } - if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleOffsetDateTime(_o1, _o2, _o3, _o4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("o1", "o2", "o3", "o4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleOffsetDateTime] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleOffsetDateTime, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleOffsetDateTime = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleOffsetDateTime]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleOffsetTime_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("o1", "o2", "o3", "o4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleOffsetTime, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("o1") - out.value(in.o1) - out.label("o2") - out.value(in.o2) - out.label("o3") - out.value(in.o3) - out.label("o4") - out.value(in.o4) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleOffsetTime = { - var _o1: java.time.OffsetTime = null - var _o2: java.time.OffsetTime = null - var _o3: java.time.OffsetTime = null - var _o4: java.time.OffsetTime = null - var required: scala.Int = 15 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleOffsetTime_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleOffsetTime] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _o1 = `in₂`.expectString[java.time.OffsetTime](((x$0: java.lang.CharSequence) => java.time.OffsetTime.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _o2 = `in₂`.expectString[java.time.OffsetTime](((`x$0₂`: java.lang.CharSequence) => java.time.OffsetTime.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _o3 = `in₂`.expectString[java.time.OffsetTime](((`x$0₃`: java.lang.CharSequence) => java.time.OffsetTime.parse(`x$0₃`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _o4 = `in₂`.expectString[java.time.OffsetTime](((`x$0₄`: java.lang.CharSequence) => java.time.OffsetTime.parse(`x$0₄`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o4"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleOffsetTime_fields) - } - if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleOffsetTime(_o1, _o2, _o3, _o4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("o1", "o2", "o3", "o4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleOffsetTime] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleOffsetTime, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleOffsetTime = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleOffsetTime]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleOffsetTime_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("o1", "o2", "o3", "o4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleOffsetTime, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("o1") - out.value(in.o1) - out.label("o2") - out.value(in.o2) - out.label("o3") - out.value(in.o3) - out.label("o4") - out.value(in.o4) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleOffsetTime = { - var _o1: java.time.OffsetTime = null - var _o2: java.time.OffsetTime = null - var _o3: java.time.OffsetTime = null - var _o4: java.time.OffsetTime = null - var required: scala.Int = 15 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleOffsetTime_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleOffsetTime] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _o1 = `in₂`.expectString[java.time.OffsetTime](((x$0: java.lang.CharSequence) => java.time.OffsetTime.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _o2 = `in₂`.expectString[java.time.OffsetTime](((`x$0₂`: java.lang.CharSequence) => java.time.OffsetTime.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _o3 = `in₂`.expectString[java.time.OffsetTime](((`x$0₃`: java.lang.CharSequence) => java.time.OffsetTime.parse(`x$0₃`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _o4 = `in₂`.expectString[java.time.OffsetTime](((`x$0₄`: java.lang.CharSequence) => java.time.OffsetTime.parse(`x$0₄`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o4"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleOffsetTime_fields) - } - if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleOffsetTime(_o1, _o2, _o3, _o4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("o1", "o2", "o3", "o4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleOffsetTime] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleOffsetTime, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleOffsetTime = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleOffsetTime]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SamplePeriod_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("p1", "p2", "p3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SamplePeriod, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("p1") - out.value(in.p1) - out.label("p2") - out.value(in.p2) - out.label("p3") - out.value(in.p3) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SamplePeriod = { - var _p1: java.time.Period = null - var _p2: java.time.Period = null - var _p3: java.time.Period = null - var required: scala.Int = 7 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SamplePeriod_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SamplePeriod] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _p1 = `in₂`.expectString[java.time.Period](((x$0: java.lang.CharSequence) => java.time.Period.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("p1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _p2 = `in₂`.expectString[java.time.Period](((`x$0₂`: java.lang.CharSequence) => java.time.Period.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("p2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _p3 = `in₂`.expectString[java.time.Period](((`x$0₃`: java.lang.CharSequence) => java.time.Period.parse(`x$0₃`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("p3"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SamplePeriod_fields) - } - if (required.&(7).==(0)) new co.blocke.scalajack.json.primitives.SamplePeriod(_p1, _p2, _p3) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("p1", "p2", "p3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(7)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SamplePeriod] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SamplePeriod, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SamplePeriod = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SamplePeriod]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SamplePeriod_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("p1", "p2", "p3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SamplePeriod, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("p1") - out.value(in.p1) - out.label("p2") - out.value(in.p2) - out.label("p3") - out.value(in.p3) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SamplePeriod = { - var _p1: java.time.Period = null - var _p2: java.time.Period = null - var _p3: java.time.Period = null - var required: scala.Int = 7 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SamplePeriod_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SamplePeriod] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _p1 = `in₂`.expectString[java.time.Period](((x$0: java.lang.CharSequence) => java.time.Period.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("p1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _p2 = `in₂`.expectString[java.time.Period](((`x$0₂`: java.lang.CharSequence) => java.time.Period.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("p2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _p3 = `in₂`.expectString[java.time.Period](((`x$0₃`: java.lang.CharSequence) => java.time.Period.parse(`x$0₃`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("p3"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SamplePeriod_fields) - } - if (required.&(7).==(0)) new co.blocke.scalajack.json.primitives.SamplePeriod(_p1, _p2, _p3) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("p1", "p2", "p3")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(7)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SamplePeriod] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SamplePeriod, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SamplePeriod = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SamplePeriod]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleYear_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("y1", "y2", "y3", "y4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleYear, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("y1") - out.value(in.y1) - out.label("y2") - out.value(in.y2) - out.label("y3") - out.value(in.y3) - out.label("y4") - out.value(in.y4) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleYear = { - var _y1: java.time.Year = null - var _y2: java.time.Year = null - var _y3: java.time.Year = null - var _y4: java.time.Year = null - var required: scala.Int = 15 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleYear_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleYear] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _y1 = `in₂`.expectString[java.time.Year](((x$0: java.lang.CharSequence) => java.time.Year.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _y2 = `in₂`.expectString[java.time.Year](((`x$0₂`: java.lang.CharSequence) => java.time.Year.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _y3 = `in₂`.expectString[java.time.Year](((`x$0₃`: java.lang.CharSequence) => java.time.Year.parse(`x$0₃`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _y4 = `in₂`.expectString[java.time.Year](((`x$0₄`: java.lang.CharSequence) => java.time.Year.parse(`x$0₄`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y4"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleYear_fields) - } - if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleYear(_y1, _y2, _y3, _y4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("y1", "y2", "y3", "y4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleYear] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleYear, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleYear = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleYear]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleYear_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("y1", "y2", "y3", "y4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleYear, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("y1") - out.value(in.y1) - out.label("y2") - out.value(in.y2) - out.label("y3") - out.value(in.y3) - out.label("y4") - out.value(in.y4) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleYear = { - var _y1: java.time.Year = null - var _y2: java.time.Year = null - var _y3: java.time.Year = null - var _y4: java.time.Year = null - var required: scala.Int = 15 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleYear_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleYear] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _y1 = `in₂`.expectString[java.time.Year](((x$0: java.lang.CharSequence) => java.time.Year.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _y2 = `in₂`.expectString[java.time.Year](((`x$0₂`: java.lang.CharSequence) => java.time.Year.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _y3 = `in₂`.expectString[java.time.Year](((`x$0₃`: java.lang.CharSequence) => java.time.Year.parse(`x$0₃`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _y4 = `in₂`.expectString[java.time.Year](((`x$0₄`: java.lang.CharSequence) => java.time.Year.parse(`x$0₄`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y4"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleYear_fields) - } - if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleYear(_y1, _y2, _y3, _y4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("y1", "y2", "y3", "y4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleYear] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleYear, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleYear = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleYear]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleYearMonth_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("y1", "y2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleYearMonth, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("y1") - out.value(in.y1) - out.label("y2") - out.value(in.y2) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleYearMonth = { - var _y1: java.time.YearMonth = null - var _y2: java.time.YearMonth = null - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleYearMonth_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleYearMonth] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _y1 = `in₂`.expectString[java.time.YearMonth](((x$0: java.lang.CharSequence) => java.time.YearMonth.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _y2 = `in₂`.expectString[java.time.YearMonth](((`x$0₂`: java.lang.CharSequence) => java.time.YearMonth.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y2"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleYearMonth_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleYearMonth(_y1, _y2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("y1", "y2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleYearMonth] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleYearMonth, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleYearMonth = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleYearMonth]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleYearMonth_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("y1", "y2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleYearMonth, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("y1") - out.value(in.y1) - out.label("y2") - out.value(in.y2) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleYearMonth = { - var _y1: java.time.YearMonth = null - var _y2: java.time.YearMonth = null - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleYearMonth_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleYearMonth] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _y1 = `in₂`.expectString[java.time.YearMonth](((x$0: java.lang.CharSequence) => java.time.YearMonth.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _y2 = `in₂`.expectString[java.time.YearMonth](((`x$0₂`: java.lang.CharSequence) => java.time.YearMonth.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("y2"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleYearMonth_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleYearMonth(_y1, _y2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("y1", "y2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleYearMonth] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleYearMonth, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleYearMonth = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleYearMonth]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleZoneId_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("z1", "z2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleZoneId, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("z1") - out.value(in.z1) - out.label("z2") - out.value(in.z2) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleZoneId = { - var _z1: java.time.ZoneId = null - var _z2: java.time.ZoneId = null - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleZoneId_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleZoneId] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _z1 = `in₂`.expectString[java.time.ZoneId](((x$0: java.lang.String) => java.time.ZoneId.of(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("z1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _z2 = `in₂`.expectString[java.time.ZoneId](((`x$0₂`: java.lang.String) => java.time.ZoneId.of(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("z2"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleZoneId_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleZoneId(_z1, _z2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("z1", "z2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleZoneId] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleZoneId, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleZoneId = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleZoneId]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleZoneId_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("z1", "z2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleZoneId, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("z1") - out.value(in.z1) - out.label("z2") - out.value(in.z2) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleZoneId = { - var _z1: java.time.ZoneId = null - var _z2: java.time.ZoneId = null - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleZoneId_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleZoneId] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _z1 = `in₂`.expectString[java.time.ZoneId](((x$0: java.lang.String) => java.time.ZoneId.of(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("z1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _z2 = `in₂`.expectString[java.time.ZoneId](((`x$0₂`: java.lang.String) => java.time.ZoneId.of(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("z2"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleZoneId_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleZoneId(_z1, _z2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("z1", "z2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleZoneId] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleZoneId, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleZoneId = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleZoneId]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleZonedDateTime_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("o1", "o2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleZonedDateTime, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("o1") - out.value(in.o1) - out.label("o2") - out.value(in.o2) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleZonedDateTime = { - var _o1: java.time.ZonedDateTime = null - var _o2: java.time.ZonedDateTime = null - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleZonedDateTime_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleZonedDateTime] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _o1 = `in₂`.expectString[java.time.ZonedDateTime](((x$0: java.lang.CharSequence) => java.time.ZonedDateTime.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _o2 = `in₂`.expectString[java.time.ZonedDateTime](((`x$0₂`: java.lang.CharSequence) => java.time.ZonedDateTime.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o2"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleZonedDateTime_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleZonedDateTime(_o1, _o2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("o1", "o2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleZonedDateTime] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleZonedDateTime, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleZonedDateTime = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleZonedDateTime]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleZonedDateTime_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("o1", "o2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleZonedDateTime, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("o1") - out.value(in.o1) - out.label("o2") - out.value(in.o2) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleZonedDateTime = { - var _o1: java.time.ZonedDateTime = null - var _o2: java.time.ZonedDateTime = null - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleZonedDateTime_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleZonedDateTime] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _o1 = `in₂`.expectString[java.time.ZonedDateTime](((x$0: java.lang.CharSequence) => java.time.ZonedDateTime.parse(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _o2 = `in₂`.expectString[java.time.ZonedDateTime](((`x$0₂`: java.lang.CharSequence) => java.time.ZonedDateTime.parse(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("o2"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleZonedDateTime_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleZonedDateTime(_o1, _o2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("o1", "o2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleZonedDateTime] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleZonedDateTime, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleZonedDateTime = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleZonedDateTime]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleNet_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("u1", "u2", "u3", "u4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleNet, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("u1") - out.value(in.u1) - out.label("u2") - out.value(in.u2) - out.label("u3") - out.value(in.u3) - out.label("u4") - out.value(in.u4) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleNet = { - var _u1: java.net.URL = null - var _u2: java.net.URL = null - var _u3: java.net.URI = null - var _u4: java.net.URI = null - var required: scala.Int = 15 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleNet_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleNet] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _u1 = `in₂`.expectString[java.net.URL](((s: java.lang.String) => new java.net.URL(s))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _u2 = `in₂`.expectString[java.net.URL](((`s₂`: java.lang.String) => new java.net.URL(`s₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _u3 = `in₂`.expectString[java.net.URI](((`s₃`: java.lang.String) => new java.net.URI(`s₃`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _u4 = `in₂`.expectString[java.net.URI](((`s₄`: java.lang.String) => new java.net.URI(`s₄`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u4"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleNet_fields) - } - if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleNet(_u1, _u2, _u3, _u4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("u1", "u2", "u3", "u4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleNet] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleNet, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleNet = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleNet]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleNet_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("u1", "u2", "u3", "u4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleNet, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("u1") - out.value(in.u1) - out.label("u2") - out.value(in.u2) - out.label("u3") - out.value(in.u3) - out.label("u4") - out.value(in.u4) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleNet = { - var _u1: java.net.URL = null - var _u2: java.net.URL = null - var _u3: java.net.URI = null - var _u4: java.net.URI = null - var required: scala.Int = 15 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleNet_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleNet] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _u1 = `in₂`.expectString[java.net.URL](((s: java.lang.String) => new java.net.URL(s))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _u2 = `in₂`.expectString[java.net.URL](((`s₂`: java.lang.String) => new java.net.URL(`s₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u2"), `in₂`) - case 2 => - if (required.&(4).!=(0)) { - required = required.^(4) - _u3 = `in₂`.expectString[java.net.URI](((`s₃`: java.lang.String) => new java.net.URI(`s₃`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u3"), `in₂`) - case 3 => - if (required.&(8).!=(0)) { - required = required.^(8) - _u4 = `in₂`.expectString[java.net.URI](((`s₄`: java.lang.String) => new java.net.URI(`s₄`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u4"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleNet_fields) - } - if (required.&(15).==(0)) new co.blocke.scalajack.json.primitives.SampleNet(_u1, _u2, _u3, _u4) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("u1", "u2", "u3", "u4")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(15)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleNet] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleNet, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleNet = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleNet]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleUUID_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("u1", "u2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleUUID, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("u1") - out.value(in.u1) - out.label("u2") - out.value(in.u2) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleUUID = { - var _u1: java.util.UUID = null - var _u2: java.util.UUID = null - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleUUID_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleUUID] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _u1 = `in₂`.expectString[java.util.UUID](((x$0: java.lang.String) => java.util.UUID.fromString(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _u2 = `in₂`.expectString[java.util.UUID](((`x$0₂`: java.lang.String) => java.util.UUID.fromString(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u2"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleUUID_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleUUID(_u1, _u2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("u1", "u2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleUUID] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleUUID, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleUUID = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleUUID]) -} -Codec: { - val __co_blocke_scalajack_json_primitives_SampleUUID_fields: co.blocke.scalajack.json.StringMatrix = new co.blocke.scalajack.json.StringMatrix(scala.Array.apply[java.lang.String]("u1", "u2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String]))) - def w0(in: co.blocke.scalajack.json.primitives.SampleUUID, out: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = if (in.==(null)) out.burpNull() else { - out.startObject() - out.label("u1") - out.value(in.u1) - out.label("u2") - out.value(in.u2) - out.endObject() - } - def r0(`in₂`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleUUID = { - var _u1: java.util.UUID = null - var _u2: java.util.UUID = null - var required: scala.Int = 3 - var maybeFieldNum: scala.Option[scala.Int] = `in₂`.expectFirstObjectField(__co_blocke_scalajack_json_primitives_SampleUUID_fields) - if (maybeFieldNum.==(null)) null.asInstanceOf[co.blocke.scalajack.json.primitives.SampleUUID] else { - while (maybeFieldNum.isDefined) { - maybeFieldNum.get match { - case 0 => - if (required.&(1).!=(0)) { - required = required.^(1) - _u1 = `in₂`.expectString[java.util.UUID](((x$0: java.lang.String) => java.util.UUID.fromString(x$0))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u1"), `in₂`) - case 1 => - if (required.&(2).!=(0)) { - required = required.^(2) - _u2 = `in₂`.expectString[java.util.UUID](((`x$0₂`: java.lang.String) => java.util.UUID.fromString(`x$0₂`))) - } else throw new co.blocke.scalajack.json.JsonParseError("Duplicate field ".+("u2"), `in₂`) - case _ => - `in₂`.skipValue() - } - maybeFieldNum = `in₂`.expectObjectField(__co_blocke_scalajack_json_primitives_SampleUUID_fields) - } - if (required.&(3).==(0)) new co.blocke.scalajack.json.primitives.SampleUUID(_u1, _u2) else throw new co.blocke.scalajack.json.JsonParseError("Missing required field(s) ".+(scala.Array.apply[java.lang.String]("u1", "u2")(scala.reflect.ClassTag.apply[java.lang.String](scala.Predef.classOf[java.lang.String])).apply(java.lang.Integer.numberOfTrailingZeros(required.&(3)))), `in₂`) - } - } - final class $anon() extends co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleUUID] { - def encodeValue(`in₃`: co.blocke.scalajack.json.primitives.SampleUUID, `out₂`: co.blocke.scalajack.json.writing.JsonOutput): scala.Unit = w0(`in₃`, `out₂`) - def decodeValue(`in₄`: co.blocke.scalajack.json.reading.JsonSource): co.blocke.scalajack.json.primitives.SampleUUID = r0(`in₄`) - } - - (new $anon(): co.blocke.scalajack.json.JsonCodec[co.blocke.scalajack.json.primitives.SampleUUID]) -} -[info] done compiling -[info] JavaCollSpec: -[info] ------------------------------- -[info] : Java Collection Tests : -[info] ------------------------------- -[info] +++ Basic functions (Set) +++ -[info] - Set is null must work -[info] - Set of numeric must work -[info] - Set of string must work -[info] - Set of boolean must work -[info] - Set of Set (nested) must work *** FAILED *** -[info] java.lang.ClassCastException: class java.util.TreeSet cannot be cast to class java.lang.Comparable (java.util.TreeSet and java.lang.Comparable are in module java.base of loader 'bootstrap') -[info] at java.base/java.util.TreeMap.compare(TreeMap.java:1569) -[info] at java.base/java.util.TreeMap.addEntryToEmptyMap(TreeMap.java:776) -[info] at java.base/java.util.TreeMap.put(TreeMap.java:785) -[info] at java.base/java.util.TreeMap.put(TreeMap.java:534) -[info] at java.base/java.util.TreeSet.add(TreeSet.java:255) -[info] at java.base/java.util.AbstractCollection.addAll(AbstractCollection.java:338) -[info] at java.base/java.util.TreeSet.addAll(TreeSet.java:309) -[info] at java.base/java.util.TreeSet.(TreeSet.java:160) -[info] at co.blocke.scalajack.json.collections.JavaCollSpec.co$blocke$scalajack$json$collections$JavaCollSpec$$_$_$r0$5(JavaCollSpec.scala:61) -[info] at co.blocke.scalajack.json.collections.JavaCollSpec$$anon$6.decodeValue(JavaCollSpec.scala:61) -[info] ... -[info] - Set of either must work *** FAILED *** -[info] java.lang.ClassCastException: class scala.util.Right cannot be cast to class java.lang.Comparable (scala.util.Right is in unnamed module of loader sbt.internal.ScalaLibraryClassLoader @34e01d38; java.lang.Comparable is in module java.base of loader 'bootstrap') -[info] at java.base/java.util.TreeMap.compare(TreeMap.java:1569) -[info] at java.base/java.util.TreeMap.addEntryToEmptyMap(TreeMap.java:776) -[info] at java.base/java.util.TreeMap.put(TreeMap.java:785) -[info] at java.base/java.util.TreeMap.put(TreeMap.java:534) -[info] at java.base/java.util.TreeSet.add(TreeSet.java:255) -[info] at java.base/java.util.AbstractCollection.addAll(AbstractCollection.java:338) -[info] at java.base/java.util.TreeSet.addAll(TreeSet.java:309) -[info] at java.base/java.util.TreeSet.(TreeSet.java:160) -[info] at co.blocke.scalajack.json.collections.JavaCollSpec.co$blocke$scalajack$json$collections$JavaCollSpec$$_$_$r0$6(JavaCollSpec.scala:68) -[info] at co.blocke.scalajack.json.collections.JavaCollSpec$$anon$7.decodeValue(JavaCollSpec.scala:68) -[info] ... -[info] - Set of union must work *** FAILED *** -[info] java.lang.ClassCastException: class java.lang.Boolean cannot be cast to class java.lang.Integer (java.lang.Boolean and java.lang.Integer are in module java.base of loader 'bootstrap') -[info] at java.base/java.lang.Integer.compareTo(Integer.java:72) -[info] at java.base/java.util.TreeMap.put(TreeMap.java:814) -[info] at java.base/java.util.TreeMap.put(TreeMap.java:534) -[info] at java.base/java.util.TreeSet.add(TreeSet.java:255) -[info] at java.base/java.util.AbstractCollection.addAll(AbstractCollection.java:338) -[info] at java.base/java.util.TreeSet.addAll(TreeSet.java:309) -[info] at java.base/java.util.TreeSet.(TreeSet.java:160) -[info] at co.blocke.scalajack.json.collections.JavaCollSpec.co$blocke$scalajack$json$collections$JavaCollSpec$$_$_$r0$7(JavaCollSpec.scala:75) -[info] at co.blocke.scalajack.json.collections.JavaCollSpec$$anon$8.decodeValue(JavaCollSpec.scala:75) -[info] at co.blocke.scalajack.json.collections.JavaCollSpec$$anon$8.decodeValue(JavaCollSpec.scala:75) -[info] ... -[info] - Set of option must work *** FAILED *** -[info] java.lang.ClassCastException: class scala.Some cannot be cast to class java.lang.Comparable (scala.Some is in unnamed module of loader sbt.internal.ScalaLibraryClassLoader @34e01d38; java.lang.Comparable is in module java.base of loader 'bootstrap') -[info] at java.base/java.util.TreeMap.compare(TreeMap.java:1569) -[info] at java.base/java.util.TreeMap.addEntryToEmptyMap(TreeMap.java:776) -[info] at java.base/java.util.TreeMap.put(TreeMap.java:785) -[info] at java.base/java.util.TreeMap.put(TreeMap.java:534) -[info] at java.base/java.util.TreeSet.add(TreeSet.java:255) -[info] at java.base/java.util.AbstractCollection.addAll(AbstractCollection.java:338) -[info] at java.base/java.util.TreeSet.addAll(TreeSet.java:309) -[info] at java.base/java.util.TreeSet.(TreeSet.java:160) -[info] at co.blocke.scalajack.json.collections.JavaCollSpec.co$blocke$scalajack$json$collections$JavaCollSpec$$_$_$r0$8(JavaCollSpec.scala:82) -[info] at co.blocke.scalajack.json.collections.JavaCollSpec$$anon$9.decodeValue(JavaCollSpec.scala:82) -[info] ... -[info] - Set of map must work *** FAILED *** -[info] java.lang.ClassCastException: class scala.collection.immutable.Map$Map2 cannot be cast to class java.lang.Comparable (scala.collection.immutable.Map$Map2 is in unnamed module of loader sbt.internal.ScalaLibraryClassLoader @34e01d38; java.lang.Comparable is in module java.base of loader 'bootstrap') -[info] at java.base/java.util.TreeMap.compare(TreeMap.java:1569) -[info] at java.base/java.util.TreeMap.addEntryToEmptyMap(TreeMap.java:776) -[info] at java.base/java.util.TreeMap.put(TreeMap.java:785) -[info] at java.base/java.util.TreeMap.put(TreeMap.java:534) -[info] at java.base/java.util.TreeSet.add(TreeSet.java:255) -[info] at java.base/java.util.AbstractCollection.addAll(AbstractCollection.java:338) -[info] at java.base/java.util.TreeSet.addAll(TreeSet.java:309) -[info] at java.base/java.util.TreeSet.(TreeSet.java:160) -[info] at co.blocke.scalajack.json.collections.JavaCollSpec.co$blocke$scalajack$json$collections$JavaCollSpec$$_$_$r0$9(JavaCollSpec.scala:89) -[info] at co.blocke.scalajack.json.collections.JavaCollSpec$$anon$10.decodeValue(JavaCollSpec.scala:89) -[info] ... -[info] - Set of class must work *** FAILED *** -[info] java.lang.ClassCastException: class co.blocke.scalajack.json.collections.Person cannot be cast to class java.lang.Comparable (co.blocke.scalajack.json.collections.Person is in unnamed module of loader sbt.internal.LayeredClassLoader @2aa03814; java.lang.Comparable is in module java.base of loader 'bootstrap') -[info] at java.base/java.util.TreeMap.compare(TreeMap.java:1569) -[info] at java.base/java.util.TreeMap.addEntryToEmptyMap(TreeMap.java:776) -[info] at java.base/java.util.TreeMap.put(TreeMap.java:785) -[info] at java.base/java.util.TreeMap.put(TreeMap.java:534) -[info] at java.base/java.util.TreeSet.add(TreeSet.java:255) -[info] at java.base/java.util.AbstractCollection.addAll(AbstractCollection.java:338) -[info] at java.base/java.util.TreeSet.addAll(TreeSet.java:309) -[info] at java.base/java.util.TreeSet.(TreeSet.java:160) -[info] at co.blocke.scalajack.json.collections.JavaCollSpec.co$blocke$scalajack$json$collections$JavaCollSpec$$_$_$r0$10(JavaCollSpec.scala:96) -[info] at co.blocke.scalajack.json.collections.JavaCollSpec$$anon$11.decodeValue(JavaCollSpec.scala:96) -[info] ... -[info] +++ Basic functions (ArrayList) +++ -[info] - ArrayList is null must work -[info] - ArrayList of numeric must work -[info] - ArrayList of string must work -[info] - ArrayList of boolean must work -[info] - ArrayList of ArrayList (nested) must work -[info] - ArrayList of either must work -[info] - ArrayList of union must work -[info] - ArrayList of option must work -[info] - ArrayList of map must work -[info] - ArrayList of class must work -[info] +++ Coersions (special cases, traits, etc) +++ -[info] - ArrayBlockingQueue -[info] - TreeSet -[info] - Stack (pending) -[info] - List (pending) -[info] - Iterable (pending) -[info] - Queue (pending) -[info] - SortedSet (pending) -[info] - Deque (pending) -[info] - BlockingQueue (pending) -[info] - Vector (generic Collection example) (pending) -[info] Run completed in 180 milliseconds. -[info] Total number of tests run: 22 -[info] Suites: completed 1, aborted 0 -[info] Tests: succeeded 16, failed 6, canceled 0, ignored 0, pending 8 -[info] *** 6 TESTS FAILED *** -[error] Failed tests: -[error] co.blocke.scalajack.json.collections.JavaCollSpec -[error] (Test / testOnly) sbt.TestsFailedException: Tests unsuccessful -[error] Total time: 22 s, completed Apr 6, 2024, 11:44:32 PM diff --git a/benchmark/src/main/scala/co.blocke/ScalaJack.scala b/benchmark/src/main/scala/co.blocke/ScalaJack.scala index 5dc3aa20..cddf4536 100644 --- a/benchmark/src/main/scala/co.blocke/ScalaJack.scala +++ b/benchmark/src/main/scala/co.blocke/ScalaJack.scala @@ -26,6 +26,4 @@ object ScalaJackZ: implicit val blah: ScalaJack[co.blocke.Record2] = sj[co.blocke.Record2] def writeRecordScalaJack = ScalaJack[co.blocke.Record2].toJson(record) - - TODO: Maybe rewrite sj to be something like buildCodec or something more descriptive. */ \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala b/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala index 1be08334..4851d6d2 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala @@ -25,7 +25,7 @@ object JsonCodecMaker: import q.reflect.* // Cache generated method Symbols + an array of the generated functions (DefDef) - case class MethodKey(ref: RTypeRef[?], isStringified: Boolean) // <-- TODO: Not clear what isStringified does here... + case class MethodKey(ref: RTypeRef[?], isNonConstructorFields: Boolean) val writeMethodSyms = new scala.collection.mutable.HashMap[MethodKey, Symbol] val writeMethodDefs = new scala.collection.mutable.ArrayBuffer[DefDef] @@ -103,6 +103,8 @@ object JsonCodecMaker: ) '{} + inline def changeFieldName(fr: FieldInfoRef): String = fr.annotations.get("co.blocke.scalajack.Change").flatMap(_.get("name")).getOrElse(fr.name) + // --------------------------------------------------------------------------------------------- def testValidMapKey(testRef: RTypeRef[?]): Boolean = @@ -325,9 +327,9 @@ object JsonCodecMaker: val cases = t.sealedChildren.map { child => child.refType match case '[c] => - val subtype = TypeIdent(TypeRepr.of[c].typeSymbol) - val sym = Symbol.newBind(Symbol.spliceOwner, "t", Flags.EmptyFlags, subtype.tpe) - CaseDef(Bind(sym, Typed(Ref(sym), subtype)), None, genEncFnBody[c](child, Ref(sym).asExprOf[c], out, true).asTerm) + val subtype = TypeRepr.of[c] + val sym = Symbol.newBind(Symbol.spliceOwner, "t", Flags.EmptyFlags, subtype) + CaseDef(Bind(sym, Typed(Wildcard(), Inferred(subtype))), None, genEncFnBody[c](child, Ref(sym).asExprOf[c], out, true).asTerm) } :+ CaseDef(Literal(NullConstant()), None, '{ $out.burpNull() }.asTerm) val matchExpr = Match(aE.asTerm, cases).asExprOf[Unit] matchExpr @@ -347,7 +349,7 @@ object JsonCodecMaker: f.fieldRef.refType match case '[z] => val fieldValue = Select.unique(in.asTerm, f.name).asExprOf[z] - val fieldName = f.annotations.get("co.blocke.scalajack.Change").flatMap(_.get("name")).getOrElse(f.name) + val fieldName = changeFieldName(f) maybeWrite[z](fieldName, fieldValue, f.fieldRef.asInstanceOf[RTypeRef[z]], out, cfg) } if emitDiscriminator then @@ -365,12 +367,12 @@ object JsonCodecMaker: else Expr.block(eachField.init, eachField.last) } - if !t.isCaseClass && cfg.writeNonConstructorFields then + if !t.isCaseClass && cfg._writeNonConstructorFields then val eachField = t.nonConstructorFields.map { f => f.fieldRef.refType match case '[e] => val fieldValue = Select.unique(in.asTerm, f.getterLabel).asExprOf[e] - val fieldName = f.annotations.get("co.blocke.scalajack.Change").flatMap(_.get("name")).getOrElse(f.name) + val fieldName = changeFieldName(f) maybeWrite[e](fieldName, fieldValue, f.fieldRef.asInstanceOf[RTypeRef[e]], out, cfg) } val subBody = eachField.length match @@ -799,10 +801,10 @@ object JsonCodecMaker: case t: ZoneIdRef => '{ $out.value(${ aE.asExprOf[java.time.ZoneId] }) } case t: ZoneOffsetRef => '{ $out.value(${ aE.asExprOf[java.time.ZoneOffset] }) } - case t: URLRef => '{ $out.value(${ aE.asExprOf[java.net.URL] }) } - case t: URIRef => '{ $out.value(${ aE.asExprOf[java.net.URI] }) } - case t: UUIDRef => '{ $out.value(${ aE.asExprOf[java.util.UUID] }) } - case t: ObjectRef => '{ $out.value(${ Expr(t.name) }) } + case t: URLRef => '{ $out.value(${ aE.asExprOf[java.net.URL] }) } + case t: URIRef => '{ $out.value(${ aE.asExprOf[java.net.URI] }) } + case t: UUIDRef => '{ $out.value(${ aE.asExprOf[java.util.UUID] }) } + case t: ObjectRef[?] => '{ $out.value(${ Expr(t.name) }) } case t: AliasRef[?] => // Special check for RawJson pseudo-type @@ -887,7 +889,6 @@ object JsonCodecMaker: case _ => (false, Language.Scala) def genDecFnBody[T: Type](r: RTypeRef[?], in: Expr[JsonSource])(using Quotes): Expr[Unit] = - import quotes.reflect.* def typeArgs(tpe: TypeRepr): List[TypeRepr] = tpe match case AppliedType(_, typeArgs) => typeArgs.map(_.dealias) @@ -897,6 +898,102 @@ object JsonCodecMaker: case '[b] => r match + case t: ScalaClassRef[?] if t.isSealed && t.isAbstractClass && !t.childrenAreObject => + t.sealedChildren.map(kid => + kid.refType match + case '[c] => + genDecFnBody[c](kid.asInstanceOf[RTypeRef[c]], in) + ) + makeReadFn[T](MethodKey(t, false), in)(in => + val hintLabelE = Expr(cfg.typeHintLabel) + val classPrefixE = Expr(allButLastPart(t.name)) + val caseDefs = t.sealedChildren.map { childRef => + val childNameE = Expr(childRef.name) + cfg.typeHintPolicy match + case TypeHintPolicy.SCRAMBLE_CLASSNAME => + val sym = Symbol.newBind(Symbol.spliceOwner, "t", Flags.EmptyFlags, TypeRepr.of[String]) + CaseDef( + Bind(sym, Typed(Wildcard(), Inferred(TypeRepr.of[String]))), + Some({ + val tE = Ref(sym).asExprOf[String] + '{ descramble($tE, lastPart($childNameE).hashCode) }.asTerm + }), { + val methodKey = MethodKey(childRef, false) + readMethodSyms + .get(methodKey) + .map { sym => + Apply(Ref(sym), List(in.asTerm)).asExprOf[T] + } + .get + .asTerm + } + ) + case TypeHintPolicy.USE_ANNOTATION => + val sym = Symbol.newBind(Symbol.spliceOwner, "t", Flags.EmptyFlags, TypeRepr.of[String]) + val annoOrName = + childRef match + case cr: ClassRef[?] => cr.annotations.get("co.blocke.scalajack.TypeHint").flatMap(_.get("hintValue")).getOrElse(lastPart(cr.name)) + case _ => lastPart(childRef.name) + CaseDef( + Literal(StringConstant(annoOrName)), + None, { + val methodKey = MethodKey(childRef, false) + readMethodSyms + .get(methodKey) + .map { sym => + Apply(Ref(sym), List(in.asTerm)).asExprOf[T] + } + .get + .asTerm + } + ) + case TypeHintPolicy.SIMPLE_CLASSNAME => // TODO: Annotation hints + CaseDef( + Literal(StringConstant(childRef.name)), + None, { + val methodKey = MethodKey(childRef, false) + readMethodSyms + .get(methodKey) + .map { sym => + Apply(Ref(sym), List(in.asTerm)).asExprOf[T] + } + .get + .asTerm + } + ) + } + '{ + if $in.expectNull() then null + else + val hint = $in.findObjectField($hintLabelE).getOrElse(throw JsonParseError(s"Unable to find type hint for abstract class $$cnameE", $in)) + ${ + cfg.typeHintPolicy match + case TypeHintPolicy.SIMPLE_CLASSNAME => Match('{ $classPrefixE + "." + hint }.asTerm, caseDefs).asExprOf[T] + case TypeHintPolicy.SCRAMBLE_CLASSNAME => Match('{ hint }.asTerm, caseDefs).asExprOf[T] + case TypeHintPolicy.USE_ANNOTATION => Match('{ hint }.asTerm, caseDefs).asExprOf[T] + } + }.asExprOf[T] + ) + + case t: ScalaClassRef[?] if t.isSealed && t.isAbstractClass && t.childrenAreObject => // case objects + makeReadFn[T](MethodKey(t, false), in)(in => + val classPrefixE = Expr(allButLastPart(t.name)) + val caseDefs = t.sealedChildren.map { childRef => + val nameE = Expr(childRef.name) + childRef.refType match + case '[o] => + CaseDef( + Literal(StringConstant(childRef.name)), + None, + Ref(TypeRepr.of[o].typeSymbol).asExprOf[o].asTerm + ) + } + '{ + if $in.expectNull() then null + else ${ Match('{ $classPrefixE + "." + $in.expectString() }.asTerm, caseDefs).asExprOf[T] } + }.asExprOf[T] + ) + case t: ScalaClassRef[?] => makeReadFn[T](MethodKey(t, false), in)(in => val fieldNames = Expr(t.fields.map(_.name).toArray) @@ -908,7 +1005,7 @@ object JsonCodecMaker: val totalRequired = math.pow(2, t.fields.length).toInt - 1 var required = 0 val reqSym = Symbol.newVal(Symbol.spliceOwner, "required", TypeRepr.of[Int], Flags.Mutable, Symbol.noSymbol) - val allFieldNames = Expr(t.fields.map(_.name).toArray) // Used for missing required field error + val allFieldNames = Expr(t.fields.map(f => changeFieldName(f)).toArray) // Used for missing required field error val together = t.fields.map { oneField => oneField.fieldRef.refType match { @@ -984,7 +1081,7 @@ object JsonCodecMaker: } val reqVarDef = ValDef(reqSym, Some(Literal(IntConstant(totalRequired)))) val (varDefs, caseDefs, idents) = together.unzip3 - val caseDefsWithFinal = caseDefs :+ CaseDef(Wildcard(), None, '{ $in.skipValue() }.asTerm) + val caseDefsWithFinal = caseDefs :+ CaseDef(Wildcard(), None, '{ $in.skipValue() }.asTerm) // skip values of unrecognized fields val argss = List(idents) val primaryConstructor = tpe.classSymbol.get.primaryConstructor @@ -996,21 +1093,63 @@ object JsonCodecMaker: val exprRequired = Expr(required) - makeClassFieldMatrixValDef(MethodKey(t, false), t.name.replaceAll("\\.", "_"), t.fields.map(_.name).toArray) + makeClassFieldMatrixValDef(MethodKey(t, false), t.name.replaceAll("\\.", "_"), t.fields.map(f => changeFieldName(f)).toArray) val fieldMatrixSym = classFieldMatrixSyms(MethodKey(t, false)).asInstanceOf[Symbol] - val parseLoop = '{ - var maybeFieldNum = $in.expectFirstObjectField(${ Ref(fieldMatrixSym).asExprOf[StringMatrix] }) - if maybeFieldNum == null then null.asInstanceOf[T] + var finalVarDefs = varDefs + val parseLoop = + if !cfg._writeNonConstructorFields || t.nonConstructorFields.isEmpty then + // When we don't care about non-constructor fields + '{ + var maybeFieldNum = $in.expectFirstObjectField(${ Ref(fieldMatrixSym).asExprOf[StringMatrix] }) + if maybeFieldNum == null then null.asInstanceOf[T] + else + while maybeFieldNum.isDefined do + ${ Match('{ maybeFieldNum.get }.asTerm, caseDefsWithFinal).asExprOf[Any] } + maybeFieldNum = $in.expectObjectField(${ Ref(fieldMatrixSym).asExprOf[StringMatrix] }) + + if (${ Ref(reqSym).asExprOf[Int] } & ${ exprRequired }) == 0 then ${ instantiateClass.asExprOf[T] } + else throw new JsonParseError("Missing required field(s) " + ${ allFieldNames }(Integer.numberOfTrailingZeros(${ Ref(reqSym).asExprOf[Int] } & ${ exprRequired })), $in) + }.asTerm else - while maybeFieldNum.isDefined do - ${ Match('{ maybeFieldNum.get }.asTerm, caseDefsWithFinal).asExprOf[Any] } - maybeFieldNum = $in.expectObjectField(${ Ref(fieldMatrixSym).asExprOf[StringMatrix] }) - if (${ Ref(reqSym).asExprOf[Int] } & ${ exprRequired }) == 0 then ${ instantiateClass.asExprOf[T] } - else throw new JsonParseError("Missing required field(s) " + ${ allFieldNames }(Integer.numberOfTrailingZeros(${ Ref(reqSym).asExprOf[Int] } & ${ exprRequired })), $in) - }.asTerm - - Block(varDefs :+ reqVarDef, parseLoop).asExprOf[T] + val instanceSym = Symbol.newVal(Symbol.spliceOwner, "_instance", TypeRepr.of[T], Flags.Mutable, Symbol.noSymbol) + finalVarDefs = finalVarDefs :+ ValDef(instanceSym, Some('{ null }.asTerm)) // add var _instance=null to gen'ed code + val instanceSymRef = Ident(instanceSym.termRef) + // When we do care about non-constructor fields + makeClassFieldMatrixValDef(MethodKey(t, true), t.name.replaceAll("\\.", "_"), t.nonConstructorFields.sortBy(_.index).map(f => changeFieldName(f)).toArray) + val fieldMatrixSymNCF = classFieldMatrixSyms(MethodKey(t, true)).asInstanceOf[Symbol] + // New Case/Match for non-constructor fields + val caseDefsWithFinalNC = t.nonConstructorFields.map(ncf => + ncf.fieldRef.refType match + case '[u] => + CaseDef( + Literal(IntConstant(ncf.index)), + None, + // Call the setter for this field here... + Apply(Select.unique(Ref(instanceSym), ncf.setterLabel), List(genReadVal[u](ncf.fieldRef.asInstanceOf[RTypeRef[u]], in).asTerm)).asExpr.asTerm + ) + ) :+ CaseDef(Wildcard(), None, '{ $in.skipValue() }.asTerm) // skip values of unrecognized fields + '{ + val mark = $in.pos + var maybeFieldNum = $in.expectFirstObjectField(${ Ref(fieldMatrixSym).asExprOf[StringMatrix] }) + if maybeFieldNum == null then null.asInstanceOf[T] + else + while maybeFieldNum.isDefined do + ${ Match('{ maybeFieldNum.get }.asTerm, caseDefsWithFinal).asExprOf[Any] } + maybeFieldNum = $in.expectObjectField(${ Ref(fieldMatrixSym).asExprOf[StringMatrix] }) + + if (${ Ref(reqSym).asExprOf[Int] } & ${ exprRequired }) == 0 then + ${ Assign(instanceSymRef, instantiateClass.asExpr.asTerm).asExprOf[Unit] } // _instance = (new instance) + $in.revertToPos(mark) // go back to re-parse object json, this time for non-constructor fields + maybeFieldNum = $in.expectFirstObjectField(${ Ref(fieldMatrixSymNCF).asExprOf[StringMatrix] }) + while maybeFieldNum.isDefined do + ${ Match('{ maybeFieldNum.get }.asTerm, caseDefsWithFinalNC).asExprOf[Any] } + maybeFieldNum = $in.expectObjectField(${ Ref(fieldMatrixSymNCF).asExprOf[StringMatrix] }) + ${ Ref(instanceSym).asExprOf[T] } + else throw new JsonParseError("Missing required field(s) " + ${ allFieldNames }(Integer.numberOfTrailingZeros(${ Ref(reqSym).asExprOf[Int] } & ${ exprRequired })), $in) + }.asTerm + + Block(finalVarDefs :+ reqVarDef, parseLoop).asExprOf[T] ) case t => throw new ParseError("Not yet implemented: " + t) @@ -1266,6 +1405,9 @@ object JsonCodecMaker: case t: URIRef => '{ $in.expectString((s: String) => new java.net.URI(s)) }.asExprOf[T] case t: UUIDRef => '{ $in.expectString(java.util.UUID.fromString) }.asExprOf[T] + // ZZZ: TODO + // case t: ObjectRef => '{ null }.asExprOf[T] + case t: AliasRef[?] => // Special check for RawJson pseudo-type if lastPart(t.definedType) == "RawJson" then @@ -1455,10 +1597,7 @@ syntheticTA.write(t.asInstanceOf[L], writer, out) t.elementRef.refType match case '[e] => val rtypeRef = t.elementRef.asInstanceOf[RTypeRef[e]] - // println("REF: " + rtypeRef) - // LeftRightRef(scala.Union,List(L, R),TryRef(scala.util.Try,List(A),ScalaOptionRef(scala.Option,List(A),IntRef())),StringRef(),UnionRType) '{ - // null val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in, inTuple) }) if parsedArray != null then parsedArray.toSeq else null @@ -1566,7 +1705,6 @@ syntheticTA.write(t.asInstanceOf[L], writer, out) else null }.asExprOf[T] case "java.util.List" | "java.lang.Iterable" => - println(ref) '{ val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in, inTuple) }) if parsedArray != null then new java.util.ArrayList(parsedArray.toList.asJava) @@ -1913,6 +2051,19 @@ syntheticTA.write(t.asInstanceOf[L], writer, out) val arg = genReadVal[f](theField.asInstanceOf[RTypeRef[f]], in, isMapKey = isMapKey).asTerm Apply(constructor, List(arg)).asExprOf[T] + case t: SelfRefRef[?] => + t.refType match + case '[e] => + val ref = ReflectOnType[e](q)(TypeRepr.of[e])(using scala.collection.mutable.Map.empty[TypedName, Boolean]) + val key = MethodKey(ref, false) + val sym = readMethodSyms(key) + Ref(sym).appliedTo(in.asTerm).asExprOf[T] + // val tin = aE.asExprOf[b] + // '{ + // if $tin == null then $out.burpNull() + // else ${ Ref(sym).appliedTo(tin.asTerm, out.asTerm).asExprOf[Unit] } + // } + case _ => // Classes, traits, etc. genDecFnBody[T](ref, in) // Create a new decoder function (presumably for class, trait, etc) diff --git a/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala b/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala index 4952ed9d..90320892 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala @@ -11,22 +11,22 @@ class JsonConfig private[scalajack] ( val tryFailureHandling: TryPolicy, val eitherLeftHandling: EitherLeftPolicy, // val undefinedFieldHandling: UndefinedValueOption = UndefinedValueOption.THROW_EXCEPTION, - val writeNonConstructorFields: Boolean, // -------------------------- val typeHintLabel: String, val typeHintPolicy: TypeHintPolicy, // -------------------------- val enumsAsIds: Option[List[String]], // None=no enums as ids, Some(Nil)=all enums as ids, Some(List(...))=specified classes enums as ids + val _writeNonConstructorFields: Boolean, val _suppressEscapedStrings: Boolean, val _suppressTypeHints: Boolean ): def withNoneAsNull(): JsonConfig = copy(noneAsNull = true) def withTryFailureHandling(tryPolicy: TryPolicy): JsonConfig = copy(tryFailureHandling = tryPolicy) def withEitherLeftHandling(eitherPolicy: EitherLeftPolicy): JsonConfig = copy(eitherLeftHandling = eitherPolicy) - def withWriteNonConstructorFields(nonConstFlds: Boolean): JsonConfig = copy(writeNonConstructorFields = nonConstFlds) def withTypeHintLabel(label: String): JsonConfig = copy(typeHintLabel = label) def withTypeHintPolicy(hintPolicy: TypeHintPolicy): JsonConfig = copy(typeHintPolicy = hintPolicy) def withEnumsAsIds(asIds: Option[List[String]]): JsonConfig = copy(enumsAsIds = asIds) + def writeNonConstructorFields(): JsonConfig = copy(_writeNonConstructorFields = true) def suppressEscapedStrings(): JsonConfig = copy(_suppressEscapedStrings = true) def suppressTypeHints(): JsonConfig = copy(_suppressTypeHints = true) @@ -34,20 +34,20 @@ class JsonConfig private[scalajack] ( noneAsNull: Boolean = noneAsNull, tryFailureHandling: TryPolicy = tryFailureHandling, eitherLeftHandling: EitherLeftPolicy = eitherLeftHandling, - writeNonConstructorFields: Boolean = writeNonConstructorFields, typeHintLabel: String = typeHintLabel, typeHintPolicy: TypeHintPolicy = typeHintPolicy, enumsAsIds: Option[List[String]] = enumsAsIds, + _writeNonConstructorFields: Boolean = _writeNonConstructorFields, _suppressEscapedStrings: Boolean = _suppressEscapedStrings, _suppressTypeHints: Boolean = _suppressTypeHints ): JsonConfig = new JsonConfig( noneAsNull, tryFailureHandling, eitherLeftHandling, - writeNonConstructorFields, typeHintLabel, typeHintPolicy, enumsAsIds, + _writeNonConstructorFields, _suppressEscapedStrings, _suppressTypeHints ) @@ -69,10 +69,10 @@ object JsonConfig noneAsNull = false, tryFailureHandling = TryPolicy.AS_NULL, eitherLeftHandling = EitherLeftPolicy.AS_VALUE, - writeNonConstructorFields = false, typeHintLabel = "_hint", typeHintPolicy = TypeHintPolicy.SIMPLE_CLASSNAME, enumsAsIds = None, + _writeNonConstructorFields = false, _suppressEscapedStrings = false, _suppressTypeHints = false ): @@ -84,7 +84,6 @@ object JsonConfig val jc = JsonConfig .withTryFailureHandling(${ Expr(x.tryFailureHandling) }) .withEitherLeftHandling(${ Expr(x.eitherLeftHandling) }) - .withWriteNonConstructorFields(${ Expr(x.writeNonConstructorFields) }) .withTypeHintLabel(${ Expr(x.typeHintLabel) }) .withTypeHintPolicy(${ Expr(x.typeHintPolicy) }) .withEnumsAsIds(${ Expr(x.enumsAsIds) }) @@ -97,10 +96,14 @@ object JsonConfig else '{ jc2 } } val jc4 = ${ - if !x._suppressTypeHints then '{ jc2.suppressTypeHints() } + if !x._suppressTypeHints then '{ jc3.suppressTypeHints() } else '{ jc3 } } - jc4 + val jc5 = ${ + if !x._writeNonConstructorFields then '{ jc4.writeNonConstructorFields() } + else '{ jc4 } + } + jc5 } } @@ -120,10 +123,10 @@ object JsonConfig $tryFailureHandlerE, $eitherLeftHandlerE, // $undefinedFieldHandlingE, - $writeNonConstructorFieldsE, $typeHintLabelE, $typeHintPolicyE, $enumsAsIdsE, + $writeNonConstructorFieldsE, $suppressEscapedStringsE, $suppressTypeHintsE ) @@ -134,10 +137,10 @@ object JsonConfig extract("noneAsNull", noneAsNullE), extract("tryFailureHandler", tryFailureHandlerE), extract("eitherLeftHandler", eitherLeftHandlerE), - extract("writeNonConstructorFields", writeNonConstructorFieldsE), extract("typeHintLabel", typeHintLabelE), extract("typeHintPolicy", typeHintPolicyE), extract("enumsAsIds", enumsAsIdsE), + extract("_writeNonConstructorFields", writeNonConstructorFieldsE), extract("_suppressEscapedStrings", suppressEscapedStringsE), extract("_suppressTypeHints", suppressTypeHintsE) ) @@ -147,16 +150,16 @@ object JsonConfig println("ERROR: " + x.getMessage) None } - case '{ JsonConfig } => Some(JsonConfig) - case '{ ($x: JsonConfig).withNoneAsNull() } => Some(x.valueOrAbort.withNoneAsNull()) - case '{ ($x: JsonConfig).withTryFailureHandling($v) } => Some(x.valueOrAbort.withTryFailureHandling(v.valueOrAbort)) - case '{ ($x: JsonConfig).withEitherLeftHandling($v) } => Some(x.valueOrAbort.withEitherLeftHandling(v.valueOrAbort)) - case '{ ($x: JsonConfig).withWriteNonConstructorFields($v) } => Some(x.valueOrAbort.withWriteNonConstructorFields(v.valueOrAbort)) - case '{ ($x: JsonConfig).withTypeHintLabel($v) } => Some(x.valueOrAbort.withTypeHintLabel(v.valueOrAbort)) - case '{ ($x: JsonConfig).withTypeHintPolicy($v) } => Some(x.valueOrAbort.withTypeHintPolicy(v.valueOrAbort)) - case '{ ($x: JsonConfig).withEnumsAsIds($v) } => Some(x.valueOrAbort.withEnumsAsIds(v.valueOrAbort)) - case '{ ($x: JsonConfig).suppressEscapedStrings() } => Some(x.valueOrAbort.suppressEscapedStrings()) - case '{ ($x: JsonConfig).suppressTypeHints() } => Some(x.valueOrAbort.suppressTypeHints()) + case '{ JsonConfig } => Some(JsonConfig) + case '{ ($x: JsonConfig).withNoneAsNull() } => Some(x.valueOrAbort.withNoneAsNull()) + case '{ ($x: JsonConfig).withTryFailureHandling($v) } => Some(x.valueOrAbort.withTryFailureHandling(v.valueOrAbort)) + case '{ ($x: JsonConfig).withEitherLeftHandling($v) } => Some(x.valueOrAbort.withEitherLeftHandling(v.valueOrAbort)) + case '{ ($x: JsonConfig).withTypeHintLabel($v) } => Some(x.valueOrAbort.withTypeHintLabel(v.valueOrAbort)) + case '{ ($x: JsonConfig).withTypeHintPolicy($v) } => Some(x.valueOrAbort.withTypeHintPolicy(v.valueOrAbort)) + case '{ ($x: JsonConfig).withEnumsAsIds($v) } => Some(x.valueOrAbort.withEnumsAsIds(v.valueOrAbort)) + case '{ ($x: JsonConfig).writeNonConstructorFields() } => Some(x.valueOrAbort.writeNonConstructorFields()) + case '{ ($x: JsonConfig).suppressEscapedStrings() } => Some(x.valueOrAbort.suppressEscapedStrings()) + case '{ ($x: JsonConfig).suppressTypeHints() } => Some(x.valueOrAbort.suppressTypeHints()) } private[scalajack] given ToExpr[TryPolicy] with { diff --git a/src/main/scala/co.blocke.scalajack/json/package.scala b/src/main/scala/co.blocke.scalajack/json/package.scala index ceccf26f..f68c898c 100644 --- a/src/main/scala/co.blocke.scalajack/json/package.scala +++ b/src/main/scala/co.blocke.scalajack/json/package.scala @@ -11,6 +11,10 @@ val BUFFER_EXCEEDED: Char = 7 // Old "BELL" ASCII value, used as a marker when w val END_OF_STRING: Char = 3 inline def lastPart(n: String) = n.split('.').last.stripSuffix("$") +inline def allButLastPart(n: String) = + val l = n.lastIndexOf('.') + if l >= 0 then n.substring(0, l) + else n val random = new scala.util.Random() def scramble(hash: Int): String = @@ -19,7 +23,7 @@ def scramble(hash: Int): String = if digits(0) % 2 == 0 then s"${last5(0)}${digits(0)}${last5(1)}${digits(1)}${last5(2)}-${digits(2)}${last5(3)}${digits(3)}-${last5(4)}${digits(4)}A" else s"${digits(0)}${last5(0)}${digits(1)}${last5(1)}${digits(2)}-${last5(2)}${digits(3)}${last5(3)}-${digits(4)}${last5(4)}B" -def descrambleTest(in: String, hash: Int): Boolean = +def descramble(in: String, hash: Int): Boolean = val last5 = f"$hash%05d".takeRight(5) in.last match case 'A' if in.length == 13 => "" + in(0) + in(2) + in(4) + in(7) + in(10) == last5 diff --git a/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala b/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala index 488d06c4..2adac083 100644 --- a/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala +++ b/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala @@ -125,7 +125,8 @@ case class JsonSource(js: CharSequence): i += 1 bs = fieldNameMatrix.exact(bs, fi) if readToken() != ':' then throw new JsonParseError(s"Expected ':' field separator but found $here", this) - fieldNameMatrix.first(bs) + val ret = fieldNameMatrix.first(bs) + ret @tailrec final def parseMap[K, V](kf: () => K, vf: () => V, acc: Map[K, V], isFirst: Boolean = true): Map[K, V] = // initial '{' already consumed @@ -144,6 +145,31 @@ case class JsonSource(js: CharSequence): parseMap[K, V](kf, vf, acc + (key -> value), false) case c => throw JsonParseError(s"Expected either object end '}' or field separator ',' here but got '$c'", this) + @tailrec + final def findObjectField(fieldName: String): Option[String] = + val mark = i + expectToken('{') + readToken() match + case '}' => + i = mark + None + case '"' => + val endI = parseString(i) + val str = js.subSequence(i, endI).toString + i = endI + 1 + expectToken(':') + if str == fieldName then + val found = Some(expectString()) + i = mark + found + else + skipValue() + if readToken() == '}' then backspace() // else consume ',' + findObjectField(fieldName) + case t => + backspace() + throw JsonParseError(s"Expected either string start '\"' or object end '}' but got '$t'", this) + // Array and Tuple... // ======================================================= diff --git a/src/test/scala/co.blocke.scalajack/json/classes/ClassSpec.scala b/src/test/scala/co.blocke.scalajack/json/classes/ClassSpec.scala new file mode 100644 index 00000000..de14c777 --- /dev/null +++ b/src/test/scala/co.blocke.scalajack/json/classes/ClassSpec.scala @@ -0,0 +1,143 @@ +package co.blocke.scalajack +package json +package classes + +import ScalaJack.* +import co.blocke.scala_reflection.* +import org.scalatest.funspec.AnyFunSpec +import org.scalatest.matchers.should.Matchers.* +import org.scalatest.* +import scala.util.* +import TestUtil.* + +import java.util.UUID + +class ClassSpec() extends AnyFunSpec with JsonMatchers: + opaque type phone = String + + describe(colorString("-------------------------------\n: Class Tests :\n-------------------------------", Console.YELLOW)) { + describe(colorString("+++ Positive Tests +++")) { + it("Simple case class must work (with field renaming)") { + val inst = Person("Bob", 34) + val sj = sjCodecOf[Person] + val js = sj.toJson(inst) + js should matchJson("""{"name":"Bob","duration":34}""") + sj.fromJson(js) shouldEqual(inst) + } + it("Inherited class must work") { + val inst = Child("Bob", 34, 3) + val sj = sjCodecOf[Child] + val js = sj.toJson(inst) + js should matchJson("""{"name":"Bob","age":34,"phase":3}""") + sj.fromJson(js) shouldEqual(inst) + } + it("Non-constructor fields of class must work") { + val inst = Parent(99, List("x","y")) + inst.hidden_=(true) + inst.nope_=(false) + inst.foo = "we'll see" + val sj = sjCodecOf[Parent](JsonConfig.writeNonConstructorFields()) + val js = sj.toJson(inst) + js should matchJson("""{"phase":99,"stuff":["x","y"],"foo":"we'll see","hidden":true}""") + val re = sj.fromJson(js) + re.phase shouldEqual(inst.phase) + re.stuff shouldEqual(inst.stuff) + re.foo shouldEqual(inst.foo) + re.hidden shouldEqual(inst.hidden) + } + it("Block non-constructor fields of class must work") { + val inst = Parent(99, List("x","y")) + inst.hidden_=(true) + inst.nope_=(false) + val sj = sjCodecOf[Parent] + val js = sj.toJson(inst) + js should matchJson("""{"phase":99,"stuff":["x","y"]}""") + val re = sj.fromJson(js) + re.phase shouldEqual(inst.phase) + re.stuff shouldEqual(inst.stuff) + re.foo shouldEqual("ok") + re.hidden shouldEqual(false) + } + it("Parameterized class must work") { + val num: phone = "123-456-7890" + val inst = Params(List(Person("Bob", 34), Person("Sarah", 28)), Some(num)) + val sj = sjCodecOf[Params[Person, phone]] + val js = sj.toJson(inst) + js should matchJson("""{"a":[{"name":"Bob","duration":34},{"name":"Sarah","duration":28}],"b":"123-456-7890"}""") + sj.fromJson(js) shouldEqual(inst) + } + + it("Sealed abstract class with case objects and case classes must work") { + val inst = AbstractClassHolder(Start2, Fish2("Beta", false), Miami2(101.1)) + val sj = sjCodecOf[AbstractClassHolder] + val js = sj.toJson(inst) + js should matchJson("""{"a":"Start2","b":{"_hint":"Fish2","species":"Beta","freshwater":false},"c":{"_hint":"Miami2","temp":101.1}}""") + val re = sj.fromJson(js) + re.a shouldEqual(inst.a) + re.b shouldEqual(inst.b) + (re.c.asInstanceOf[Miami2].temp == inst.c.asInstanceOf[Miami2].temp) shouldEqual(true) + } + it("Sealed abstract class with modified type hint label must work") { + val inst = AbstractClassHolder(Start2, Fish2("Beta", false), Miami2(101.1)) + val sj = sjCodecOf[AbstractClassHolder](JsonConfig.withTypeHintLabel("ref")) + val js = sj.toJson(inst) + js should matchJson("""{"a":"Start2","b":{"ref":"Fish2","species":"Beta","freshwater":false},"c":{"ref":"Miami2","temp":101.1}}""") + val re = sj.fromJson(js) + re.a shouldEqual(inst.a) + re.b shouldEqual(inst.b) + (re.c.asInstanceOf[Miami2].temp == inst.c.asInstanceOf[Miami2].temp) shouldEqual(true) + } + it("Sealed abstract class with type hint policy SCRAMBLE_CLASSNAME label must work") { + val inst = AbstractClassHolder(Start2, Fish2("Beta", false), Miami2(101.1)) + val sj = sjCodecOf[AbstractClassHolder](JsonConfig.withTypeHintPolicy(TypeHintPolicy.SCRAMBLE_CLASSNAME)) + val js = sj.toJson(inst) + val diff = parseJValue(js).diff(parseJValue("""{"a":"Start2","b":{"_hint":"82949-049-49A","species":"Beta","freshwater":false},"c":{"_hint":"53150-867-73B","temp":101.1}}""")) + val diffMap = diff.changed.values.asInstanceOf[Map[String, Map[String, _]]] + assert(diffMap("b").contains("_hint") && diffMap("c").contains("_hint") == true) // ie only the scrambled _hint values are different + val re = sj.fromJson(js) + re.a shouldEqual(inst.a) + re.b shouldEqual(inst.b) + (re.c.asInstanceOf[Miami2].temp == inst.c.asInstanceOf[Miami2].temp) shouldEqual(true) + } + it("Sealed abstract class with type hint policy USE_ANNOTATION label must work") { + val inst = AbstractClassHolder(Start2, Fish2("Beta", false), Miami2(101.1)) + val sj = sjCodecOf[AbstractClassHolder](JsonConfig.withTypeHintPolicy(TypeHintPolicy.USE_ANNOTATION)) + val js = sj.toJson(inst) + js should matchJson("""{"a":"Start2","b":{"_hint":"flipper","species":"Beta","freshwater":false},"c":{"_hint":"vice","temp":101.1}}""") + val re = sj.fromJson(js) + re.a shouldEqual(inst.a) + re.b shouldEqual(inst.b) + (re.c.asInstanceOf[Miami2].temp == inst.c.asInstanceOf[Miami2].temp) shouldEqual(true) + } + it("Parameterized sealed abstract class must work") { + val inst = AbstractClassHolder2(Thing2(15L,"wow")) + val sj = sjCodecOf[AbstractClassHolder2[Long]] + val js = sj.toJson(inst) + js should matchJson("""{"a":{"_hint":"Thing2","t":15,"s":"wow"}}""") + val re = sj.fromJson(js) + re.a.asInstanceOf[Thing2[Long]].t shouldEqual(15L) + re.a.asInstanceOf[Thing2[Long]].s shouldEqual("wow") + } + it("Top-level abstract class must work") { + val inst: AThing[Long] = Thing2(99L,"ok") + val sj = sjCodecOf[AThing[Long]] + val js = sj.toJson(inst) + js should matchJson("""{"_hint":"Thing2","t":99,"s":"ok"}""") + val re = sj.fromJson(js) + re.asInstanceOf[Thing2[Long]].t shouldEqual(99L) + re.asInstanceOf[Thing2[Long]].s shouldEqual("ok") + } + it("Self-referencing class must work (bonus: parameterized self-referencing class)") { + val inst = Empl("abc123", 5, Empl("xyz11", -1, null, Nil), List(Empl("tru777", 0, null, Nil), Empl("pop9", 9, null, Nil))) + val sj = sjCodecOf[Empl[Int]] + val js = sj.toJson(inst) + js should matchJson("""{"id":"abc123","data":5,"boss":{"id":"xyz11","data":-1,"boss":null,"coworkers":[]},"coworkers":[{"id":"tru777","data":0,"boss":null,"coworkers":[]},{"id":"pop9","data":9,"boss":null,"coworkers":[]}]}""") + sj.fromJson(js) shouldEqual(inst) + } + // it("Java classes must work") { + // val inst = new SampleClass("John Doe", 45, "123 Main St") + // val js = sjCodecOf[SampleClass].toJson(inst) + // js should matchJson("""{"address":"123 Main St","name":"John Doe"}""") + // } + } + } diff --git a/src/test/scala/co.blocke.scalajack/json/classes/ClassSpec.scalax b/src/test/scala/co.blocke.scalajack/json/classes/ClassSpec.scalax deleted file mode 100644 index 45e3598d..00000000 --- a/src/test/scala/co.blocke.scalajack/json/classes/ClassSpec.scalax +++ /dev/null @@ -1,109 +0,0 @@ -package co.blocke.scalajack -package json -package classes - -import ScalaJack.* -import co.blocke.scala_reflection.* -import org.scalatest.funspec.AnyFunSpec -import org.scalatest.matchers.should.Matchers.* -import org.scalatest.* -import scala.util.* -import TestUtil.* - -import java.util.UUID - -class ClassSpec() extends AnyFunSpec with JsonMatchers: - opaque type phone = String - - describe(colorString("-------------------------------\n: Class Tests :\n-------------------------------", Console.YELLOW)) { - describe(colorString("+++ Positive Tests +++")) { - it("Simple case class must work") { - val inst = Person("Bob", 34) - val js = sj[Person].toJson(inst) - js should matchJson("""{"name":"Bob","duration":34}""") - } - it("Inherited class must work") { - val inst = Child("Bob", 34, 3) - val js = sj[Child].toJson(inst) - js should matchJson("""{"name":"Bob","age":34,"phase":3}""") - } - it("Non-constructor fields of class must work") { - val inst = Parent(99) - inst.hidden_=(true) - inst.nope_=(false) - val js = sj[Parent].toJson(inst) - js should matchJson("""{"phase":99,"foo":"ok","hidden":true}""") - } - it("Block non-constructor fields of class must work") { - val inst = Parent(99) - inst.hidden_=(true) - inst.nope_=(false) - val js = sj[Parent](JsonConfig.withWriteNonConstructorFields(false)).toJson(inst) - js should matchJson("""{"phase":99}""") - } - it("Parameterized class must work") { - val num: phone = "123-456-7890" - val inst = Params(List(Person("Bob", 34), Person("Sarah", 28)), Some(num)) - val js = sj[Params[Person, phone]].toJson(inst) - js should matchJson("""{"a":[{"name":"Bob","duration":34},{"name":"Sarah","duration":28}],"b":"123-456-7890"}""") - } - - it("Sealed trait with case objects and case classes must work") { - val inst = TraitHolder(Start, Fish("Beta", false), Miami(101.1)) - val js = sj[TraitHolder].toJson(inst) - js should matchJson("""{"a":"Start","b":{"_hint":"Fish","species":"Beta","freshwater":false},"c":{"_hint":"Miami","temp":101.1}}""") - } - it("Sealed trait with modified type hint label must work") { - val inst = TraitHolder(Start, Fish("Beta", false), Miami(101.1)) - val js = sj[TraitHolder](JsonConfig.withTypeHintLabel("ref")).toJson(inst) - js should matchJson("""{"a":"Start","b":{"ref":"Fish","species":"Beta","freshwater":false},"c":{"ref":"Miami","temp":101.1}}""") - } - it("Sealed trait with type hint policy SCRAMBLE_CLASSNAME label must work") { - val inst = TraitHolder(Start, Fish("Beta", false), Miami(101.1)) - val js = sj[TraitHolder](JsonConfig.withTypeHintPolicy(TypeHintPolicy.SCRAMBLE_CLASSNAME)).toJson(inst) - val diff = parseJValue(js).diff(parseJValue("""{"a":"Start","b":{"_hint":"82949-049-49A","species":"Beta","freshwater":false},"c":{"_hint":"53150-867-73B","temp":101.1}}""")) - val diffMap = diff.changed.values.asInstanceOf[Map[String, Map[String, _]]] - assert(diffMap("b").contains("_hint") && diffMap("c").contains("_hint") == true) - } - it("Sealed trait with type hint policy USE_ANNOTATION label must work") { - val inst = TraitHolder(Start, Fish("Beta", false), Miami(101.1)) - val js = sj[TraitHolder](JsonConfig.withTypeHintPolicy(TypeHintPolicy.USE_ANNOTATION)).toJson(inst) - js should matchJson("""{"a":"Start","b":{"_hint":"flipper","species":"Beta","freshwater":false},"c":{"_hint":"vice","temp":101.1}}""") - } - - it("Sealed abstract class with case objects and case classes must work") { - val inst = AbstractClassHolder(Start2, Fish2("Beta", false), Miami2(101.1)) - val js = sj[AbstractClassHolder].toJson(inst) - js should matchJson("""{"a":"Start2","b":{"_hint":"Fish2","species":"Beta","freshwater":false},"c":{"_hint":"Miami2","temp":101.1}}""") - } - it("Sealed abstract class with modified type hint label must work") { - val inst = AbstractClassHolder(Start2, Fish2("Beta", false), Miami2(101.1)) - val js = sj[AbstractClassHolder](JsonConfig.withTypeHintLabel("ref")).toJson(inst) - js should matchJson("""{"a":"Start2","b":{"ref":"Fish2","species":"Beta","freshwater":false},"c":{"ref":"Miami2","temp":101.1}}""") - } - it("Sealed abstract class with type hint policy SCRAMBLE_CLASSNAME label must work") { - val inst = AbstractClassHolder(Start2, Fish2("Beta", false), Miami2(101.1)) - val js = sj[AbstractClassHolder](JsonConfig.withTypeHintPolicy(TypeHintPolicy.SCRAMBLE_CLASSNAME)).toJson(inst) - val diff = parseJValue(js).diff(parseJValue("""{"a":"Start2","b":{"_hint":"82949-049-49A","species":"Beta","freshwater":false},"c":{"_hint":"53150-867-73B","temp":101.1}}""")) - val diffMap = diff.changed.values.asInstanceOf[Map[String, Map[String, _]]] - assert(diffMap("b").contains("_hint") && diffMap("c").contains("_hint") == true) - } - it("Sealed abstract class with type hint policy USE_ANNOTATION label must work") { - val inst = AbstractClassHolder(Start2, Fish2("Beta", false), Miami2(101.1)) - val js = sj[AbstractClassHolder](JsonConfig.withTypeHintPolicy(TypeHintPolicy.USE_ANNOTATION)).toJson(inst) - js should matchJson("""{"a":"Start2","b":{"_hint":"flipper","species":"Beta","freshwater":false},"c":{"_hint":"vice","temp":101.1}}""") - } - - it("Self-referencing class must work (bonus: parameterized self-referencing class)") { - val inst = Empl("abc123", 5, Empl("xyz11", -1, null, Nil), List(Empl("tru777", 0, null, Nil), Empl("pop9", 9, null, Nil))) - val js = sj[Empl[Int]].toJson(inst) - js should matchJson("""{"id":"abc123","data":5,"boss":{"id":"xyz11","data":-1,"boss":null,"coworkers":[]},"coworkers":[{"id":"tru777","data":0,"boss":null,"coworkers":[]},{"id":"pop9","data":9,"boss":null,"coworkers":[]}]}""") - } - - it("Java classes must work") { - val inst = new SampleClass("John Doe", 45, "123 Main St") - val js = sj[SampleClass].toJson(inst) - js should matchJson("""{"address":"123 Main St","name":"John Doe"}""") - } - } - } diff --git a/src/test/scala/co.blocke.scalajack/json/classes/Model.scala b/src/test/scala/co.blocke.scalajack/json/classes/Model.scala index 3e3c8ef3..52e586c6 100644 --- a/src/test/scala/co.blocke.scalajack/json/classes/Model.scala +++ b/src/test/scala/co.blocke.scalajack/json/classes/Model.scala @@ -4,10 +4,11 @@ package classes import co.blocke.scala_reflection.Ignore import dotty.tools.repl.Command +import java.net.NoRouteToHostException case class Person(name: String, @Change(name = "duration") age: Int) -class Parent(val phase: Int): +class Parent(val phase: Int, var stuff: List[String]): private var _hidden: Boolean = false def hidden: Boolean = _hidden def hidden_=(h: Boolean) = _hidden = h @@ -19,7 +20,7 @@ class Parent(val phase: Int): var foo: String = "ok" @Ignore var noFoo: String = "not ok" -case class Child(name: String, age: Int, override val phase: Int) extends Parent(phase) +case class Child(name: String, age: Int, override val phase: Int) extends Parent(phase, Nil) case class Params[X, Y](a: List[X], b: Option[Y]) @@ -38,7 +39,14 @@ class Dallas(val pop: Int) extends City @TypeHint(hintValue = "vice") class Miami(val temp: Double) extends City -case class TraitHolder(a: Command, b: Animal, c: City) +sealed trait Route +class CityRoute(val numStreets: Int) extends Route +// Testing indirection. In real-world scenario all your sealed trait's classes +// must be defined in one file. Implementation classes like CityRouteImpl could +// be in other files so the sealed trait's file doesn't grow huge. +case class CityRouteImpl(override val numStreets: Int) extends CityRoute(numStreets) + +case class TraitHolder(a: Command, b: Animal, c: City, d: Route) sealed abstract class Command2 case object Start2 extends Command2 @@ -55,6 +63,11 @@ class Dallas2(val pop: Int) extends City2 @TypeHint(hintValue = "vice") class Miami2(val temp: Double) extends City2 +sealed abstract class AThing[T] +class Thing1[T](val t: T) extends AThing[T] +class Thing2[T](val t: T, val s: String) extends AThing[T] + case class AbstractClassHolder(a: Command2, b: Animal2, c: City2) +case class AbstractClassHolder2[P](a: AThing[P]) case class Empl[T](id: String, data: T, boss: Empl[T], coworkers: List[Empl[T]]) diff --git a/src/test/scala/co.blocke.scalajack/json/classes/TraitSpec.scalax b/src/test/scala/co.blocke.scalajack/json/classes/TraitSpec.scalax new file mode 100644 index 00000000..eda957a3 --- /dev/null +++ b/src/test/scala/co.blocke.scalajack/json/classes/TraitSpec.scalax @@ -0,0 +1,47 @@ +package co.blocke.scalajack +package json +package classes + +import ScalaJack.* +import co.blocke.scala_reflection.* +import org.scalatest.funspec.AnyFunSpec +import org.scalatest.matchers.should.Matchers.* +import org.scalatest.* +import scala.util.* +import TestUtil.* + +import java.util.UUID + +class TraitSpec() extends AnyFunSpec with JsonMatchers: + opaque type phone = String + + describe(colorString("-------------------------------\n: Trait Tests :\n-------------------------------", Console.YELLOW)) { + describe(colorString("+++ Positive Tests +++")) { + it("Sealed trait with case objects and case classes must work") { + val inst = TraitHolder(Start, Fish("Beta", false), Miami(101.1)) + val js = sj[TraitHolder].toJson(inst) + js should matchJson("""{"a":"Start","b":{"_hint":"Fish","species":"Beta","freshwater":false},"c":{"_hint":"Miami","temp":101.1}}""") + } + it("Sealed trait with modified type hint label must work") { + val inst = TraitHolder(Start, Fish("Beta", false), Miami(101.1)) + val js = sj[TraitHolder](JsonConfig.withTypeHintLabel("ref")).toJson(inst) + js should matchJson("""{"a":"Start","b":{"ref":"Fish","species":"Beta","freshwater":false},"c":{"ref":"Miami","temp":101.1}}""") + } + it("Sealed trait with type hint policy SCRAMBLE_CLASSNAME label must work") { + val inst = TraitHolder(Start, Fish("Beta", false), Miami(101.1)) + val js = sj[TraitHolder](JsonConfig.withTypeHintPolicy(TypeHintPolicy.SCRAMBLE_CLASSNAME)).toJson(inst) + val diff = parseJValue(js).diff(parseJValue("""{"a":"Start","b":{"_hint":"82949-049-49A","species":"Beta","freshwater":false},"c":{"_hint":"53150-867-73B","temp":101.1}}""")) + val diffMap = diff.changed.values.asInstanceOf[Map[String, Map[String, _]]] + assert(diffMap("b").contains("_hint") && diffMap("c").contains("_hint") == true) + } + it("Sealed trait with type hint policy USE_ANNOTATION label must work") { + val inst = TraitHolder(Start, Fish("Beta", false), Miami(101.1)) + val js = sj[TraitHolder](JsonConfig.withTypeHintPolicy(TypeHintPolicy.USE_ANNOTATION)).toJson(inst) + js should matchJson("""{"a":"Start","b":{"_hint":"flipper","species":"Beta","freshwater":false},"c":{"_hint":"vice","temp":101.1}}""") + } + + it("Sealed trait with class children implemented by case classes (in separate compilation unit)") { + (pending) + } + } + } diff --git a/src/test/scala/co.blocke.scalajack/json/collections/JavaMapSpec.scala b/src/test/scala/co.blocke.scalajack/json/collections/JavaMapSpec.scala index 924f6057..ada78bbc 100644 --- a/src/test/scala/co.blocke.scalajack/json/collections/JavaMapSpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/collections/JavaMapSpec.scala @@ -20,7 +20,7 @@ class JavaMapSpec() extends AnyFunSpec with JsonMatchers: val sj = sjCodecOf[JMapHolder[Int, Int]] val js = sj.toJson(inst) js should matchJson("""{"a":null}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Map key of string must work") { val m: java.util.Map[String, Int] = new java.util.HashMap[String, Int]() @@ -30,8 +30,8 @@ class JavaMapSpec() extends AnyFunSpec with JsonMatchers: val sj = sjCodecOf[JMapHolder[String, Int]] val js = sj.toJson(inst) js should matchJson("""{"a":{"x":1,"y":2}}""") - sj.fromJson(js) shouldEqual(inst) - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) + sj.fromJson(js) shouldEqual (inst) } it("Map key of long must work") { val m: java.util.Map[Long, Int] = new java.util.HashMap[Long, Int]() @@ -41,7 +41,7 @@ class JavaMapSpec() extends AnyFunSpec with JsonMatchers: val sj = sjCodecOf[JMapHolder[Long, Int]] val js = sj.toJson(inst) js should matchJson("""{"a":{"15":1,"25":2}}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Map key of boolean must work") { val m: java.util.Map[Boolean, Int] = new java.util.HashMap[Boolean, Int]() @@ -51,7 +51,7 @@ class JavaMapSpec() extends AnyFunSpec with JsonMatchers: val sj = sjCodecOf[JMapHolder[Boolean, Int]] val js = sj.toJson(inst) js should matchJson("""{"a":{"true":1,"false":2}}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Map key of uuid must work") { val m: java.util.Map[UUID, String] = new java.util.HashMap[UUID, String]() @@ -61,7 +61,7 @@ class JavaMapSpec() extends AnyFunSpec with JsonMatchers: val sj = sjCodecOf[JMapHolder[UUID, String]] val js = sj.toJson(inst) js should matchJson("""{"a":{"1b9ab03f-26a3-4ec5-a8dd-d5122ff86b03":"x","09abdeb1-8b07-4683-8f97-1f5621696008":"y"}}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Map key of value class must work") { val m: java.util.Map[Distance, String] = new java.util.HashMap[Distance, String]() @@ -71,7 +71,7 @@ class JavaMapSpec() extends AnyFunSpec with JsonMatchers: val sj = sjCodecOf[JMapHolder[Distance, String]] val js = sj.toJson(inst) js should matchJson("""{"a":{"1.23":"w","4.56":"y"}}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Map value of string must work") { val m: java.util.Map[String, String] = new java.util.HashMap[String, String]() @@ -81,7 +81,7 @@ class JavaMapSpec() extends AnyFunSpec with JsonMatchers: val sj = sjCodecOf[JMapHolder[String, String]] val js = sj.toJson(inst) js should matchJson("""{"a":{"w":"x","y":"z"}}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Map value of long must work") { val m: java.util.Map[String, Long] = new java.util.HashMap[String, Long]() @@ -91,7 +91,7 @@ class JavaMapSpec() extends AnyFunSpec with JsonMatchers: val sj = sjCodecOf[JMapHolder[String, Long]] val js = sj.toJson(inst) js should matchJson("""{"a":{"w":3,"y":4}}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Map value of boolean must work") { val m: java.util.Map[String, Boolean] = new java.util.HashMap[String, Boolean]() @@ -101,7 +101,7 @@ class JavaMapSpec() extends AnyFunSpec with JsonMatchers: val sj = sjCodecOf[JMapHolder[String, Boolean]] val js = sj.toJson(inst) js should matchJson("""{"a":{"w":true,"y":false}}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Map value of uuid must work") { val m: java.util.Map[String, UUID] = new java.util.HashMap[String, UUID]() @@ -111,7 +111,7 @@ class JavaMapSpec() extends AnyFunSpec with JsonMatchers: val sj = sjCodecOf[JMapHolder[String, UUID]] val js = sj.toJson(inst) js should matchJson("""{"a":{"x":"1b9ab03f-26a3-4ec5-a8dd-d5122ff86b03","y":"09abdeb1-8b07-4683-8f97-1f5621696008"}}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Map value of Seq must work") { val m: java.util.Map[String, List[Int]] = new java.util.HashMap[String, List[Int]]() @@ -121,7 +121,7 @@ class JavaMapSpec() extends AnyFunSpec with JsonMatchers: val sj = sjCodecOf[JMapHolder[String, List[Int]]] val js = sj.toJson(inst) js should matchJson("""{"a":{"w":[1,2],"y":[3,4]}}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Map value of Map (nested) must work") { val m: java.util.Map[String, Map[String, Int]] = new java.util.HashMap[String, Map[String, Int]]() @@ -131,7 +131,7 @@ class JavaMapSpec() extends AnyFunSpec with JsonMatchers: val sj = sjCodecOf[JMapHolder[String, Map[String, Int]]] val js = sj.toJson(inst) js should matchJson("""{"a":{"w":{"r":3,"t":4},"y":{"s":7,"q":9}}}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Map value of class must work") { val m: java.util.Map[String, Person] = new java.util.HashMap[String, Person]() @@ -141,7 +141,7 @@ class JavaMapSpec() extends AnyFunSpec with JsonMatchers: val sj = sjCodecOf[JMapHolder[String, Person]] val js = sj.toJson(inst) js should matchJson("""{"a":{"w":{"name":"Bob","age":34},"y":{"name":"Sally","age":25}}}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Map value of union type must work") { val m: java.util.Map[String, Int | List[String]] = new java.util.HashMap[String, Int | List[String]]() @@ -151,7 +151,7 @@ class JavaMapSpec() extends AnyFunSpec with JsonMatchers: val sj = sjCodecOf[JMapHolder[String, Int | List[String]]] val js = sj.toJson(inst) js should matchJson("""{"a":{"w":3,"y":["wow","blah"]}}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Map value of value class must work") { val m: java.util.Map[String, Distance] = new java.util.HashMap[String, Distance]() @@ -161,7 +161,7 @@ class JavaMapSpec() extends AnyFunSpec with JsonMatchers: val sj = sjCodecOf[JMapHolder[String, Distance]] val js = sj.toJson(inst) js should matchJson("""{"a":{"w":1.23,"y":4.56}}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } } describe(colorString("+++ Special/Specific Map Tests +++")) { @@ -173,7 +173,7 @@ class JavaMapSpec() extends AnyFunSpec with JsonMatchers: val sj = sjCodecOf[Holder[java.util.NavigableMap[String, Int]]] val js = sj.toJson(inst) js should matchJson("""{"a":{"x":1,"y":2}}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("SortedMap must work") { val m: java.util.SortedMap[String, Int] = new java.util.TreeMap[String, Int]() @@ -183,7 +183,7 @@ class JavaMapSpec() extends AnyFunSpec with JsonMatchers: val sj = sjCodecOf[Holder[java.util.SortedMap[String, Int]]] val js = sj.toJson(inst) js should matchJson("""{"a":{"x":1,"y":2}}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("TreeMap must work") { val m = new java.util.TreeMap[String, Int]() @@ -193,7 +193,7 @@ class JavaMapSpec() extends AnyFunSpec with JsonMatchers: val sj = sjCodecOf[Holder[java.util.TreeMap[String, Int]]] val js = sj.toJson(inst) js should matchJson("""{"a":{"x":1,"y":2}}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("ConcurrentMap must work") { val m = new java.util.concurrent.ConcurrentHashMap[String, Int]() @@ -203,7 +203,7 @@ class JavaMapSpec() extends AnyFunSpec with JsonMatchers: val sj = sjCodecOf[Holder[java.util.concurrent.ConcurrentMap[String, Int]]] val js = sj.toJson(inst) js should matchJson("""{"a":{"x":1,"y":2}}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("ConcurrentNavigableMap must work") { val m = new java.util.concurrent.ConcurrentSkipListMap[String, Int]() @@ -213,7 +213,7 @@ class JavaMapSpec() extends AnyFunSpec with JsonMatchers: val sj = sjCodecOf[Holder[java.util.concurrent.ConcurrentNavigableMap[String, Int]]] val js = sj.toJson(inst) js should matchJson("""{"a":{"x":1,"y":2}}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Specific must work (eg HashMap)") { val m = new java.util.HashMap[String, Int]() @@ -223,7 +223,7 @@ class JavaMapSpec() extends AnyFunSpec with JsonMatchers: val sj = sjCodecOf[Holder[java.util.HashMap[String, Int]]] val js = sj.toJson(inst) js should matchJson("""{"a":{"x":1,"y":2}}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } } } From 0545e3f220235f81241170c78f267c00ac7a2420 Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Sun, 21 Apr 2024 23:57:33 -0500 Subject: [PATCH 56/65] enum tests done --- build.sbt | 4 +- project/metals.sbt | 4 +- .../json/FastStringBuilder.scala | 6 +- .../json/JsonCodecMaker.scala | 266 ++++-- .../co.blocke.scalajack/json/JsonConfig.scala | 10 +- .../json/reading/Numbers.scala | 18 +- .../json/schema/JsonSchema.scala | 2 +- .../json/writing/AnyWriter.scala | 30 +- .../json/writing/WriteCodec.scalax | 766 ++++++++++++++++++ .../co.blocke.scalajack/run/Record.scala | 14 +- .../co/blocke/scalajack/json/SampleClass.java | 12 +- .../scalajack/json/collections/CarEnum.java | 2 +- .../json/classes/ClassSpec.scala | 275 ++++--- .../json/classes/Model.scala | 1 - .../json/classes/Model2.scala | 5 + .../json/classes/TraitSpec.scala | 65 ++ .../json/classes/TraitSpec.scalax | 47 -- .../json/misc/EnumSpec.scala | 109 +++ .../json/misc/EnumSpec.scalax | 54 -- .../json/misc/MiscSpec.scala | 92 +++ .../json/misc/MiscTests.scalax | 73 -- .../co.blocke.scalajack/json/misc/Model.scala | 20 +- .../json/primitives/Enums.scala | 1 - 23 files changed, 1444 insertions(+), 432 deletions(-) create mode 100644 src/main/scala/co.blocke.scalajack/json/writing/WriteCodec.scalax create mode 100644 src/test/scala/co.blocke.scalajack/json/classes/Model2.scala create mode 100644 src/test/scala/co.blocke.scalajack/json/classes/TraitSpec.scala delete mode 100644 src/test/scala/co.blocke.scalajack/json/classes/TraitSpec.scalax create mode 100644 src/test/scala/co.blocke.scalajack/json/misc/EnumSpec.scala delete mode 100644 src/test/scala/co.blocke.scalajack/json/misc/EnumSpec.scalax create mode 100644 src/test/scala/co.blocke.scalajack/json/misc/MiscSpec.scala delete mode 100644 src/test/scala/co.blocke.scalajack/json/misc/MiscTests.scalax diff --git a/build.sbt b/build.sbt index 71c3d1dd..63d10e11 100644 --- a/build.sbt +++ b/build.sbt @@ -19,7 +19,7 @@ inThisBuild(List( name := "scalajack" ThisBuild / organization := "co.blocke" -ThisBuild / scalaVersion := "3.3.1" +ThisBuild / scalaVersion := "3.4.1" lazy val root = project .in(file(".")) @@ -35,7 +35,7 @@ lazy val root = project Test / parallelExecution := false, scalafmtOnCompile := !isCI, libraryDependencies ++= Seq( - "co.blocke" %% "scala-reflection" % "fixOptionUnit_ea63ce", //"2.0.3", + "co.blocke" %% "scala-reflection" % "fixOptionUnit_7632aa", //"2.0.3", "org.apache.commons" % "commons-text" % "1.11.0", "io.github.kitlangton" %% "neotype" % "0.0.9", "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core" % "2.24.5-SNAPSHOT", diff --git a/project/metals.sbt b/project/metals.sbt index 119c9296..aaf3382f 100644 --- a/project/metals.sbt +++ b/project/metals.sbt @@ -1,6 +1,8 @@ +// format: off // DO NOT EDIT! This file is auto-generated. // This file enables sbt-bloop to create bloop config files. -addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.5.15") +addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.5.17") +// format: on diff --git a/src/main/scala/co.blocke.scalajack/json/FastStringBuilder.scala b/src/main/scala/co.blocke.scalajack/json/FastStringBuilder.scala index adf3d895..e02925b2 100644 --- a/src/main/scala/co.blocke.scalajack/json/FastStringBuilder.scala +++ b/src/main/scala/co.blocke.scalajack/json/FastStringBuilder.scala @@ -7,8 +7,8 @@ import scala.annotation.tailrec // like StringBuilder but doesn't have any encoding or range checks final class FastStringBuilder(initial: Int = 16) { - private[this] var chars: Array[Char] = new Array[Char](initial) - private[this] var i: Int = 0 + private var chars: Array[Char] = new Array[Char](initial) + private var i: Int = 0 def clear() = i = 0 def length = i @@ -41,7 +41,7 @@ final class FastStringBuilder(initial: Int = 16) { 25446, 25702, 25958, 26214 ) - private[this] def appendEscapedUnicode(c: Char): Unit = { + private def appendEscapedUnicode(c: Char): Unit = { append('\\') append('u') append("%04x".format(c.toInt)) diff --git a/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala b/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala index 4851d6d2..9bbc5100 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala @@ -113,7 +113,9 @@ object JsonCodecMaker: case _: TimeRef => true case _: NetRef => true case c: ScalaClassRef[?] if c.isValueClass => true + case _: EnumRef[?] => true case a: AliasRef[?] => testValidMapKey(a.unwrappedType) + case t: TraitRef[?] if t.childrenAreObject => true case _ => false if !isValid then throw new JsonTypeError(s"For JSON serialization, map keys must be a simple type. ${testRef.name} is too complex.") isValid @@ -253,7 +255,7 @@ object JsonCodecMaker: ${ _maybeWrite[lt](prefix, '{ $tin.asInstanceOf[lt] }, t.leftRef.asInstanceOf[RTypeRef[lt]], out, cfg) } } case t: AnyRef => - AnyWriter.okToWrite2(prefix, aE, out, cfg) + AnyWriter.isOkToWrite(prefix, aE, out, cfg) case _ => ref.refType match case '[u] => @@ -316,12 +318,14 @@ object JsonCodecMaker: } } - case t: ScalaClassRef[?] if t.isSealed && t.isAbstractClass => // basically just like sealed trait... + // case t: ScalaClassRef[?] if t.isSealed => // basically just like sealed trait... + case t: Sealable if t.isSealed => // basically just like sealed trait... if t.childrenAreObject then val tin = aE.asExprOf[b] // case object -> just write the simple name of the object '{ - $out.value($tin.getClass.getName.split('.').last.stripSuffix("$")) + if $tin == null then $out.burpNull() + else $out.value($tin.getClass.getName.split('.').last.stripSuffix("$")) } else val cases = t.sealedChildren.map { child => @@ -349,6 +353,20 @@ object JsonCodecMaker: f.fieldRef.refType match case '[z] => val fieldValue = Select.unique(in.asTerm, f.name).asExprOf[z] + // val fieldValue = + // f.fieldRef match + // case e: ScalaEnumerationRef[?] => + // try + // val tE = Select.unique(in.asTerm, f.name).asExpr // .asExprOf[z] + // '{ $tE.asInstanceOf[z] } + // catch { + // case t: Throwable => + // println("BOOM: " + f.name) + // println("Type: " + f.fieldRef.refType) + // throw t + // } + // case _ => + // Select.unique(in.asTerm, f.name).asExprOf[z] val fieldName = changeFieldName(f) maybeWrite[z](fieldName, fieldValue, f.fieldRef.asInstanceOf[RTypeRef[z]], out, cfg) } @@ -462,7 +480,7 @@ object JsonCodecMaker: makeWriteFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => t.elementRef.refType match case '[e] => - val tin = '{ $in.asInstanceOf[java.util.Collection[_]] } + val tin = '{ $in.asInstanceOf[java.util.Collection[?]] } '{ if $tin == null then $out.burpNull() else @@ -525,30 +543,7 @@ object JsonCodecMaker: } } - case t: TraitRef[?] => - if !t.isSealed then throw new JsonUnsupportedType("Non-sealed traits are not supported") - if t.childrenAreObject then - // case object -> just write the simple name of the object - val tin = aE.asExprOf[b] - '{ - $out.value($tin.getClass.getName.split('.').last.stripSuffix("$")) - } - else - // So... sealed trait children could be any of those defined for the trait. We need to - // generate a match/case statement that in turn generates render functions for each - // child of the sealed trait. - // Refer to Jsoniter: JsonCodecMaker.scala around line 920 for example how to do this, incl a wildcard. - // (f: (Expr[U], Expr[JsonOutput]) => Expr[Unit]) - makeWriteFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => - val cases = t.sealedChildren.map { child => - child.refType match - case '[c] => - val subtype = TypeIdent(TypeRepr.of[c].typeSymbol) - val sym = Symbol.newBind(Symbol.spliceOwner, "t", Flags.EmptyFlags, subtype.tpe) - CaseDef(Bind(sym, Typed(Wildcard(), Inferred(subtype.tpe))), None, genEncFnBody[c](child, Ref(sym).asExprOf[c], out, cfg._suppressTypeHints).asTerm) - } :+ CaseDef(Literal(NullConstant()), None, '{ $out.burpNull() }.asTerm) - Match(in.asTerm, cases).asExprOf[Unit] - } + case t: TraitRef[?] => throw JsonUnsupportedType("Non-sealed traits are not supported") // No makeWriteFn here--Option is just a wrapper to the real thingy case t: ScalaOptionRef[?] => @@ -751,7 +746,10 @@ object JsonCodecMaker: case t: ShortRef => if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[Short] }) } else '{ $out.value(${ aE.asExprOf[Short] }) } - case t: StringRef => '{ $out.valueEscaped(${ aE.asExprOf[String] }) } + + case t: StringRef => + if cfg._suppressEscapedStrings then '{ $out.value(${ aE.asExprOf[String] }) } + else '{ $out.valueEscaped(${ aE.asExprOf[String] }) } case t: JBigDecimalRef => if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[java.math.BigDecimal] }) } @@ -814,19 +812,19 @@ object JsonCodecMaker: case '[e] => genWriteVal[e](aE.asInstanceOf[Expr[e]], t.unwrappedType.asInstanceOf[RTypeRef[e]], out, inTuple = inTuple) - // These one's here becaue Enums and their various flavors can be Map keys + // These are here becaue Enums and their various flavors can be Map keys // (EnumRef handles: Scala 3 enum, Scala 2 Enumeration, Java Enumeration) case t: EnumRef[?] => val enumAsId = cfg.enumsAsIds match - case None => false - case Some(Nil) => true - case Some(list) if list.contains(t.name) => true - case _ => false + case List("-") => false + case Nil => true + case list if list.contains(t.name) => true + case _ => false val rtype = t.expr if enumAsId then - if isMapKey then '{ $out.value($rtype.asInstanceOf[EnumRType[_]].ordinal($aE.toString).get.toString) } // stringified id - else '{ $out.value($rtype.asInstanceOf[EnumRType[_]].ordinal($aE.toString).get) } // int value of id - else '{ $out.value($aE.toString) } + if isMapKey then '{ $out.value($rtype.asInstanceOf[EnumRType[?]].ordinal($aE.toString).get.toString) } // stringified id + else '{ $out.value($rtype.asInstanceOf[EnumRType[?]].ordinal($aE.toString).get) } // int value of id + else '{ if $aE == null then $out.burpNull() else $out.value($aE.toString) } // NeoType is a bit of a puzzle-box. To get the correct underlying base type, I had to dig into // the argument of method validate$retainedBody. It happened to have the correctly-typed parameter. @@ -839,21 +837,6 @@ object JsonCodecMaker: val baseTypeRef = ReflectOnType.apply(q)(tt.tpe)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) genWriteVal[u]('{ $aE.asInstanceOf[u] }, baseTypeRef.asInstanceOf[RTypeRef[u]], out) - /* This is how you call make(), which includes validate() - val myMake = module.methodMember("make").head - val tm = Ref(module) - val z = Apply( - Select.unique(tm, "make"), - List( - Expr(List(1, 2, 3)).asTerm - ) - ).asExprOf[Either[String, _]] - '{ - println("Hello...") - println($z) - } - */ - case t: AnyRef => '{ AnyWriter.writeAny($aE, $out, ${ Expr(cfg) }) } // Everything else... @@ -898,7 +881,47 @@ object JsonCodecMaker: case '[b] => r match - case t: ScalaClassRef[?] if t.isSealed && t.isAbstractClass && !t.childrenAreObject => + case t: AnyRef => + makeReadFn[T](MethodKey(t, false), in)(in => + '{ + if $in.expectNull() then null + else + $in.readToken() match + case '[' => + $in.backspace() + val parsedArray = $in.expectArray[Any](() => ${ genReadVal[Any](AnyRef(), in, false) }) + if parsedArray != null then parsedArray.toList + else null + case '{' => + $in.parseMap[String, Any]( + () => ${ genReadVal[String](StringRef(), in, false, true) }, + () => ${ genReadVal[Any](AnyRef(), in, false) }, + Map.empty[String, Any], + true + ) + case 't' | 'f' => + $in.backspace() + $in.expectBoolean() + case n if n == '-' | n == '+' | n == '.' | (n >= '0' && n <= '9') => + $in.backspace() + $in.expectNumberOrNull() match + case null => null + case s => + scala.math.BigDecimal(s) match { + case i if i.isValidInt => i.toIntExact + case i if i.isValidLong => i.toLongExact + case d if d.isDecimalDouble => d.toDouble + case d if d.ulp == 1 => d.toBigInt + case d => d + } + case '\"' => + $in.backspace() + $in.expectString() + case _ => throw new JsonParseError("Illegal JSON char while parsing Any value", $in) + }.asExprOf[T] + ) + + case t: Sealable if t.isSealed && !t.childrenAreObject => t.sealedChildren.map(kid => kid.refType match case '[c] => @@ -975,7 +998,7 @@ object JsonCodecMaker: }.asExprOf[T] ) - case t: ScalaClassRef[?] if t.isSealed && t.isAbstractClass && t.childrenAreObject => // case objects + case t: Sealable if t.isSealed && t.childrenAreObject => // case objects makeReadFn[T](MethodKey(t, false), in)(in => val classPrefixE = Expr(allButLastPart(t.name)) val caseDefs = t.sealedChildren.map { childRef => @@ -994,10 +1017,11 @@ object JsonCodecMaker: }.asExprOf[T] ) + case t: TraitRef[?] => + throw JsonUnsupportedType("Non-sealed traits are not supported") + case t: ScalaClassRef[?] => makeReadFn[T](MethodKey(t, false), in)(in => - val fieldNames = Expr(t.fields.map(_.name).toArray) - // Generate vars for each contractor argument, populated with either a "unit" value (eg 0, "") or given default value val tpe = TypeRepr.of[b] val classCompanion = tpe.typeSymbol.companionClass @@ -1031,6 +1055,8 @@ object JsonCodecMaker: val unitVal = oneField.fieldRef match { case _: OptionRef[?] => oneField.fieldRef.unitVal.asTerm // not required + case _: AnyRef => + oneField.fieldRef.unitVal.asTerm // not required case r: LeftRightRef[?] if r.lrkind == LRKind.EITHER => // maybe required val (optionRecipe, lang) = lrHasOptionChild(r) if optionRecipe.length == 0 then @@ -1152,6 +1178,48 @@ object JsonCodecMaker: Block(finalVarDefs :+ reqVarDef, parseLoop).asExprOf[T] ) + case t: JavaClassRef[?] => + makeReadFn[T](MethodKey(t, false), in)(in => + val classNameE = Expr(t.name) + val tpe = TypeRepr.of[b] + val instanceSym = Symbol.newVal(Symbol.spliceOwner, "_instance", TypeRepr.of[T], Flags.Mutable, Symbol.noSymbol) + val instanceSymRef = Ident(instanceSym.termRef) + val nullConst = tpe.classSymbol.get.companionModule.declaredMethods + .find(m => m.paramSymss == List(Nil)) + .getOrElse( + throw JsonTypeError("ScalaJack only supports Java classes that have a zero-argument constructor") + ) + makeClassFieldMatrixValDef(MethodKey(t, true), t.name.replaceAll("\\.", "_"), t.fields.sortBy(_.index).map(f => changeFieldName(f)).toArray) + val fieldMatrixSym = classFieldMatrixSyms(MethodKey(t, true)).asInstanceOf[Symbol] + val caseDefs = t.fields.map(ncf => + ncf.fieldRef.refType match + case '[u] => + CaseDef( + Literal(IntConstant(ncf.index)), + None, + // Call the setter for this field here... + Apply( + Select.unique(Ref(instanceSym), ncf.asInstanceOf[NonConstructorFieldInfoRef].setterLabel), + List(genReadVal[u](ncf.fieldRef.asInstanceOf[RTypeRef[u]], in).asTerm) + ).asExpr.asTerm + ) + ) :+ CaseDef(Wildcard(), None, '{ $in.skipValue() }.asTerm) // skip values of unrecognized fields + + val parseLoop = + '{ + var maybeFieldNum = $in.expectFirstObjectField(${ Ref(fieldMatrixSym).asExprOf[StringMatrix] }) + if maybeFieldNum == null then null.asInstanceOf[T] + else + ${ Assign(instanceSymRef, '{ Class.forName($classNameE).getDeclaredConstructor().newInstance().asInstanceOf[T] }.asTerm).asExprOf[Any] } // _instance = (new instance) + // ${ Assign(instanceSymRef, Apply(Select(New(Inferred(tpe)), nullConst), Nil).asExpr.asTerm).asExprOf[Unit] } // _instance = (new instance) + while maybeFieldNum.isDefined do + ${ Match('{ maybeFieldNum.get }.asTerm, caseDefs).asExprOf[Any] } + maybeFieldNum = $in.expectObjectField(${ Ref(fieldMatrixSym).asExprOf[StringMatrix] }) + ${ Ref(instanceSym).asExprOf[T] } + }.asTerm + Block(List(ValDef(instanceSym, Some('{ null }.asTerm))), parseLoop).asExprOf[T] + ) + case t => throw new ParseError("Not yet implemented: " + t) // --------------------------------------------------------------------------------------------- @@ -1405,9 +1473,6 @@ object JsonCodecMaker: case t: URIRef => '{ $in.expectString((s: String) => new java.net.URI(s)) }.asExprOf[T] case t: UUIDRef => '{ $in.expectString(java.util.UUID.fromString) }.asExprOf[T] - // ZZZ: TODO - // case t: ObjectRef => '{ null }.asExprOf[T] - case t: AliasRef[?] => // Special check for RawJson pseudo-type if lastPart(t.definedType) == "RawJson" then @@ -1522,9 +1587,19 @@ syntheticTA.write(t.asInstanceOf[L], writer, out) case null => null case s: String => ${ - val typeRepr = TypeRepr.of[e] - val m = typeRepr.typeSymbol.companionClass.declaredMethod("valueOf") - Apply(Ref(m(0)), List('{ s }.asTerm)).asExpr + cfg.enumsAsIds match + case List("-") => + val typeRepr = TypeRepr.of[e] + val m = typeRepr.typeSymbol.companionClass.declaredMethod("valueOf") + Apply(Ref(m(0)), List('{ s }.asTerm)).asExpr + case c if c == Nil || c.contains(t.name) => + val typeRepr = TypeRepr.of[e] + val m = typeRepr.typeSymbol.companionClass.declaredMethod("fromOrdinal") + Apply(Ref(m(0)), List('{ s.toInt }.asTerm)).asExpr + case _ => + val typeRepr = TypeRepr.of[e] + val m = typeRepr.typeSymbol.companionClass.declaredMethod("valueOf") + Apply(Ref(m(0)), List('{ s }.asTerm)).asExpr } case v: Int => ${ @@ -1542,9 +1617,19 @@ syntheticTA.write(t.asInstanceOf[L], writer, out) case null => null case s: String => ${ - val enumeration = TypeRepr.of[e] match - case TypeRef(ct, _) => Ref(ct.termSymbol).asExprOf[Enumeration] - '{ ${ enumeration }.values.iterator.find(_.toString == s).get }.asExprOf[e] + cfg.enumsAsIds match + case List("-") => + val enumeration = TypeRepr.of[e] match + case TypeRef(ct, _) => Ref(ct.termSymbol).asExprOf[Enumeration] + '{ ${ enumeration }.values.iterator.find(_.toString == s).get }.asExprOf[e] + case c if c == Nil || c.contains(t.name) => + val enumeration = TypeRepr.of[e] match + case TypeRef(ct, _) => Ref(ct.termSymbol).asExprOf[Enumeration] + '{ ${ enumeration }.values.iterator.find(_.id == s.toInt).get }.asExprOf[e] + case _ => + val enumeration = TypeRepr.of[e] match + case TypeRef(ct, _) => Ref(ct.termSymbol).asExprOf[Enumeration] + '{ ${ enumeration }.values.iterator.find(_.toString == s).get }.asExprOf[e] } case v: Int => ${ @@ -1557,17 +1642,30 @@ syntheticTA.write(t.asInstanceOf[L], writer, out) import quotes.reflect.* t.refType match case '[e] => + val valuesE = Expr(t.values) '{ $in.expectEnum() match case null => null case s: String => + scala.util.Try(s.toInt) match + case Success(v) => + ${ + val typeRepr = TypeRepr.of[e] + val m = typeRepr.classSymbol.get.companionModule.methodMember("valueOf") + Apply(Ref(m(0)), List('{ $valuesE(v) }.asTerm)).asExpr + } + case _ => + ${ + val typeRepr = TypeRepr.of[e] + val m = typeRepr.classSymbol.get.companionModule.methodMember("valueOf") + Apply(Ref(m(0)), List('{ s }.asTerm)).asExpr + } + case v: Int => ${ val typeRepr = TypeRepr.of[e] val m = typeRepr.classSymbol.get.companionModule.methodMember("valueOf") - Apply(Ref(m(0)), List('{ s }.asTerm)).asExpr + Apply(Ref(m(0)), List('{ $valuesE(v) }.asTerm)).asExpr } - case v: Int => - throw JsonParseError("Ordinal value initiation not valid for Java Enums", $in) }.asExprOf[T] // -------------------- @@ -2058,11 +2156,28 @@ syntheticTA.write(t.asInstanceOf[L], writer, out) val key = MethodKey(ref, false) val sym = readMethodSyms(key) Ref(sym).appliedTo(in.asTerm).asExprOf[T] - // val tin = aE.asExprOf[b] - // '{ - // if $tin == null then $out.burpNull() - // else ${ Ref(sym).appliedTo(tin.asTerm, out.asTerm).asExprOf[Unit] } - // } + + // -------------------- + // NeoType... + // -------------------- + case t: NeoTypeRef[?] => // in Quotes context + val module = Symbol.requiredModule(t.typedName.toString) + val myMake = module.methodMember("make").head + val tm = Ref(module) + t.wrappedTypeRef.refType match + case '[e] => + val res = Apply( + Select.unique(tm, "make"), + List( + genReadVal[e](t.wrappedTypeRef.asInstanceOf[RTypeRef[e]], in, isMapKey = isMapKey).asTerm + ) + ).asExprOf[Either[String, T]] + val tnameE = Expr(t.name) + '{ + $res match + case Right(r) => r + case Left(m) => throw JsonParseError("NeoType validation for " + $tnameE + " failed", $in) + } case _ => // Classes, traits, etc. @@ -2093,9 +2208,6 @@ syntheticTA.write(t.asInstanceOf[L], writer, out) def decodeValue(in: JsonSource): T = ${ genReadVal(ref, 'in) } } }.asTerm - val neededDefs = - // others here??? Refer to Jsoniter file JsonCodecMaker.scala - classFieldMatrixValDefs ++ writeMethodDefs ++ readMethodDefs - val codec = Block(neededDefs.toList, codecDef).asExprOf[JsonCodec[T]] + val codec = Block((classFieldMatrixValDefs ++ writeMethodDefs ++ readMethodDefs).toList, codecDef).asExprOf[JsonCodec[T]] // println(s"Codec: ${codec.show}") codec diff --git a/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala b/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala index 90320892..2f88ea26 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala @@ -15,7 +15,7 @@ class JsonConfig private[scalajack] ( val typeHintLabel: String, val typeHintPolicy: TypeHintPolicy, // -------------------------- - val enumsAsIds: Option[List[String]], // None=no enums as ids, Some(Nil)=all enums as ids, Some(List(...))=specified classes enums as ids + val enumsAsIds: List[String], // Default: string values. Nil=all enums as ids, List(...)=specified classes enums as ids val _writeNonConstructorFields: Boolean, val _suppressEscapedStrings: Boolean, val _suppressTypeHints: Boolean @@ -25,18 +25,18 @@ class JsonConfig private[scalajack] ( def withEitherLeftHandling(eitherPolicy: EitherLeftPolicy): JsonConfig = copy(eitherLeftHandling = eitherPolicy) def withTypeHintLabel(label: String): JsonConfig = copy(typeHintLabel = label) def withTypeHintPolicy(hintPolicy: TypeHintPolicy): JsonConfig = copy(typeHintPolicy = hintPolicy) - def withEnumsAsIds(asIds: Option[List[String]]): JsonConfig = copy(enumsAsIds = asIds) + def withEnumsAsIds(asIds: List[String]): JsonConfig = copy(enumsAsIds = asIds) def writeNonConstructorFields(): JsonConfig = copy(_writeNonConstructorFields = true) def suppressEscapedStrings(): JsonConfig = copy(_suppressEscapedStrings = true) def suppressTypeHints(): JsonConfig = copy(_suppressTypeHints = true) - private[this] def copy( + private def copy( noneAsNull: Boolean = noneAsNull, tryFailureHandling: TryPolicy = tryFailureHandling, eitherLeftHandling: EitherLeftPolicy = eitherLeftHandling, typeHintLabel: String = typeHintLabel, typeHintPolicy: TypeHintPolicy = typeHintPolicy, - enumsAsIds: Option[List[String]] = enumsAsIds, + enumsAsIds: List[String] = enumsAsIds, _writeNonConstructorFields: Boolean = _writeNonConstructorFields, _suppressEscapedStrings: Boolean = _suppressEscapedStrings, _suppressTypeHints: Boolean = _suppressTypeHints @@ -71,7 +71,7 @@ object JsonConfig eitherLeftHandling = EitherLeftPolicy.AS_VALUE, typeHintLabel = "_hint", typeHintPolicy = TypeHintPolicy.SIMPLE_CLASSNAME, - enumsAsIds = None, + enumsAsIds = List("-"), // default -> enum as value _writeNonConstructorFields = false, _suppressEscapedStrings = false, _suppressTypeHints = false diff --git a/src/main/scala/co.blocke.scalajack/json/reading/Numbers.scala b/src/main/scala/co.blocke.scalajack/json/reading/Numbers.scala index 0dc05c95..23bfe631 100644 --- a/src/main/scala/co.blocke.scalajack/json/reading/Numbers.scala +++ b/src/main/scala/co.blocke.scalajack/json/reading/Numbers.scala @@ -284,20 +284,20 @@ object SafeNumbers { }.toString } - private[this] def rop(g1: Long, g0: Long, cp: Long): Long = { + private def rop(g1: Long, g0: Long, cp: Long): Long = { val x1 = multiplyHigh(g0, cp) // FIXME: Use Math.multiplyHigh after dropping JDK 8 support val z = (g1 * cp >>> 1) + x1 val y1 = multiplyHigh(g1, cp) // FIXME: Use Math.multiplyHigh after dropping JDK 8 support (z >>> 63) + y1 | -(z & 0x7fffffffffffffffL) >>> 63 } - private[this] def rop(g: Long, cp: Int): Int = { + private def rop(g: Long, cp: Int): Int = { val x1 = ((g & 0xffffffffL) * cp >>> 32) + (g >>> 32) * cp // FIXME: Use Math.multiplyHigh after dropping JDK 8 support (x1 >>> 31).toInt | -x1.toInt >>> 31 } - private[this] def multiplyHigh(x: Long, y: Long): Long = { + private def multiplyHigh(x: Long, y: Long): Long = { val x2 = x & 0xffffffffL val y2 = y & 0xffffffffL val b = x2 * y2 @@ -309,7 +309,7 @@ object SafeNumbers { // Adoption of a nice trick form Daniel Lemire's blog that works for numbers up to 10^18: // https://lemire.me/blog/2021/06/03/computing-the-number-of-digits-of-an-integer-even-faster/ - private[this] def digitCount(x: Long): Int = (offsets(java.lang.Long.numberOfLeadingZeros(x)) + x >> 58).toInt + private def digitCount(x: Long): Int = (offsets(java.lang.Long.numberOfLeadingZeros(x)) + x >> 58).toInt final private val offsets = Array( 5088146770730811392L, 5088146770730811392L, 5088146770730811392L, 5088146770730811392L, 5088146770730811392L, 5088146770730811392L, 5088146770730811392L, 5088146770730811392L, 4889916394579099648L, 4889916394579099648L, 4889916394579099648L, @@ -320,7 +320,7 @@ object SafeNumbers { 864691128455135132L, 864691128455135132L, 864691128455135132L, 576460752303423478L, 576460752303423478L, 576460752303423478L, 576460752303423478L, 576460752303423478L, 576460752303423478L, 576460752303423478L ) - private[this] val gs: Array[Long] = Array( + private val gs: Array[Long] = Array( 5696189077778435540L, 6557778377634271669L, 9113902524445496865L, 1269073367360058862L, 7291122019556397492L, 1015258693888047090L, 5832897615645117993L, 6346230177223303157L, 4666318092516094394L, 8766332956520552849L, 7466108948025751031L, 8492109508320019073L, 5972887158420600825L, 4949013199285060097L, 4778309726736480660L, 3959210559428048077L, 7645295562778369056L, 6334736895084876923L, 6116236450222695245L, 3223115108696946377L, 4892989160178156196L, 2578492086957557102L, 7828782656285049914L, 436238524390181040L, 6263026125028039931L, 2193665226883099993L, 5010420900022431944L, 9133629810990300641L, 8016673440035891111L, 9079784475471615541L, 6413338752028712889L, 5419153173006337271L, 5130671001622970311L, @@ -568,7 +568,7 @@ object UnsafeNumbers { } // measured faster than Character.isDigit - @inline private[this] def isDigit(i: Int): Boolean = + @inline private def isDigit(i: Int): Boolean = '0' <= i && i <= '9' // is it worth keeping this custom long__ instead of using bigInteger since it @@ -829,8 +829,8 @@ object UnsafeNumbers { else res } // note that bigDecimal does not have a negative zero - private[this] val bigIntegers: Array[java.math.BigInteger] = + private val bigIntegers: Array[java.math.BigInteger] = (0L to 9L).map(java.math.BigInteger.valueOf).toArray - private[this] val longunderflow: Long = Long.MinValue / 10L - private[this] val longoverflow: Long = Long.MaxValue / 10L + private val longunderflow: Long = Long.MinValue / 10L + private val longoverflow: Long = Long.MaxValue / 10L } diff --git a/src/main/scala/co.blocke.scalajack/json/schema/JsonSchema.scala b/src/main/scala/co.blocke.scalajack/json/schema/JsonSchema.scala index a087c2bc..6eb8aaa0 100644 --- a/src/main/scala/co.blocke.scalajack/json/schema/JsonSchema.scala +++ b/src/main/scala/co.blocke.scalajack/json/schema/JsonSchema.scala @@ -255,7 +255,7 @@ object JsonSchema: t.refType match case '[u] => val requiredFields = Expr(t.fields.collect { - case f: FieldInfoRef if !f.fieldRef.isInstanceOf[OptionRef[_]] => f.name + case f: FieldInfoRef if !f.fieldRef.isInstanceOf[OptionRef[?]] => f.name }) '{ ObjectSchema( diff --git a/src/main/scala/co.blocke.scalajack/json/writing/AnyWriter.scala b/src/main/scala/co.blocke.scalajack/json/writing/AnyWriter.scala index ecb3243a..66b8f35a 100644 --- a/src/main/scala/co.blocke.scalajack/json/writing/AnyWriter.scala +++ b/src/main/scala/co.blocke.scalajack/json/writing/AnyWriter.scala @@ -75,7 +75,7 @@ object AnyWriter: case v: Map[?, ?] => out.startObject() - v.map { case (k, v) => okToWrite(k.toString, v, out, cfg) } + v.map { case (k, v) => _okToWrite(k.toString, v, out, cfg) } out.endObject() case v: Option[?] => @@ -116,34 +116,34 @@ object AnyWriter: m.setAccessible(true) val fieldValue = m.invoke(v) val fieldName = f.annotations.get("co.blocke.scalajack.Change").flatMap(_.get("name")).getOrElse(f.name) - okToWrite(fieldName, fieldValue, out, cfg) + _okToWrite(fieldName, fieldValue, out, cfg) ) out.endObject() case _ => throw new JsonUnsupportedType("Class " + v.getClass.getName + " not supported for Any type") - // Called for Any-typed classes - def okToWrite(label: String, value: Any, out: JsonOutput, cfg: JsonConfig): Unit = - isOkToWrite(value, cfg).map { v => - out.label(label) - writeAny(v, out, cfg) - } - // Called by non-Any classes (in JsonCodecMaker) that have Any-typed fields - def okToWrite2(prefix: Expr[Unit], value: Expr[Any], out: Expr[JsonOutput], cfg: JsonConfig)(using Quotes): Expr[Unit] = + def isOkToWrite(prefix: Expr[Unit], value: Expr[Any], out: Expr[JsonOutput], cfg: JsonConfig)(using Quotes): Expr[Unit] = import quotes.reflect.* '{ - isOkToWrite($value, ${ Expr(cfg) }).map { v => + _okToWrite($value, ${ Expr(cfg) }).map { v => $prefix AnyWriter.writeAny(v, $out, ${ Expr(cfg) }) } } - def isOkToWrite(value: Any, cfg: JsonConfig): Option[Any] = + // Called for Any-typed classes + private def _okToWrite(label: String, value: Any, out: JsonOutput, cfg: JsonConfig): Unit = + _okToWrite(value, cfg).map { v => + out.label(label) + writeAny(v, out, cfg) + } + + private def _okToWrite(value: Any, cfg: JsonConfig): Option[Any] = value match - case None => if cfg.noneAsNull then Some("null") else None + case None => if cfg.noneAsNull then Some(null) else None case Failure(e) => cfg.tryFailureHandling match - case TryPolicy.AS_NULL => Some("null") + case TryPolicy.AS_NULL => Some(null) case TryPolicy.ERR_MSG_STRING => Some("Try Failure with msg: " + e.getMessage()) case TryPolicy.THROW_EXCEPTION => throw e @@ -153,5 +153,5 @@ object AnyWriter: case EitherLeftPolicy.AS_NULL => Some("null") case EitherLeftPolicy.ERR_MSG_STRING => Some("Left Error: " + v.toString) case EitherLeftPolicy.THROW_EXCEPTION => throw new JsonEitherLeftError("Left Error: " + v.toString) - case Some(v) => isOkToWrite(v, cfg) + case Some(v) => _okToWrite(v, cfg) case _ => Some(value) diff --git a/src/main/scala/co.blocke.scalajack/json/writing/WriteCodec.scalax b/src/main/scala/co.blocke.scalajack/json/writing/WriteCodec.scalax new file mode 100644 index 00000000..3c92bbe7 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/json/writing/WriteCodec.scalax @@ -0,0 +1,766 @@ +package co.blocke.scalajack +package json +package writing + +import JsonCodecMaker.MethodKey +import co.blocke.scala_reflection.RTypeRef +import co.blocke.scala_reflection.reflect.rtypeRefs.* +import co.blocke.scala_reflection.reflect.ReflectOnType + +import scala.quoted.* + +case class WriteCodec[T](ref: RTypeRef[T], cfg: JsonConfig)(using q: Quotes)(using tt: Type[T]): + import q.reflect.* + + private val writeMethodSyms = new scala.collection.mutable.HashMap[MethodKey, Symbol] + val writeMethodDefs = new scala.collection.mutable.ArrayBuffer[DefDef] + + // Fantastic magic here--lifted from Jasoniter. Props! This thing will create a DefDef, and a Symbol to it. + // The Symbol will let you call the generated function later from other macro-generated code. The goal is to use + // generated functions to create cleaner/faster macro code than what straight quotes/splices would create unaided. + def makeWriteFn[U: Type](methodKey: MethodKey, arg: Expr[U], out: Expr[JsonOutput])(f: (Expr[U], Expr[JsonOutput]) => Expr[Unit]): Expr[Unit] = + // Get a symbol, if one already created for this key... else make one. + Apply( + Ref( + writeMethodSyms.getOrElse( + methodKey, { + val sym = Symbol.newMethod( + Symbol.spliceOwner, + "w" + writeMethodSyms.size, // 'w' is for Writer! + MethodType(List("in", "out"))(_ => List(TypeRepr.of[U], TypeRepr.of[JsonOutput]), _ => TypeRepr.of[Unit]) + ) + writeMethodSyms.update(methodKey, sym) + writeMethodDefs += DefDef( + sym, + params => { + val List(List(in, out)) = params + Some(f(in.asExprOf[U], out.asExprOf[JsonOutput]).asTerm.changeOwner(sym)) + } + ) + sym + } + ) + ), + List(arg.asTerm, out.asTerm) + ).asExprOf[Unit] + + def maybeWrite[T](label: String, aE: Expr[T], ref: RTypeRef[T], out: Expr[JsonOutput], cfg: JsonConfig): Expr[Unit] = + val labelE = Expr(label) + _maybeWrite[T]( + '{ $out.label($labelE) }, + aE, + ref, + out, + cfg + ) + + def maybeWriteMap[K, V](keyE: Expr[K], valueE: Expr[V], keyRef: RTypeRef[K], valueRef: RTypeRef[V], out: Expr[JsonOutput], cfg: JsonConfig): Expr[Unit] = + keyRef.refType match + case '[k] => + _maybeWrite[V]( + '{ + $out.maybeComma() + ${ genWriteVal(keyE.asExprOf[k], keyRef.asInstanceOf[RTypeRef[k]], out, false, true) } + $out.colon() + }, + valueE, + valueRef, + out, + cfg + ) + + // Tests whether we should write something or not--mainly in the case of Option, or wrapped Option + // Affected types: Option, java.util.Optional, Left/Right, Try/Failure + // Returns Expr[Unit] containing either the original phrase (if ok to write) or the phrase + // prepended with the type-appropriate runtime check. This may seem like drama, but the idea + // is to avoid slowing runtime down with extra "if" checks unless they're absolutely needed. + def _maybeWrite[T](prefix: Expr[Unit], aE: Expr[T], ref: RTypeRef[T], out: Expr[JsonOutput], cfg: JsonConfig): Expr[Unit] = + ref match + case t: ScalaOptionRef[?] if !cfg.noneAsNull => + t.optionParamType.refType match + case '[e] => + val tin = aE.asExprOf[Option[e]] + '{ + $tin match + case None => () + case Some(v) => ${ _maybeWrite[e](prefix, '{ v }.asExprOf[e], t.optionParamType.asInstanceOf[RTypeRef[e]], out, cfg) } + } + case t: JavaOptionalRef[?] if !cfg.noneAsNull => + t.optionParamType.refType match + case '[e] => + val tin = aE.asExprOf[java.util.Optional[e]] + '{ + if ! $tin.isEmpty then ${ _maybeWrite[e](prefix, '{ $tin.get }.asExprOf[e], t.optionParamType.asInstanceOf[RTypeRef[e]], out, cfg) } + } + case t: TryRef[?] => + t.tryRef.refType match + case '[e] => + val tin = aE.asExprOf[scala.util.Try[e]] + '{ + $tin match + case scala.util.Failure(v) => + ${ + cfg.tryFailureHandling match + case TryPolicy.AS_NULL => + '{ + $prefix + $out.burpNull() + } + case TryPolicy.ERR_MSG_STRING => + '{ + $prefix + $out.value("Try Failure with msg: " + v.getMessage()) + } + case TryPolicy.THROW_EXCEPTION => '{ throw v } + } + case _ => ${ _maybeWrite[e](prefix, '{ $tin.get }.asExprOf[e], t.tryRef.asInstanceOf[RTypeRef[e]], out, cfg) } + } + case t: LeftRightRef[?] if t.lrkind == LRKind.EITHER => + t.refType match + case '[u] => + val tin = aE.asExprOf[u] + t.rightRef.refType match + case '[rt] => + cfg.eitherLeftHandling match + case EitherLeftPolicy.AS_NULL => + '{ + if $tin == null then $out.burpNull() + $tin match + case Left(_) => + $prefix + $out.burpNull() + case Right(v) => ${ _maybeWrite[rt](prefix, '{ v.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out, cfg) } + } + case EitherLeftPolicy.ERR_MSG_STRING => + '{ + if $tin == null then $out.burpNull() + $tin match + case Left(err) => + $prefix + $out.value("Left Error: " + err.toString) + case Right(v) => ${ _maybeWrite[rt](prefix, '{ v.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out, cfg) } + } + case EitherLeftPolicy.THROW_EXCEPTION => + '{ + if $tin == null then $out.burpNull() + $tin match + case Left(err) => throw new JsonEitherLeftError("Left Error: " + err.toString) + case Right(v) => ${ _maybeWrite[rt](prefix, '{ v.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out, cfg) } + } + case EitherLeftPolicy.AS_VALUE => + t.leftRef.refType match + case '[lt] => + '{ + if $tin == null then $out.burpNull() + $tin match + case Left(v) => + ${ _maybeWrite[lt](prefix, '{ v.asInstanceOf[lt] }, t.leftRef.asInstanceOf[RTypeRef[lt]], out, cfg) } + case Right(v) => + ${ _maybeWrite[rt](prefix, '{ v.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out, cfg) } + } + case t: LeftRightRef[?] if t.lrkind != LRKind.EITHER => + t.refType match + case '[e] => + t.rightRef.refType match + case '[rt] => + t.leftRef.refType match + case '[lt] => + val tin = aE.asExprOf[e] + '{ + if $tin == null then $out.burpNull() + else + $out.mark() + scala.util.Try { + ${ _maybeWrite[rt](prefix, '{ $tin.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out, cfg) } + } match + case scala.util.Success(_) => () + case scala.util.Failure(f) => + $out.revert() + ${ _maybeWrite[lt](prefix, '{ $tin.asInstanceOf[lt] }, t.leftRef.asInstanceOf[RTypeRef[lt]], out, cfg) } + } + case t: AnyRef => + AnyWriter.isOkToWrite(prefix, aE, out, cfg) + case _ => + ref.refType match + case '[u] => + '{ + $prefix + ${ genWriteVal[u](aE.asExprOf[u], ref.asInstanceOf[RTypeRef[u]], out) } + } + + + def genEncFnBody[T](r: RTypeRef[?], aE: Expr[T], out: Expr[JsonOutput], emitDiscriminator: Boolean = false, inTuple: Boolean = false, isMapKey: Boolean = false)(using Quotes): Expr[Unit] = + r.refType match + case '[b] => + r match + case t: ArrayRef[?] => + makeWriteFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => + t.elementRef.refType match + case '[e] => + val tin = in.asInstanceOf[Expr[Array[e]]] + '{ + if $tin == null then $out.burpNull() + else + $out.startArray() + $tin.foreach { i => + ${ genWriteVal('{ i }, t.elementRef.asInstanceOf[RTypeRef[e]], out) } + } + $out.endArray() + } + } + + case t: SeqRef[?] => + makeWriteFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => + t.elementRef.refType match + case '[e] => + val tin = if t.isMutable then in.asExprOf[scala.collection.mutable.Seq[e]] else in.asExprOf[Seq[e]] + '{ + if $tin == null then $out.burpNull() + else + $out.startArray() + $tin.foreach { i => + ${ genWriteVal('{ i }, t.elementRef.asInstanceOf[RTypeRef[e]], out) } + } + $out.endArray() + } + } + + case t: SetRef[?] => + makeWriteFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => + t.elementRef.refType match + case '[e] => + val tin = if t.isMutable then in.asExprOf[scala.collection.mutable.Set[e]] else in.asExprOf[Set[e]] + '{ + if $tin == null then $out.burpNull() + else + $out.startArray() + $tin.foreach { i => + ${ genWriteVal('{ i }.asExprOf[e], t.elementRef.asInstanceOf[RTypeRef[e]], out) } + } + $out.endArray() + } + } + + // case t: ScalaClassRef[?] if t.isSealed => // basically just like sealed trait... + case t: Sealable if t.isSealed => // basically just like sealed trait... + if t.childrenAreObject then + val tin = aE.asExprOf[b] + // case object -> just write the simple name of the object + '{ + if $tin == null then $out.burpNull() + else $out.value($tin.getClass.getName.split('.').last.stripSuffix("$")) + } + else + val cases = t.sealedChildren.map { child => + child.refType match + case '[c] => + val subtype = TypeRepr.of[c] + val sym = Symbol.newBind(Symbol.spliceOwner, "t", Flags.EmptyFlags, subtype) + CaseDef(Bind(sym, Typed(Wildcard(), Inferred(subtype))), None, genEncFnBody[c](child, Ref(sym).asExprOf[c], out, true).asTerm) + } :+ CaseDef(Literal(NullConstant()), None, '{ $out.burpNull() }.asTerm) + val matchExpr = Match(aE.asTerm, cases).asExprOf[Unit] + matchExpr + + // We don't use makeWriteFn here because a value class is basically just a "box" around a simple type + case t: ScalaClassRef[?] if t.isValueClass => + val theField = t.fields.head.fieldRef + theField.refType match + case '[e] => + val fieldValue = Select.unique(aE.asTerm, t.fields.head.name).asExprOf[e] + genWriteVal(fieldValue, theField.asInstanceOf[RTypeRef[e]], out, isMapKey = isMapKey) + + case t: ScalaClassRef[?] => + makeWriteFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => + val body = { + val eachField = t.fields.map { f => + f.fieldRef.refType match + case '[z] => + val fieldValue = Select.unique(in.asTerm, f.name).asExprOf[z] + val fieldName = changeFieldName(f) + maybeWrite[z](fieldName, fieldValue, f.fieldRef.asInstanceOf[RTypeRef[z]], out, cfg) + } + if emitDiscriminator then + val cname = cfg.typeHintPolicy match + case TypeHintPolicy.SIMPLE_CLASSNAME => Expr(lastPart(t.name)) + case TypeHintPolicy.SCRAMBLE_CLASSNAME => '{ scramble(${ Expr(lastPart(t.name).hashCode) }) } + case TypeHintPolicy.USE_ANNOTATION => + Expr(t.annotations.get("co.blocke.scalajack.TypeHint").flatMap(_.get("hintValue")).getOrElse(lastPart(t.name))) + val withDisc = '{ + $out.label(${ Expr(cfg.typeHintLabel) }) + $out.value($cname) + } +: eachField + Expr.block(withDisc.init, withDisc.last) + else if eachField.length == 1 then eachField.head + else Expr.block(eachField.init, eachField.last) + } + + if !t.isCaseClass && cfg._writeNonConstructorFields then + val eachField = t.nonConstructorFields.map { f => + f.fieldRef.refType match + case '[e] => + val fieldValue = Select.unique(in.asTerm, f.getterLabel).asExprOf[e] + val fieldName = changeFieldName(f) + maybeWrite[e](fieldName, fieldValue, f.fieldRef.asInstanceOf[RTypeRef[e]], out, cfg) + } + val subBody = eachField.length match + case 0 => '{} + case 1 => eachField.head + case _ => Expr.block(eachField.init, eachField.last) + '{ + if $in == null then $out.burpNull() + else + $out.startObject() + $body + $subBody + $out.endObject() + } + else + '{ + if $in == null then $out.burpNull() + else + $out.startObject() + $body + $out.endObject() + } + } + + case t: MapRef[?] => + makeWriteFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => + t.elementRef.refType match + case '[k] => + t.elementRef2.refType match + case '[v] => + val tin = if t.isMutable then in.asExprOf[scala.collection.mutable.Map[k, v]] else in.asExprOf[Map[k, v]] + '{ + if $tin == null then $out.burpNull() + else + $out.startObject() + $tin.foreach { case (key, value) => + ${ + (t.elementRef, t.elementRef2) match + case (aliasK: AliasRef[?], aliasV: AliasRef[?]) => + aliasK.unwrappedType.refType match + case '[ak] => + aliasV.unwrappedType.refType match + case '[av] => + testValidMapKey(aliasK.unwrappedType) + maybeWriteMap[ak, av]( + '{ key.asInstanceOf[ak] }, + '{ value.asInstanceOf[av] }, + aliasK.unwrappedType.asInstanceOf[RTypeRef[ak]], + aliasV.unwrappedType.asInstanceOf[RTypeRef[av]], + out, + cfg + ) + case (_, aliasV: AliasRef[?]) => + aliasV.unwrappedType.refType match + case '[av] => + testValidMapKey(t.elementRef) + maybeWriteMap[k, av]( + '{ key }.asExprOf[k], + '{ value.asInstanceOf[av] }, + t.elementRef.asInstanceOf[RTypeRef[k]], + aliasV.unwrappedType.asInstanceOf[RTypeRef[av]], + out, + cfg + ) + case (aliasK: AliasRef[?], _) => + aliasK.unwrappedType.refType match + case '[ak] => + testValidMapKey(aliasK.unwrappedType) + maybeWriteMap[ak, v]( + '{ key.asInstanceOf[ak] }, + '{ value }.asExprOf[v], + aliasK.unwrappedType.asInstanceOf[RTypeRef[ak]], + t.elementRef2.asInstanceOf[RTypeRef[v]], + out, + cfg + ) + case (_, _) => + testValidMapKey(t.elementRef) + maybeWriteMap[k, v]('{ key }, '{ value }.asExprOf[v], t.elementRef.asInstanceOf[RTypeRef[k]], t.elementRef2.asInstanceOf[RTypeRef[v]], out, cfg) + } + } + $out.endObject() + } + } + + case t: JavaCollectionRef[?] => + makeWriteFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => + t.elementRef.refType match + case '[e] => + val tin = '{ $in.asInstanceOf[java.util.Collection[_]] } + '{ + if $tin == null then $out.burpNull() + else + $out.startArray() + $tin.toArray.foreach { elem => + ${ genWriteVal('{ elem.asInstanceOf[e] }, t.elementRef.asInstanceOf[RTypeRef[e]], out) } + } + $out.endArray() + } + } + + case t: JavaMapRef[?] => + makeWriteFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => + t.elementRef.refType match + case '[k] => + t.elementRef2.refType match + case '[v] => + val tin = in.asExprOf[java.util.Map[k, v]] + '{ + if $tin == null then $out.burpNull() + else + $out.startObject() + $tin.asScala.foreach { case (key, value) => + ${ + maybeWriteMap[k, v]('{ key }, '{ value }.asExprOf[v], t.elementRef.asInstanceOf[RTypeRef[k]], t.elementRef2.asInstanceOf[RTypeRef[v]], out, cfg) + } + } + $out.endObject() + } + } + + case t: JavaClassRef[?] => + makeWriteFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => + t.refType match + case '[p] => + val rtype = t.expr.asExprOf[JavaClassRType[p]] + val tin = in.asExprOf[b] + var fieldRefs = t.fields.asInstanceOf[List[NonConstructorFieldInfoRef]] + val sref = ReflectOnType[String](q)(TypeRepr.of[String])(using scala.collection.mutable.Map.empty[TypedName, Boolean]) + '{ + if $tin == null then $out.burpNull() + else + $out.startObject() + val rt = $rtype + rt.fields.foreach { f => + val field = f.asInstanceOf[NonConstructorFieldInfo] + val m = $tin.getClass.getMethod(field.getterLabel) + m.setAccessible(true) + val fieldValue = m.invoke($tin) + val fieldName = field.annotations.get("co.blocke.scalajack.Change").flatMap(_.get("name")).getOrElse(f.name) + ${ + val ref = fieldRefs.head + fieldRefs = fieldRefs.tail + ref.fieldRef.refType match + case '[e] => + maybeWriteMap[String, e]('{ fieldName }, '{ fieldValue.asInstanceOf[e] }, sref, ref.fieldRef.asInstanceOf[RTypeRef[e]], out, cfg) + } + } + $out.endObject() + } + } + + case t: TraitRef[?] => throw JsonUnsupportedType("Non-sealed traits are not supported") + + // No makeWriteFn here--Option is just a wrapper to the real thingy + case t: ScalaOptionRef[?] => + t.optionParamType.refType match + case '[e] => + val tin = aE.asExprOf[b] + '{ + $tin match + case null => $out.burpNull() + case None => + ${ + if cfg.noneAsNull || inTuple then '{ $out.burpNull() } + else '{ () } + } + case Some(v) => + val vv = v.asInstanceOf[e] + ${ genWriteVal[e]('{ vv }, t.optionParamType.asInstanceOf[RTypeRef[e]], out) } + } + case t: JavaOptionalRef[?] => + t.optionParamType.refType match + case '[e] => + val tin = aE.asExprOf[b] + '{ + $tin.asInstanceOf[java.util.Optional[e]] match + case null => $out.burpNull() + case o if o.isEmpty => + ${ + if cfg.noneAsNull || inTuple then '{ $out.burpNull() } + else '{ () } + } + case o => + val vv = o.get().asInstanceOf[e] + ${ genWriteVal[e]('{ vv }, t.optionParamType.asInstanceOf[RTypeRef[e]], out) } + } + + // No makeWriteFn here... SelfRef is referring to something we've already seen before. There absolutely should already be a geneated + // and cached function for this thing that we can call. + case t: SelfRefRef[?] => + t.refType match + case '[e] => + val ref = ReflectOnType[e](q)(TypeRepr.of[e])(using scala.collection.mutable.Map.empty[TypedName, Boolean]) + val key = MethodKey(ref, false) + val sym = writeMethodSyms(key) + val tin = aE.asExprOf[b] + '{ + if $tin == null then $out.burpNull() + else ${ Ref(sym).appliedTo(tin.asTerm, out.asTerm).asExprOf[Unit] } + } + + // No makeWriteFn here. All LeftRight types (Either, Union, Intersection) are just type wrappers + case t: LeftRightRef[?] => + val tin = aE.asExprOf[b] + t.leftRef.refType match + case '[lt] => + t.rightRef.refType match + case '[rt] => + // This is a close parallel with maybeWrite handling of Either. If the Either is a field in a class or + // Map, the maybeWrite logic applies--because we need to not write both the Either value AND the field label. + // If the Either is part of a tuple, Seq, etc., then this logic applies. + if t.lrkind == LRKind.EITHER then + cfg.eitherLeftHandling match + case EitherLeftPolicy.AS_VALUE => + '{ + if $tin == null then $out.burpNull() + else + $tin match + case Left(v) => + ${ genWriteVal[lt]('{ v.asInstanceOf[lt] }, t.leftRef.asInstanceOf[RTypeRef[lt]], out, inTuple = inTuple) } + case Right(v) => + ${ genWriteVal[rt]('{ v.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out, inTuple = inTuple) } + } + case EitherLeftPolicy.AS_NULL => + '{ + if $tin == null then $out.burpNull() + else + $tin match + case Left(v) => $out.burpNull() + case Right(v) => + ${ genWriteVal[rt]('{ v.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out, inTuple = inTuple) } + } + case EitherLeftPolicy.ERR_MSG_STRING => + '{ + if $tin == null then $out.burpNull() + else + $tin match + case Left(v) => $out.value("Left Error: " + v.toString) + case Right(v) => + ${ genWriteVal[rt]('{ v.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out, inTuple = inTuple) } + } + case EitherLeftPolicy.THROW_EXCEPTION => + '{ + if $tin == null then $out.burpNull() + else + $tin match + case Left(v) => throw new JsonEitherLeftError("Left Error: " + v.toString) + case Right(v) => + ${ genWriteVal[rt]('{ v.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out, inTuple = inTuple) } + } + else + '{ + $out.mark() + scala.util.Try { + ${ genWriteVal[rt]('{ $tin.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out, inTuple = inTuple) } + } match + case scala.util.Success(_) => () // do nothing further--write to out already happened + case scala.util.Failure(_) => + $out.revert() + ${ genWriteVal[lt]('{ $tin.asInstanceOf[lt] }, t.leftRef.asInstanceOf[RTypeRef[lt]], out, inTuple = inTuple) } + } + + // No makeWriteFn here. Try is just a wrapper + case t: TryRef[?] => + t.tryRef.refType match + case '[e] => + val tin = aE.asExprOf[scala.util.Try[e]] + '{ + if $tin == null then $out.burpNull() + else + $tin match + case scala.util.Success(v) => + ${ genWriteVal[e]('{ v }.asInstanceOf[Expr[e]], t.tryRef.asInstanceOf[RTypeRef[e]], out, inTuple = inTuple) } + case scala.util.Failure(v) => + ${ + cfg.tryFailureHandling match + case _ if inTuple => '{ $out.burpNull() } + case TryPolicy.AS_NULL => '{ $out.burpNull() } + case TryPolicy.ERR_MSG_STRING => '{ $out.value("Try Failure with msg: " + v.getMessage()) } + case TryPolicy.THROW_EXCEPTION => '{ throw v } + } + } + + case t: TupleRef[?] => + makeWriteFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => + '{ + if $in == null then $out.burpNull() + else + $out.startArray() + ${ + // Note: Don't use maybeWrite here... Tuples are fixed-length. We need to write + // something for every position, so write null for None or other "bad" values + val elementsE = t.tupleRefs.zipWithIndex.map { case (ref, i) => + ref.refType match + case '[e] => + val fieldValue = Select.unique(in.asTerm, "_" + (i + 1)).asExprOf[e] + genWriteVal[e](fieldValue, ref.asInstanceOf[RTypeRef[e]], out, inTuple = true) + } + if elementsE.size == 1 then elementsE.head + else Expr.block(elementsE.init, elementsE.last) + } + $out.endArray() + } + } + + case t => throw new JsonUnsupportedType("Type represented by " + t.name + " is unsupported for JSON writes") + + // --------------------------------------------------------------------------------------------- + + def genWriteVal[T: Type]( + aE: Expr[T], + ref: RTypeRef[T], + out: Expr[JsonOutput], + inTuple: Boolean = false, + isMapKey: Boolean = false // only primitive or primitive-equiv types can be Map keys + )(using Quotes): Expr[Unit] = + val methodKey = MethodKey(ref, false) + writeMethodSyms + .get(methodKey) + .map { sym => // hit cache first... then match on Ref type + Apply(Ref(sym), List(aE.asTerm, out.asTerm)).asExprOf[Unit] + } + .getOrElse( + ref match + // First cover all primitive and simple types... + case t: BigDecimalRef => + if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[scala.math.BigDecimal] }) } + else '{ $out.value(${ aE.asExprOf[scala.math.BigDecimal] }) } + case t: BigIntRef => + if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[scala.math.BigInt] }) } + else '{ $out.value(${ aE.asExprOf[scala.math.BigInt] }) } + case t: BooleanRef => + if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[Boolean] }) } + else '{ $out.value(${ aE.asExprOf[Boolean] }) } + case t: ByteRef => + if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[Byte] }) } + else '{ $out.value(${ aE.asExprOf[Byte] }) } + case t: CharRef => + '{ $out.value(${ aE.asExprOf[Char] }) } + case t: DoubleRef => + if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[Double] }) } + else '{ $out.value(${ aE.asExprOf[Double] }) } + case t: FloatRef => + if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[Float] }) } + else '{ $out.value(${ aE.asExprOf[Float] }) } + case t: IntRef => + if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[Int] }) } + else '{ $out.value(${ aE.asExprOf[Int] }) } + case t: LongRef => + if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[Long] }) } + else '{ $out.value(${ aE.asExprOf[Long] }) } + case t: ShortRef => + if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[Short] }) } + else '{ $out.value(${ aE.asExprOf[Short] }) } + case t: StringRef => '{ $out.valueEscaped(${ aE.asExprOf[String] }) } + + case t: JBigDecimalRef => + if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[java.math.BigDecimal] }) } + else '{ $out.value(${ aE.asExprOf[java.math.BigDecimal] }) } + case t: JBigIntegerRef => + if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[java.math.BigInteger] }) } + else '{ $out.value(${ aE.asExprOf[java.math.BigInteger] }) } + case t: JBooleanRef => + if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[java.lang.Boolean] }) } + else '{ $out.value(${ aE.asExprOf[java.lang.Boolean] }) } + case t: JByteRef => + if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[java.lang.Byte] }) } + else '{ $out.value(${ aE.asExprOf[java.lang.Byte] }) } + case t: JCharacterRef => + '{ $out.value(${ aE.asExprOf[java.lang.Character] }) } + case t: JDoubleRef => + if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[java.lang.Double] }) } + else '{ $out.value(${ aE.asExprOf[java.lang.Double] }) } + case t: JFloatRef => + if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[java.lang.Float] }) } + else '{ $out.value(${ aE.asExprOf[java.lang.Float] }) } + case t: JIntegerRef => + if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[java.lang.Integer] }) } + else '{ $out.value(${ aE.asExprOf[java.lang.Integer] }) } + case t: JLongRef => + if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[java.lang.Long] }) } + else '{ $out.value(${ aE.asExprOf[java.lang.Long] }) } + case t: JShortRef => + if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[java.lang.Short] }) } + else '{ $out.value(${ aE.asExprOf[java.lang.Short] }) } + case t: JNumberRef => + if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[java.lang.Number] }) } + else '{ $out.value(${ aE.asExprOf[java.lang.Number] }) } + + case t: DurationRef => '{ $out.value(${ aE.asExprOf[java.time.Duration] }) } + case t: InstantRef => '{ $out.value(${ aE.asExprOf[java.time.Instant] }) } + case t: LocalDateRef => '{ $out.value(${ aE.asExprOf[java.time.LocalDate] }) } + case t: LocalDateTimeRef => '{ $out.value(${ aE.asExprOf[java.time.LocalDateTime] }) } + case t: LocalTimeRef => '{ $out.value(${ aE.asExprOf[java.time.LocalTime] }) } + case t: MonthDayRef => '{ $out.value(${ aE.asExprOf[java.time.MonthDay] }) } + case t: OffsetDateTimeRef => '{ $out.value(${ aE.asExprOf[java.time.OffsetDateTime] }) } + case t: OffsetTimeRef => '{ $out.value(${ aE.asExprOf[java.time.OffsetTime] }) } + case t: PeriodRef => '{ $out.value(${ aE.asExprOf[java.time.Period] }) } + case t: YearRef => '{ $out.value(${ aE.asExprOf[java.time.Year] }) } + case t: YearMonthRef => '{ $out.value(${ aE.asExprOf[java.time.YearMonth] }) } + case t: ZonedDateTimeRef => '{ $out.value(${ aE.asExprOf[java.time.ZonedDateTime] }) } + case t: ZoneIdRef => '{ $out.value(${ aE.asExprOf[java.time.ZoneId] }) } + case t: ZoneOffsetRef => '{ $out.value(${ aE.asExprOf[java.time.ZoneOffset] }) } + + case t: URLRef => '{ $out.value(${ aE.asExprOf[java.net.URL] }) } + case t: URIRef => '{ $out.value(${ aE.asExprOf[java.net.URI] }) } + case t: UUIDRef => '{ $out.value(${ aE.asExprOf[java.util.UUID] }) } + case t: ObjectRef[?] => '{ $out.value(${ Expr(t.name) }) } + + case t: AliasRef[?] => + // Special check for RawJson pseudo-type + if lastPart(t.definedType) == "RawJson" then '{ $out.valueRaw(${ aE.asExprOf[RawJson] }) } + else + t.unwrappedType.refType match + case '[e] => + genWriteVal[e](aE.asInstanceOf[Expr[e]], t.unwrappedType.asInstanceOf[RTypeRef[e]], out, inTuple = inTuple) + + // These one's here becaue Enums and their various flavors can be Map keys + // (EnumRef handles: Scala 3 enum, Scala 2 Enumeration, Java Enumeration) + case t: EnumRef[?] => + val enumAsId = cfg.enumsAsIds match + case None => false + case Some(Nil) => true + case Some(list) if list.contains(t.name) => true + case _ => false + val rtype = t.expr + if enumAsId then + if isMapKey then '{ $out.value($rtype.asInstanceOf[EnumRType[_]].ordinal($aE.toString).get.toString) } // stringified id + else '{ $out.value($rtype.asInstanceOf[EnumRType[_]].ordinal($aE.toString).get) } // int value of id + else '{ $out.value($aE.toString) } + + // NeoType is a bit of a puzzle-box. To get the correct underlying base type, I had to dig into + // the argument of method validate$retainedBody. It happened to have the correctly-typed parameter. + // With the correct type, we can correct write out the value. + case t: NeoTypeRef[?] => // in Quotes context + Symbol.requiredModule(t.typedName.toString).methodMember("validate$retainedBody").head.paramSymss.head.head.tree match + case ValDef(_, tt, _) => + tt.tpe.asType match + case '[u] => + val baseTypeRef = ReflectOnType.apply(q)(tt.tpe)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) + genWriteVal[u]('{ $aE.asInstanceOf[u] }, baseTypeRef.asInstanceOf[RTypeRef[u]], out) + + /* This is how you call make(), which includes validate() + val myMake = module.methodMember("make").head + val tm = Ref(module) + val z = Apply( + Select.unique(tm, "make"), + List( + Expr(List(1, 2, 3)).asTerm + ) + ).asExprOf[Either[String, _]] + '{ + println("Hello...") + println($z) + } + */ + + case t: AnyRef => '{ AnyWriter.writeAny($aE, $out, ${ Expr(cfg) }) } + + // Everything else... + // case _ if isStringified => throw new JsonIllegalKeyType("Non-primitive/non-simple types cannot be map keys") + case _ => genEncFnBody(ref, aE, out, inTuple = inTuple, isMapKey = isMapKey) + ) diff --git a/src/main/scala/co.blocke.scalajack/run/Record.scala b/src/main/scala/co.blocke.scalajack/run/Record.scala index 8cdeda22..350f92b2 100644 --- a/src/main/scala/co.blocke.scalajack/run/Record.scala +++ b/src/main/scala/co.blocke.scalajack/run/Record.scala @@ -67,12 +67,12 @@ case class Fish(name: String, @Change(name = "fresh") isFreshwater: Option[Boole type NonEmptyString = NonEmptyString.Type given NonEmptyString: Newtype[String] with - inline def validate(input: String): Boolean = + override inline def validate(input: String): Boolean = input.nonEmpty type NonEmptyList = NonEmptyList.Type given NonEmptyList: Newtype[List[Int]] with - inline def validate(input: List[Int]): Boolean = + override inline def validate(input: List[Int]): Boolean = input.nonEmpty type NonZeroInt = NonZeroInt.Type @@ -82,18 +82,18 @@ object NonZeroInt extends Newtype[Int]: type XList = XList.Type given XList: Newtype[List[String]] with - inline def validate(input: List[String]): Boolean = + override inline def validate(input: List[String]): Boolean = input(0) == "x" case class Validated(num: NonZeroInt, name: NonEmptyString, xspot: XList, nada: List[EmptyString]) -case class Tag[X](a: X) -given [A, B](using newType: Newtype.WithType[A, B], tag: Tag[A]): Tag[B] = - newType.unsafeWrapF(tag) +// case class Tag[X](a: X) +// given [A, B](using newType: Newtype.WithType[A, B], tag: Tag[A]): Tag[B] = +// newType.unsafeWrapF(tag) type EmptyString = EmptyString.Type given EmptyString: Newtype[String] with - inline def validate(input: String): Boolean = + override inline def validate(input: String): Boolean = input.isEmpty case class Person2(age: XList) diff --git a/src/test/java/co/blocke/scalajack/json/SampleClass.java b/src/test/java/co/blocke/scalajack/json/SampleClass.java index 7f150e9a..63189e1f 100644 --- a/src/test/java/co/blocke/scalajack/json/SampleClass.java +++ b/src/test/java/co/blocke/scalajack/json/SampleClass.java @@ -9,13 +9,13 @@ public class SampleClass { private String address; // Constructors - // public SampleClass(){} + public SampleClass(){} - public SampleClass(String name, int age, String address) { - this.name = name; - this.age = age; - this.address = address; - } + // public SampleClass(String name, int age, String address) { + // this.name = name; + // this.age = age; + // this.address = address; + // } // Getter methods public String getName() { diff --git a/src/test/java/co/blocke/scalajack/json/collections/CarEnum.java b/src/test/java/co/blocke/scalajack/json/collections/CarEnum.java index a9eb34be..66cf40b6 100644 --- a/src/test/java/co/blocke/scalajack/json/collections/CarEnum.java +++ b/src/test/java/co/blocke/scalajack/json/collections/CarEnum.java @@ -1,6 +1,6 @@ package co.blocke.scalajack.json.collections; -enum CarEnum{ +public enum CarEnum{ TOYOTA, VW, PORSCHE diff --git a/src/test/scala/co.blocke.scalajack/json/classes/ClassSpec.scala b/src/test/scala/co.blocke.scalajack/json/classes/ClassSpec.scala index de14c777..0f7c68d6 100644 --- a/src/test/scala/co.blocke.scalajack/json/classes/ClassSpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/classes/ClassSpec.scala @@ -16,128 +16,157 @@ class ClassSpec() extends AnyFunSpec with JsonMatchers: opaque type phone = String describe(colorString("-------------------------------\n: Class Tests :\n-------------------------------", Console.YELLOW)) { - describe(colorString("+++ Positive Tests +++")) { - it("Simple case class must work (with field renaming)") { - val inst = Person("Bob", 34) - val sj = sjCodecOf[Person] - val js = sj.toJson(inst) - js should matchJson("""{"name":"Bob","duration":34}""") - sj.fromJson(js) shouldEqual(inst) - } - it("Inherited class must work") { - val inst = Child("Bob", 34, 3) - val sj = sjCodecOf[Child] - val js = sj.toJson(inst) - js should matchJson("""{"name":"Bob","age":34,"phase":3}""") - sj.fromJson(js) shouldEqual(inst) - } - it("Non-constructor fields of class must work") { - val inst = Parent(99, List("x","y")) - inst.hidden_=(true) - inst.nope_=(false) - inst.foo = "we'll see" - val sj = sjCodecOf[Parent](JsonConfig.writeNonConstructorFields()) - val js = sj.toJson(inst) - js should matchJson("""{"phase":99,"stuff":["x","y"],"foo":"we'll see","hidden":true}""") - val re = sj.fromJson(js) - re.phase shouldEqual(inst.phase) - re.stuff shouldEqual(inst.stuff) - re.foo shouldEqual(inst.foo) - re.hidden shouldEqual(inst.hidden) - } - it("Block non-constructor fields of class must work") { - val inst = Parent(99, List("x","y")) - inst.hidden_=(true) - inst.nope_=(false) - val sj = sjCodecOf[Parent] - val js = sj.toJson(inst) - js should matchJson("""{"phase":99,"stuff":["x","y"]}""") - val re = sj.fromJson(js) - re.phase shouldEqual(inst.phase) - re.stuff shouldEqual(inst.stuff) - re.foo shouldEqual("ok") - re.hidden shouldEqual(false) - } - it("Parameterized class must work") { - val num: phone = "123-456-7890" - val inst = Params(List(Person("Bob", 34), Person("Sarah", 28)), Some(num)) - val sj = sjCodecOf[Params[Person, phone]] - val js = sj.toJson(inst) - js should matchJson("""{"a":[{"name":"Bob","duration":34},{"name":"Sarah","duration":28}],"b":"123-456-7890"}""") - sj.fromJson(js) shouldEqual(inst) - } - - it("Sealed abstract class with case objects and case classes must work") { - val inst = AbstractClassHolder(Start2, Fish2("Beta", false), Miami2(101.1)) - val sj = sjCodecOf[AbstractClassHolder] - val js = sj.toJson(inst) - js should matchJson("""{"a":"Start2","b":{"_hint":"Fish2","species":"Beta","freshwater":false},"c":{"_hint":"Miami2","temp":101.1}}""") - val re = sj.fromJson(js) - re.a shouldEqual(inst.a) - re.b shouldEqual(inst.b) - (re.c.asInstanceOf[Miami2].temp == inst.c.asInstanceOf[Miami2].temp) shouldEqual(true) - } - it("Sealed abstract class with modified type hint label must work") { - val inst = AbstractClassHolder(Start2, Fish2("Beta", false), Miami2(101.1)) - val sj = sjCodecOf[AbstractClassHolder](JsonConfig.withTypeHintLabel("ref")) - val js = sj.toJson(inst) - js should matchJson("""{"a":"Start2","b":{"ref":"Fish2","species":"Beta","freshwater":false},"c":{"ref":"Miami2","temp":101.1}}""") - val re = sj.fromJson(js) - re.a shouldEqual(inst.a) - re.b shouldEqual(inst.b) - (re.c.asInstanceOf[Miami2].temp == inst.c.asInstanceOf[Miami2].temp) shouldEqual(true) - } - it("Sealed abstract class with type hint policy SCRAMBLE_CLASSNAME label must work") { - val inst = AbstractClassHolder(Start2, Fish2("Beta", false), Miami2(101.1)) - val sj = sjCodecOf[AbstractClassHolder](JsonConfig.withTypeHintPolicy(TypeHintPolicy.SCRAMBLE_CLASSNAME)) - val js = sj.toJson(inst) - val diff = parseJValue(js).diff(parseJValue("""{"a":"Start2","b":{"_hint":"82949-049-49A","species":"Beta","freshwater":false},"c":{"_hint":"53150-867-73B","temp":101.1}}""")) - val diffMap = diff.changed.values.asInstanceOf[Map[String, Map[String, _]]] - assert(diffMap("b").contains("_hint") && diffMap("c").contains("_hint") == true) // ie only the scrambled _hint values are different - val re = sj.fromJson(js) - re.a shouldEqual(inst.a) - re.b shouldEqual(inst.b) - (re.c.asInstanceOf[Miami2].temp == inst.c.asInstanceOf[Miami2].temp) shouldEqual(true) - } - it("Sealed abstract class with type hint policy USE_ANNOTATION label must work") { - val inst = AbstractClassHolder(Start2, Fish2("Beta", false), Miami2(101.1)) - val sj = sjCodecOf[AbstractClassHolder](JsonConfig.withTypeHintPolicy(TypeHintPolicy.USE_ANNOTATION)) - val js = sj.toJson(inst) - js should matchJson("""{"a":"Start2","b":{"_hint":"flipper","species":"Beta","freshwater":false},"c":{"_hint":"vice","temp":101.1}}""") - val re = sj.fromJson(js) - re.a shouldEqual(inst.a) - re.b shouldEqual(inst.b) - (re.c.asInstanceOf[Miami2].temp == inst.c.asInstanceOf[Miami2].temp) shouldEqual(true) - } - it("Parameterized sealed abstract class must work") { - val inst = AbstractClassHolder2(Thing2(15L,"wow")) - val sj = sjCodecOf[AbstractClassHolder2[Long]] - val js = sj.toJson(inst) - js should matchJson("""{"a":{"_hint":"Thing2","t":15,"s":"wow"}}""") - val re = sj.fromJson(js) - re.a.asInstanceOf[Thing2[Long]].t shouldEqual(15L) - re.a.asInstanceOf[Thing2[Long]].s shouldEqual("wow") - } - it("Top-level abstract class must work") { - val inst: AThing[Long] = Thing2(99L,"ok") - val sj = sjCodecOf[AThing[Long]] - val js = sj.toJson(inst) - js should matchJson("""{"_hint":"Thing2","t":99,"s":"ok"}""") - val re = sj.fromJson(js) - re.asInstanceOf[Thing2[Long]].t shouldEqual(99L) - re.asInstanceOf[Thing2[Long]].s shouldEqual("ok") - } - it("Self-referencing class must work (bonus: parameterized self-referencing class)") { - val inst = Empl("abc123", 5, Empl("xyz11", -1, null, Nil), List(Empl("tru777", 0, null, Nil), Empl("pop9", 9, null, Nil))) - val sj = sjCodecOf[Empl[Int]] - val js = sj.toJson(inst) - js should matchJson("""{"id":"abc123","data":5,"boss":{"id":"xyz11","data":-1,"boss":null,"coworkers":[]},"coworkers":[{"id":"tru777","data":0,"boss":null,"coworkers":[]},{"id":"pop9","data":9,"boss":null,"coworkers":[]}]}""") - sj.fromJson(js) shouldEqual(inst) - } - // it("Java classes must work") { - // val inst = new SampleClass("John Doe", 45, "123 Main St") - // val js = sjCodecOf[SampleClass].toJson(inst) - // js should matchJson("""{"address":"123 Main St","name":"John Doe"}""") - // } + it("Simple case class must work (with field renaming)") { + val inst = Person("Bob", 34) + val sj = sjCodecOf[Person] + val js = sj.toJson(inst) + js should matchJson("""{"name":"Bob","duration":34}""") + sj.fromJson(js) shouldEqual (inst) + } + it("Inherited class must work") { + val inst = Child("Bob", 34, 3) + val sj = sjCodecOf[Child] + val js = sj.toJson(inst) + js should matchJson("""{"name":"Bob","age":34,"phase":3}""") + sj.fromJson(js) shouldEqual (inst) + } + it("Non-constructor fields of class must work") { + val inst = Parent(99, List("x", "y")) + inst.hidden_=(true) + inst.nope_=(false) + inst.foo = "we'll see" + val sj = sjCodecOf[Parent](JsonConfig.writeNonConstructorFields()) + val js = sj.toJson(inst) + js should matchJson("""{"phase":99,"stuff":["x","y"],"foo":"we'll see","hidden":true}""") + val re = sj.fromJson(js) + re.phase shouldEqual (inst.phase) + re.stuff shouldEqual (inst.stuff) + re.foo shouldEqual (inst.foo) + re.hidden shouldEqual (inst.hidden) + } + it("Block non-constructor fields of class must work") { + val inst = Parent(99, List("x", "y")) + inst.hidden_=(true) + inst.nope_=(false) + val sj = sjCodecOf[Parent] + val js = sj.toJson(inst) + js should matchJson("""{"phase":99,"stuff":["x","y"]}""") + val re = sj.fromJson(js) + re.phase shouldEqual (inst.phase) + re.stuff shouldEqual (inst.stuff) + re.foo shouldEqual ("ok") + re.hidden shouldEqual (false) + } + it("Parameterized class must work") { + val num: phone = "123-456-7890" + val inst = Params(List(Person("Bob", 34), Person("Sarah", 28)), Some(num)) + val sj = sjCodecOf[Params[Person, phone]] + val js = sj.toJson(inst) + js should matchJson("""{"a":[{"name":"Bob","duration":34},{"name":"Sarah","duration":28}],"b":"123-456-7890"}""") + sj.fromJson(js) shouldEqual (inst) + } + it("Sealed abstract class with case objects and case classes must work") { + val inst = AbstractClassHolder(Start2, Fish2("Beta", false), Miami2(101.1)) + val sj = sjCodecOf[AbstractClassHolder] + val js = sj.toJson(inst) + js should matchJson("""{"a":"Start2","b":{"_hint":"Fish2","species":"Beta","freshwater":false},"c":{"_hint":"Miami2","temp":101.1}}""") + val re = sj.fromJson(js) + re.a shouldEqual (inst.a) + re.b shouldEqual (inst.b) + (re.c.asInstanceOf[Miami2].temp == inst.c.asInstanceOf[Miami2].temp) shouldEqual (true) + } + it("Sealed abstract class with modified type hint label must work") { + val inst = AbstractClassHolder(Start2, Fish2("Beta", false), Miami2(101.1)) + val sj = sjCodecOf[AbstractClassHolder](JsonConfig.withTypeHintLabel("ref")) + val js = sj.toJson(inst) + js should matchJson("""{"a":"Start2","b":{"ref":"Fish2","species":"Beta","freshwater":false},"c":{"ref":"Miami2","temp":101.1}}""") + val re = sj.fromJson(js) + re.a shouldEqual (inst.a) + re.b shouldEqual (inst.b) + (re.c.asInstanceOf[Miami2].temp == inst.c.asInstanceOf[Miami2].temp) shouldEqual (true) + } + it("Sealed abstract class with type hint policy SCRAMBLE_CLASSNAME label must work") { + val inst = AbstractClassHolder(Start2, Fish2("Beta", false), Miami2(101.1)) + val sj = sjCodecOf[AbstractClassHolder](JsonConfig.withTypeHintPolicy(TypeHintPolicy.SCRAMBLE_CLASSNAME)) + val js = sj.toJson(inst) + val diff = parseJValue(js).diff(parseJValue("""{"a":"Start2","b":{"_hint":"82949-049-49A","species":"Beta","freshwater":false},"c":{"_hint":"53150-867-73B","temp":101.1}}""")) + val diffMap = diff.changed.values.asInstanceOf[Map[String, Map[String, ?]]] + assert(diffMap("b").contains("_hint") && diffMap("c").contains("_hint") == true) // ie only the scrambled _hint values are different + val re = sj.fromJson(js) + re.a shouldEqual (inst.a) + re.b shouldEqual (inst.b) + (re.c.asInstanceOf[Miami2].temp == inst.c.asInstanceOf[Miami2].temp) shouldEqual (true) + } + it("Sealed abstract class with type hint policy USE_ANNOTATION label must work") { + val inst = AbstractClassHolder(Start2, Fish2("Beta", false), Miami2(101.1)) + val sj = sjCodecOf[AbstractClassHolder](JsonConfig.withTypeHintPolicy(TypeHintPolicy.USE_ANNOTATION)) + val js = sj.toJson(inst) + js should matchJson("""{"a":"Start2","b":{"_hint":"flipper","species":"Beta","freshwater":false},"c":{"_hint":"vice","temp":101.1}}""") + val re = sj.fromJson(js) + re.a shouldEqual (inst.a) + re.b shouldEqual (inst.b) + (re.c.asInstanceOf[Miami2].temp == inst.c.asInstanceOf[Miami2].temp) shouldEqual (true) + } + it("Parameterized sealed abstract class must work") { + val inst = AbstractClassHolder2(Thing2(15L, "wow")) + val sj = sjCodecOf[AbstractClassHolder2[Long]] + val js = sj.toJson(inst) + js should matchJson("""{"a":{"_hint":"Thing2","t":15,"s":"wow"}}""") + val re = sj.fromJson(js) + re.a.asInstanceOf[Thing2[Long]].t shouldEqual (15L) + re.a.asInstanceOf[Thing2[Long]].s shouldEqual ("wow") + } + it("Top-level abstract class must work") { + val inst: AThing[Long] = Thing2(99L, "ok") + val sj = sjCodecOf[AThing[Long]] + val js = sj.toJson(inst) + js should matchJson("""{"_hint":"Thing2","t":99,"s":"ok"}""") + val re = sj.fromJson(js) + re.asInstanceOf[Thing2[Long]].t shouldEqual (99L) + re.asInstanceOf[Thing2[Long]].s shouldEqual ("ok") + } + it("Self-referencing class must work (bonus: parameterized self-referencing class)") { + val inst = Empl("abc123", 5, Empl("xyz11", -1, null, Nil), List(Empl("tru777", 0, null, Nil), Empl("pop9", 9, null, Nil))) + val sj = sjCodecOf[Empl[Int]] + val js = sj.toJson(inst) + js should matchJson("""{"id":"abc123","data":5,"boss":{"id":"xyz11","data":-1,"boss":null,"coworkers":[]},"coworkers":[{"id":"tru777","data":0,"boss":null,"coworkers":[]},{"id":"pop9","data":9,"boss":null,"coworkers":[]}]}""") + sj.fromJson(js) shouldEqual (inst) + } + it("Java classes must work") { + val inst = new SampleClass() + inst.setName("John Doe") + inst.setAge(45) + inst.setAddress("123 Main St") + val sj = sjCodecOf[SampleClass] + val js = sj.toJson(inst) + js should matchJson("""{"address":"123 Main St","name":"John Doe"}""") + val re = sj.fromJson(js) + re.getName() shouldEqual ("John Doe") + re.getAge() shouldEqual (0) + re.getAddress() shouldEqual ("123 Main St") + } + it("Java class value is null") { + val inst: SampleClass = null + val sj = sjCodecOf[SampleClass] + val js = sj.toJson(inst) + js shouldEqual ("""null""") + sj.fromJson(js) shouldEqual (null) + } + it("Abstract class value is null") { + val inst = AbstractClassHolder(null, null, null) + val sj = sjCodecOf[AbstractClassHolder] + val js = sj.toJson(inst) + js should matchJson("""{"a":null,"b":null,"c":null}""") + val re = sj.fromJson(js) + re.a shouldEqual (null) + re.b shouldEqual (null) + re.c shouldEqual (null) + } + it("Scala case class value is null") { + val inst: Person = null + val sj = sjCodecOf[Person] + val js = sj.toJson(inst) + js shouldEqual ("""null""") + sj.fromJson(js) shouldEqual (inst) } } diff --git a/src/test/scala/co.blocke.scalajack/json/classes/Model.scala b/src/test/scala/co.blocke.scalajack/json/classes/Model.scala index 52e586c6..8d11cfbf 100644 --- a/src/test/scala/co.blocke.scalajack/json/classes/Model.scala +++ b/src/test/scala/co.blocke.scalajack/json/classes/Model.scala @@ -44,7 +44,6 @@ class CityRoute(val numStreets: Int) extends Route // Testing indirection. In real-world scenario all your sealed trait's classes // must be defined in one file. Implementation classes like CityRouteImpl could // be in other files so the sealed trait's file doesn't grow huge. -case class CityRouteImpl(override val numStreets: Int) extends CityRoute(numStreets) case class TraitHolder(a: Command, b: Animal, c: City, d: Route) diff --git a/src/test/scala/co.blocke.scalajack/json/classes/Model2.scala b/src/test/scala/co.blocke.scalajack/json/classes/Model2.scala new file mode 100644 index 00000000..706643f3 --- /dev/null +++ b/src/test/scala/co.blocke.scalajack/json/classes/Model2.scala @@ -0,0 +1,5 @@ +package co.blocke.scalajack +package json +package classes + +case class CityRouteImpl(override val numStreets: Int) extends CityRoute(numStreets) diff --git a/src/test/scala/co.blocke.scalajack/json/classes/TraitSpec.scala b/src/test/scala/co.blocke.scalajack/json/classes/TraitSpec.scala new file mode 100644 index 00000000..e7f83daf --- /dev/null +++ b/src/test/scala/co.blocke.scalajack/json/classes/TraitSpec.scala @@ -0,0 +1,65 @@ +package co.blocke.scalajack +package json +package classes + +import ScalaJack.* +import co.blocke.scala_reflection.* +import org.scalatest.funspec.AnyFunSpec +import org.scalatest.matchers.should.Matchers.* +import org.scalatest.* +import scala.util.* +import TestUtil.* + +import java.util.UUID + +class TraitSpec() extends AnyFunSpec with JsonMatchers: + opaque type phone = String + + describe(colorString("-------------------------------\n: Trait Tests :\n-------------------------------", Console.YELLOW)) { + it("Sealed trait with case objects and case classes must work") { + val inst = TraitHolder(Start, Fish("Beta", false), Miami(101.1), CityRouteImpl(99)) + val sj = sjCodecOf[TraitHolder] + val js = sj.toJson(inst) + js should matchJson("""{"a":"Start","b":{"_hint":"Fish","species":"Beta","freshwater":false},"c":{"_hint":"Miami","temp":101.1},"d":{"_hint":"CityRoute","numStreets":99}}""") + val re = sj.fromJson(js) + re.a shouldEqual (inst.a) + re.b shouldEqual (inst.b) + (re.c.asInstanceOf[Miami].temp == inst.c.asInstanceOf[Miami].temp) shouldEqual (true) + (re.d.asInstanceOf[CityRoute].numStreets == inst.d.asInstanceOf[CityRoute].numStreets) shouldEqual (true) + } + it("Sealed trait with modified type hint label must work") { + val inst = TraitHolder(Start, Fish("Beta", false), Miami(101.1), CityRouteImpl(25)) + val sj = sjCodecOf[TraitHolder](JsonConfig.withTypeHintLabel("ref")) + val js = sj.toJson(inst) + js should matchJson("""{"a":"Start","b":{"ref":"Fish","species":"Beta","freshwater":false},"c":{"ref":"Miami","temp":101.1},"d":{"ref":"CityRoute","numStreets":25}}""") + val re = sj.fromJson(js) + re.a shouldEqual (inst.a) + re.b shouldEqual (inst.b) + (re.c.asInstanceOf[Miami].temp == inst.c.asInstanceOf[Miami].temp) shouldEqual (true) + (re.d.asInstanceOf[CityRoute].numStreets == inst.d.asInstanceOf[CityRoute].numStreets) shouldEqual (true) + } + it("Sealed trait with type hint policy SCRAMBLE_CLASSNAME label must work") { + val inst = TraitHolder(Start, Fish("Beta", false), Miami(101.1), CityRouteImpl(25)) + val sj = sjCodecOf[TraitHolder](JsonConfig.withTypeHintPolicy(TypeHintPolicy.SCRAMBLE_CLASSNAME)) + val js = sj.toJson(inst) + val diff = parseJValue(js).diff(parseJValue("""{"a":"Start","b":{"_hint":"86999-847-46A","species":"Beta","freshwater":false},"c":{"_hint":"13652-857-33B","temp":101.1},"d":{"_hint":"51470-503-54B","numStreets":25}}""")) + val diffMap = diff.changed.values.asInstanceOf[Map[String, Map[String, ?]]] + assert(diffMap("b").contains("_hint") && diffMap("c").contains("_hint") && diffMap("d").contains("_hint") == true) // ie only the scrambled _hint values are different + val re = sj.fromJson(js) + re.a shouldEqual (inst.a) + re.b shouldEqual (inst.b) + (re.c.asInstanceOf[Miami].temp == inst.c.asInstanceOf[Miami].temp) shouldEqual (true) + (re.d.asInstanceOf[CityRoute].numStreets == inst.d.asInstanceOf[CityRoute].numStreets) shouldEqual (true) + } + it("Sealed trait with type hint policy USE_ANNOTATION label must work") { + val inst = TraitHolder(Start, Fish("Beta", false), Miami(101.1), CityRouteImpl(25)) + val sj = sjCodecOf[TraitHolder](JsonConfig.withTypeHintPolicy(TypeHintPolicy.USE_ANNOTATION)) + val js = sj.toJson(inst) + js should matchJson("""{"a":"Start","b":{"_hint":"flipper","species":"Beta","freshwater":false},"c":{"_hint":"vice","temp":101.1},"d":{"_hint":"CityRoute","numStreets":25}}""") + val re = sj.fromJson(js) + re.a shouldEqual (inst.a) + re.b shouldEqual (inst.b) + (re.c.asInstanceOf[Miami].temp == inst.c.asInstanceOf[Miami].temp) shouldEqual (true) + (re.d.asInstanceOf[CityRoute].numStreets == inst.d.asInstanceOf[CityRoute].numStreets) shouldEqual (true) + } + } diff --git a/src/test/scala/co.blocke.scalajack/json/classes/TraitSpec.scalax b/src/test/scala/co.blocke.scalajack/json/classes/TraitSpec.scalax deleted file mode 100644 index eda957a3..00000000 --- a/src/test/scala/co.blocke.scalajack/json/classes/TraitSpec.scalax +++ /dev/null @@ -1,47 +0,0 @@ -package co.blocke.scalajack -package json -package classes - -import ScalaJack.* -import co.blocke.scala_reflection.* -import org.scalatest.funspec.AnyFunSpec -import org.scalatest.matchers.should.Matchers.* -import org.scalatest.* -import scala.util.* -import TestUtil.* - -import java.util.UUID - -class TraitSpec() extends AnyFunSpec with JsonMatchers: - opaque type phone = String - - describe(colorString("-------------------------------\n: Trait Tests :\n-------------------------------", Console.YELLOW)) { - describe(colorString("+++ Positive Tests +++")) { - it("Sealed trait with case objects and case classes must work") { - val inst = TraitHolder(Start, Fish("Beta", false), Miami(101.1)) - val js = sj[TraitHolder].toJson(inst) - js should matchJson("""{"a":"Start","b":{"_hint":"Fish","species":"Beta","freshwater":false},"c":{"_hint":"Miami","temp":101.1}}""") - } - it("Sealed trait with modified type hint label must work") { - val inst = TraitHolder(Start, Fish("Beta", false), Miami(101.1)) - val js = sj[TraitHolder](JsonConfig.withTypeHintLabel("ref")).toJson(inst) - js should matchJson("""{"a":"Start","b":{"ref":"Fish","species":"Beta","freshwater":false},"c":{"ref":"Miami","temp":101.1}}""") - } - it("Sealed trait with type hint policy SCRAMBLE_CLASSNAME label must work") { - val inst = TraitHolder(Start, Fish("Beta", false), Miami(101.1)) - val js = sj[TraitHolder](JsonConfig.withTypeHintPolicy(TypeHintPolicy.SCRAMBLE_CLASSNAME)).toJson(inst) - val diff = parseJValue(js).diff(parseJValue("""{"a":"Start","b":{"_hint":"82949-049-49A","species":"Beta","freshwater":false},"c":{"_hint":"53150-867-73B","temp":101.1}}""")) - val diffMap = diff.changed.values.asInstanceOf[Map[String, Map[String, _]]] - assert(diffMap("b").contains("_hint") && diffMap("c").contains("_hint") == true) - } - it("Sealed trait with type hint policy USE_ANNOTATION label must work") { - val inst = TraitHolder(Start, Fish("Beta", false), Miami(101.1)) - val js = sj[TraitHolder](JsonConfig.withTypeHintPolicy(TypeHintPolicy.USE_ANNOTATION)).toJson(inst) - js should matchJson("""{"a":"Start","b":{"_hint":"flipper","species":"Beta","freshwater":false},"c":{"_hint":"vice","temp":101.1}}""") - } - - it("Sealed trait with class children implemented by case classes (in separate compilation unit)") { - (pending) - } - } - } diff --git a/src/test/scala/co.blocke.scalajack/json/misc/EnumSpec.scala b/src/test/scala/co.blocke.scalajack/json/misc/EnumSpec.scala new file mode 100644 index 00000000..ae3669b6 --- /dev/null +++ b/src/test/scala/co.blocke.scalajack/json/misc/EnumSpec.scala @@ -0,0 +1,109 @@ +package co.blocke.scalajack +package json +package misc + +import ScalaJack.* +import co.blocke.scalajack.json.collections.CarEnum +import co.blocke.scala_reflection.* +import org.scalatest.funspec.AnyFunSpec +import org.scalatest.matchers.should.Matchers.* +import org.scalatest.* +import scala.util.* +import TestUtil.* + +class EnumSpec() extends AnyFunSpec with JsonMatchers: + + describe(colorString("-------------------------------\n: Enum Tests :\n-------------------------------", Console.YELLOW)) { + it("Enum as Map key and value must work") { + val inst = MapHolder[Color, Color](Map(Color.Red -> Color.Blue, Color.Green -> Color.Red)) + val sj = sjCodecOf[MapHolder[Color, Color]] + val js = sj.toJson(inst) + js should matchJson("""{"a":{"Red":"Blue","Green":"Red"}}""") + sj.fromJson(js) shouldEqual(inst) + } + it("Enum as Map key and value must work (using id)") { + val inst = MapHolder[Color, Color](Map(Color.Red -> Color.Blue, Color.Green -> Color.Red)) + val sj = sjCodecOf[MapHolder[Color, Color]](JsonConfig.withEnumsAsIds(Nil)) + val js = sj.toJson(inst) + js should matchJson("""{"a":{"0":1,"2":0}}""") + sj.fromJson(js) shouldEqual(inst) + } + it("Enumeration (Scale 2) as Map key and value must work") { + import Permissions.* + val inst = MapHolder[Permissions, Permissions](Map(Permissions.READ -> Permissions.WRITE, Permissions.EXEC -> Permissions.NONE)) + val sj = sjCodecOf[MapHolder[Permissions, Permissions]] + val js = sj.toJson(inst) + js should matchJson("""{"a":{"READ":"WRITE","EXEC":"NONE"}}""") + sj.fromJson(js) shouldEqual(inst) + } + it("Enumeration (Scale 2) as Map key and value must work (using id)") { + import Permissions.* + val inst = MapHolder[Permissions, Permissions](Map(Permissions.READ -> Permissions.WRITE, Permissions.EXEC -> Permissions.NONE)) + val sj = sjCodecOf[MapHolder[Permissions, Permissions]](JsonConfig.withEnumsAsIds(Nil)) + val js = sj.toJson(inst) + js should matchJson("""{"a":{"0":1,"2":3}}""") + sj.fromJson(js) shouldEqual(inst) + } + it("Enumeration (Scala 2) ordinal mutation") { + import SizeWithType._ + val inst = SampleEnum(Size.Small, Size.Medium, Size.Large, null, Size.Medium, Little) + val sj = sjCodecOf[SampleEnum] + val js = sj.toJson(inst) + js shouldEqual("""{"e1":"Small","e2":"Medium","e3":"Large","e4":null,"e5":"Medium","e6":"Little"}""") + // mutate e5 into an ordinal... + val js2 = js.replaceAll(""""e5":"Medium"""", """"e5":1""") + sj.fromJson(js2) shouldEqual(inst) + } + it("Java Enumeration as Map key and value must work") { + val inst = MapHolder[CarEnum, CarEnum](Map(CarEnum.VW -> CarEnum.PORSCHE, CarEnum.PORSCHE -> CarEnum.TOYOTA)) + val sj = sjCodecOf[MapHolder[CarEnum, CarEnum]] + val js = sj.toJson(inst) + js should matchJson("""{"a":{"VW":"PORSCHE","PORSCHE":"TOYOTA"}}""") + sj.fromJson(js) shouldEqual(inst) + } + it("Java Enumeration as Map key and value must work (using id)") { + val inst = MapHolder[CarEnum, CarEnum](Map(CarEnum.VW -> CarEnum.PORSCHE, CarEnum.PORSCHE -> CarEnum.TOYOTA)) + val sj = sjCodecOf[MapHolder[CarEnum, CarEnum]](JsonConfig.withEnumsAsIds(Nil)) + val js = sj.toJson(inst) + val targetJs = RType.of[CarEnum] match + case t: co.blocke.scala_reflection.rtypes.JavaEnumRType[?] => + val valMap = t.values.zipWithIndex.toMap + s"""{"a":{"${valMap("VW")}":${valMap("PORSCHE")},"${valMap("PORSCHE")}":${valMap("TOYOTA")}}}""" + js shouldEqual(targetJs) + sj.fromJson(js) shouldEqual(inst) + } + it("Enum/Enumeration mix of enum as value must work") { + import Permissions.* + val inst = MapHolder[Color, Permissions](Map(Color.Red -> Permissions.WRITE, Color.Blue -> Permissions.NONE)) + val sj = sjCodecOf[MapHolder[Color, Permissions]](JsonConfig.withEnumsAsIds(List("co.blocke.scalajack.json.misc.Color"))) + val js = sj.toJson(inst) + js should matchJson("""{"a":{"0":"WRITE","1":"NONE"}}""") + sj.fromJson(js) shouldEqual(inst) + } + it("Sealed trait enumeration w/case objects must work") { + val inst = FlavorHolder( Chocolate, null, Map(Bourbon->"a"), Map("a"->Vanilla)) + val sj = sjCodecOf[FlavorHolder] + val js = sj.toJson(inst) + js should matchJson("""{"f":"Chocolate","f2":null,"f3":{"Bourbon":"a"},"f4":{"a":"Vanilla"}}""") + sj.fromJson(js) shouldEqual(inst) + } + it("Sealed trait enumeration w/case class must work") { + val inst = VehicleHolder( Car(4, "Red"), null, Map("a"->Truck(18))) + val sj = sjCodecOf[VehicleHolder] + val js = sj.toJson(inst) + js should matchJson("""{"f":{"_hint":"Car","numberOfWheels":4,"color":"Red"},"f2":null,"f4":{"a":{"_hint":"Truck","numberOfWheels":18}}}""") + sj.fromJson(js) shouldEqual(inst) + } + it("Enum must break - bad value") { + val sj = sjCodecOf[MapHolder[Color, Color]] + val js = """{"a":{"Red":"Bogus","Green":"Red"}}""" + val ex = intercept[java.lang.IllegalArgumentException](sj.fromJson(js)) + ex.getMessage() shouldEqual("enum co.blocke.scalajack.json.misc.Color has no case with name: Bogus") + } + it("Enum must break(using id) - bad value") { + val sj = sjCodecOf[MapHolder[Color, Color]](JsonConfig.withEnumsAsIds(Nil)) + val js = """{"a":{"0":1,"9":0}}""" + val ex = intercept[java.util.NoSuchElementException](sj.fromJson(js)) + ex.getMessage() shouldEqual("enum co.blocke.scalajack.json.misc.Color has no case with ordinal: 9") + } + } \ No newline at end of file diff --git a/src/test/scala/co.blocke.scalajack/json/misc/EnumSpec.scalax b/src/test/scala/co.blocke.scalajack/json/misc/EnumSpec.scalax deleted file mode 100644 index 07d2dd73..00000000 --- a/src/test/scala/co.blocke.scalajack/json/misc/EnumSpec.scalax +++ /dev/null @@ -1,54 +0,0 @@ -TBD - -Test: - -* Enumeration -* Enum -* Java Enumeration -* Sealed trait w/case object -* Sealed trait w/case classes - -* Try permutations of Map having keys of each of the above types, including ordinal (int) values where available - - -Partial: - - - it("Enum as Map key and value must work") { - val inst = MapHolder[Color, Color](Map(Color.Red -> Color.Blue, Color.Green -> Color.Red)) - val js = sj[MapHolder[Color, Color]].toJson(inst) - js should matchJson("""{"a":{"Red":"Blue","Green":"Red"}}""") - } - it("Enum as Map key and value must work (using id)") { - val inst = MapHolder[Color, Color](Map(Color.Red -> Color.Blue, Color.Green -> Color.Red)) - val js = sj[MapHolder[Color, Color]](JsonConfig.withEnumsAsIds(Some(Nil))).toJson(inst) - js should matchJson("""{"a":{"0":2,"1":0}}""") - } - it("Enumeration as Map key and value must work") { - import Permissions.* - val inst = MapHolder[Permissions, Permissions](Map(Permissions.READ -> Permissions.WRITE, Permissions.EXEC -> Permissions.NONE)) - val js = sj[MapHolder[Permissions, Permissions]].toJson(inst) - js should matchJson("""{"a":{"READ":"WRITE","EXEC":"NONE"}}""") - } - it("Enumeration as Map key and value must work (using id)") { - import Permissions.* - val inst = MapHolder[Permissions, Permissions](Map(Permissions.READ -> Permissions.WRITE, Permissions.EXEC -> Permissions.NONE)) - val js = sj[MapHolder[Permissions, Permissions]](JsonConfig.withEnumsAsIds(Some(Nil))).toJson(inst) - js should matchJson("""{"a":{"0":1,"2":3}}""") - } - it("Java Enumeration as Map key and value must work") { - val inst = MapHolder[CarEnum, CarEnum](Map(CarEnum.VW -> CarEnum.PORSCHE, CarEnum.PORSCHE -> CarEnum.TOYOTA)) - val js = sj[MapHolder[CarEnum, CarEnum]].toJson(inst) - js should matchJson("""{"a":{"VW":"PORSCHE","PORSCHE":"TOYOTA"}}""") - } - it("Java Enumeration as Map key and value must work (using id)") { - val inst = MapHolder[CarEnum, CarEnum](Map(CarEnum.VW -> CarEnum.PORSCHE, CarEnum.PORSCHE -> CarEnum.TOYOTA)) - val js = sj[MapHolder[CarEnum, CarEnum]](JsonConfig.withEnumsAsIds(Some(Nil))).toJson(inst) - js should matchJson("""{"a":{"1":2,"2":0}}""") - } - it("Enum/Enumeration mix of enum as value must work") { - import Permissions.* - val inst = MapHolder[Color, Permissions](Map(Color.Red -> Permissions.WRITE, Color.Blue -> Permissions.NONE)) - val js = sj[MapHolder[Color, Permissions]](JsonConfig.withEnumsAsIds(Some(List("co.blocke.scalajack.json.collections.Color")))).toJson(inst) - js should matchJson("""{"a":{"0":"WRITE","2":"NONE"}}""") - } \ No newline at end of file diff --git a/src/test/scala/co.blocke.scalajack/json/misc/MiscSpec.scala b/src/test/scala/co.blocke.scalajack/json/misc/MiscSpec.scala new file mode 100644 index 00000000..1a007249 --- /dev/null +++ b/src/test/scala/co.blocke.scalajack/json/misc/MiscSpec.scala @@ -0,0 +1,92 @@ +package co.blocke.scalajack +package json +package misc + +import ScalaJack.* +import co.blocke.scala_reflection.* +import org.scalatest.funspec.AnyFunSpec +import org.scalatest.matchers.should.Matchers.* +import org.scalatest.* +import scala.util.* +import TestUtil.* + +import java.util.UUID + +class MiscSpec() extends AnyFunSpec with JsonMatchers: + + describe(colorString("-------------------------------\n: Misc Tests :\n-------------------------------", Console.YELLOW)) { + it("String escaping must work (proves escape can be turned off)") { + val inst = StringHolder("""This is a "strange" test +on another level.""") + val sj1 = sjCodecOf[StringHolder] + val js1 = sj1.toJson(inst) + val sj2 = sjCodecOf[StringHolder](JsonConfig.suppressEscapedStrings()) + val js2 = sj2.toJson(inst) + js1 should equal("""{"a":"This is a \"strange\" test\non another level."}""") + js2 should equal("""{"a":"This is a "strange" test +on another level."}""") + sj1.fromJson(js1) shouldEqual(inst) + val msg = + """Expected ',' or '}' but found 's' at position [17] + |{"a":"This is a "strange" test~on another level."} + |-----------------^""".stripMargin + val ex = intercept[JsonParseError](sj2.fromJson(js2)) + ex.show shouldEqual msg + } + it("NeoType integration must work") { + val inst = Validated(NonEmptyString("Mike"), XList(List("x", "y", "z")), List(EmptyString(""), EmptyString(""), EmptyString(""))) + val sj = sjCodecOf[Validated] + val js = sj.toJson(inst) + js should equal("""{"name":"Mike","xspot":["x","y","z"],"nada":["","",""]}""") + sj.fromJson(js) shouldEqual(inst) + } + it("NeoType validation must work (test failure)") { + val sj = sjCodecOf[Validated] + val js = """{"name":"","xspot":["x","y","z"],"nada":["","",""]}""" + val msg = + """NeoType validation for NonEmptyString failed at position [10] + |{"name":"","xspot":["x","y","z"],"nada":["","",""]} + |----------^""".stripMargin + val ex = intercept[JsonParseError](sj.fromJson(js)) + ex.show shouldEqual msg + } + it("Any type must work (non-exhaustive test)") { + val inst = AnyHolder( + Some(List(1, 2, 3)), + None, + TryHolder(Success(-5)), + Success(99), + Failure(new Exception("oops")), + Map("a" -> 1, "b" -> 2), + Right(3), + Left("nope"), + (Some('a'), None, Some('b')) + ) + val sj = sjCodecOf[AnyHolder] + val js = sj.toJson(inst) + js should equal("""{"maybe":[1,2,3],"itried":{"a":-5},"itried2":99,"ifailed":null,"anymap":{"a":1,"b":2},"whichOneR":3,"whichOneL":"nope","bunch":["a",null,"b"]}""") + sj.fromJson(js) shouldEqual(AnyHolder(List(1, 2, 3),null,Map("a" -> -5),99,null,Map("a" -> 1, "b" -> 2),3,"nope",List("a", null, "b"))) + } + it("Any type must work (none as null)") { + val inst = AnyHolder( + Some(List(1, 2, 3)), + None, + TryHolder(Success(-5)), + Success(99), + Failure(new Exception("oops")), + Map("a" -> 1, "b" -> 2), + Right(3), + Left("nope"), + (Some('a'), None, Some('b')) + ) + val sj = sjCodecOf[AnyHolder]( + JsonConfig + .withNoneAsNull() + .withEitherLeftHandling(EitherLeftPolicy.ERR_MSG_STRING) + .withTryFailureHandling(TryPolicy.ERR_MSG_STRING) + ) + val js = sj.toJson(inst) + js should equal("""{"maybe":[1,2,3],"maybeNot":null,"itried":{"a":-5},"itried2":99,"ifailed":"Try Failure with msg: oops","anymap":{"a":1,"b":2},"whichOneR":3,"whichOneL":"Left Error: nope","bunch":["a",null,"b"]}""") + sj.fromJson(js) shouldEqual(AnyHolder(List(1, 2, 3),null,Map("a" -> -5),99,"Try Failure with msg: oops",Map("a" -> 1, "b" -> 2),3,"Left Error: nope",List("a", null, "b"))) + } + } diff --git a/src/test/scala/co.blocke.scalajack/json/misc/MiscTests.scalax b/src/test/scala/co.blocke.scalajack/json/misc/MiscTests.scalax deleted file mode 100644 index 40eb1266..00000000 --- a/src/test/scala/co.blocke.scalajack/json/misc/MiscTests.scalax +++ /dev/null @@ -1,73 +0,0 @@ -package co.blocke.scalajack -package json -package misc - -import ScalaJack.* -import co.blocke.scala_reflection.* -import org.scalatest.funspec.AnyFunSpec -import org.scalatest.matchers.should.Matchers.* -import org.scalatest.* -import scala.util.* -import TestUtil.* - -import java.util.UUID - -class MiscSpec() extends AnyFunSpec with JsonMatchers: - opaque type Count = Int - opaque type CountX = Option[Int] - type CountY = String - type CountZ = Option[String] - - describe(colorString("-------------------------------\n: Misc Tests :\n-------------------------------", Console.YELLOW)) { - describe(colorString("+++ Positive Tests +++")) { - it("String escaping must work (proves escape can be turned off)") { - val inst = StringHolder("""This is a "strange" test -on another level.""") - val js1 = sj[StringHolder].toJson(inst) - val js2 = sj[StringHolder](JsonConfig.withSuppressEscapedStrings()).toJson(inst) - js1 should equal("""{"a":"This is a \"strange\" test\non another level."}""") - js2 should equal("""{"a":"This is a "strange" test -on another level."}""") - } - it("NeoType integration must work") { - val inst = Validated(NonEmptyString("Mike"), XList(List("x", "y", "z")), List(EmptyString(""), EmptyString(""), EmptyString(""))) - val js = sj[Validated].toJson(inst) - js should equal("""{"name":"Mike","xspot":["x","y","z"],"nada":["","",""]}""") - } - it("Any type must work (non-exhaustive test)") { - val inst = AnyHolder( - Some(List(1, 2, 3)), - None, - TryHolder(Success(-5)), - Success(99), - Failure(new Exception("oops")), - Map("a" -> 1, "b" -> 2), - Right(3), - Left("nope"), - (Some('a'), None, Some('b')) - ) - val js = sj[AnyHolder].toJson(inst) - js should equal("""{"maybe":[1,2,3],"itried":{"a":-5},"itried2":99,"anymap":{"a":1,"b":2},"whichOneR":3,"bunch":["a",null,"b"]}""") - } - it("Any type must work (none as null)") { - val inst = AnyHolder( - Some(List(1, 2, 3)), - None, - TryHolder(Success(-5)), - Success(99), - Failure(new Exception("oops")), - Map("a" -> 1, "b" -> 2), - Right(3), - Left("nope"), - (Some('a'), None, Some('b')) - ) - val js = sj[AnyHolder]( - JsonConfig - .withNoneAsNull() - .withEitherLeftHandling(EitherLeftPolicy.ERR_MSG_STRING) - .withTryFailureHandling(TryPolicy.AS_NULL) - ).toJson(inst) - js should equal("""{"maybe":[1,2,3],"maybeNot":"null","itried":{"a":-5},"itried2":99,"ifailed":"null","anymap":{"a":1,"b":2},"whichOneR":3,"whichOneL":"Left Error: nope","bunch":["a",null,"b"]}""") - } - } - } diff --git a/src/test/scala/co.blocke.scalajack/json/misc/Model.scala b/src/test/scala/co.blocke.scalajack/json/misc/Model.scala index 506834c1..e1c88ad4 100644 --- a/src/test/scala/co.blocke.scalajack/json/misc/Model.scala +++ b/src/test/scala/co.blocke.scalajack/json/misc/Model.scala @@ -50,19 +50,21 @@ case class AliasHolder2[T](a: T, b: List[T], c: Map[String, T]) case class StringHolder(a: String) +case class MapHolder[T, V](a: Map[T, V]) + type NonEmptyString = NonEmptyString.Type given NonEmptyString: Newtype[String] with - inline def validate(input: String): Boolean = + override inline def validate(input: String): Boolean = input.nonEmpty type XList = XList.Type given XList: Newtype[List[String]] with - inline def validate(input: List[String]): Boolean = + override inline def validate(input: List[String]): Boolean = input.nonEmpty && input(0) == "x" type EmptyString = EmptyString.Type given EmptyString: Newtype[String] with - inline def validate(input: String): Boolean = + override inline def validate(input: String): Boolean = input.isEmpty case class Validated(name: NonEmptyString, xspot: XList, nada: List[EmptyString]) @@ -87,6 +89,13 @@ object SizeWithType extends Enumeration { val Little, Grand = Value } import SizeWithType.* + +object Permissions extends Enumeration { + type Permissions = Value + val READ, WRITE, EXEC, NONE = Value +} + +// case class SampleEnum(e1: Enumeration#Value, e2: Enumeration#Value, e3: Enumeration#Value, e4: Enumeration#Value, e5: Enumeration#Value, e6: SizeWithType) case class SampleEnum(e1: Size.Value, e2: Size.Value, e3: Size.Value, e4: Size.Value, e5: Size.Value, e6: SizeWithType) enum Color { @@ -98,11 +107,10 @@ sealed trait Flavor case object Vanilla extends Flavor case object Chocolate extends Flavor case object Bourbon extends Flavor +case class FlavorHolder(f: Flavor, f2: Flavor, f3: Map[Flavor, String], f4: Map[String, Flavor]) sealed trait Vehicle case class Truck(numberOfWheels: Int) extends Vehicle case class Car(numberOfWheels: Int, color: String) extends Vehicle case class Plane(numberOfEngines: Int) extends Vehicle - -case class Ride(wheels: Vehicle) -case class Favorite(flavor: Flavor) +case class VehicleHolder(f: Vehicle, f2: Vehicle, f4: Map[String, Vehicle]) diff --git a/src_old/test/scala/co.blocke.scalajack/json/primitives/Enums.scala b/src_old/test/scala/co.blocke.scalajack/json/primitives/Enums.scala index dae76066..31e4cc05 100644 --- a/src_old/test/scala/co.blocke.scalajack/json/primitives/Enums.scala +++ b/src_old/test/scala/co.blocke.scalajack/json/primitives/Enums.scala @@ -16,7 +16,6 @@ class Enums() extends FunSuite: test("Enumeration (Scala 2.x) must work (not nullable)") { describe("-----------------\n: Scala Enums :\n-----------------", Console.BLUE) - describe("+++ Positive Tests +++") import SizeWithType._ val inst = SampleEnum(Size.Small, Size.Medium, Size.Large, null, Size.Medium, Little) From 4f90b68bee695dbdc74808ff3c34d77b0568d1c5 Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Tue, 23 Apr 2024 23:27:19 -0500 Subject: [PATCH 57/65] almost code complete --- README.md | 97 +- TODO.txt | 52 - rnd/build.sbt | 53 - rnd/project/build.properties | 1 - rnd/project/metals.sbt | 6 - rnd/project/plugins.sbt | 1 - .../co/blocke/scalajack/ByteArrayAccess.java | 46 - .../co.blocke.scalajack/JsonWriter.scala | 3000 ----------------- .../JsonWriterException.scala | 4 - .../main/scala/co.blocke.scalajack/Run.scala | 27 - .../co.blocke.scalajack/WriterConfig.scala | 53 - .../json/JsonCodecMaker.scala | 29 +- .../json/readers/ClassReader.scalax | 58 - .../json/readers/CollectionReader.scalax | 130 - .../json/readers/EnumReader.scalax | 99 - .../json/readers/MiscReader.scalax | 76 - .../json/readers/PrimitiveReader.scalax | 220 -- .../json/writing/WriteCodec.scalax | 766 ----- .../scala/co.blocke.scalajack/run/Play.scala | 111 +- .../json/classes/TraitSpec.scala | 2 +- ...ArraySpec.scalax => SeqSetArraySpec.scala} | 120 +- .../json/misc/EnumSpec.scala | 186 +- .../json/misc/LRSpec.scala | 10 + .../json/misc/MiscSpec.scala | 8 +- .../co.blocke.scalajack/json/misc/Model.scala | 21 + .../main/java/co/blocke/scalajack/Change.java | 9 - .../java/co/blocke/scalajack/Collection.java | 9 - .../main/java/co/blocke/scalajack/DBKey.java | 12 - .../main/java/co/blocke/scalajack/Ignore.java | 13 - .../java/co/blocke/scalajack/JavaStuff.java | 11 - .../java/co/blocke/scalajack/Optional.java | 10 - .../co.blocke.scalajack/Converters.scala | 70 - .../scala/co.blocke.scalajack/Main.scalax | 37 - .../co.blocke.scalajack/Performance.scalax | 106 - .../scala/co.blocke.scalajack/SJCapture.scala | 9 - .../scala/co.blocke.scalajack/ScalaJack.scala | 20 - .../DelimitedEitherTypeAdapterFactory.scala | 46 - .../delimited/DelimitedFlavor.scala | 72 - .../DelimitedOptionTypeAdapterFactory.scala | 31 - .../delimited/DelimitedParser.scala | 221 -- .../delimited/DelimitedWriter.scala | 109 - .../co.blocke.scalajack/json/JsonFlavor.scala | 63 - .../co.blocke.scalajack/json/JsonParser.scala | 428 --- .../co.blocke.scalajack/json/JsonWriter.scala | 188 -- .../json4s/JValueBuilder.scala | 22 - .../json4s/Json4sFlavor.scala | 68 - .../json4s/Json4sParser.scala | 201 -- .../json4s/Json4sWriter.scala | 138 - .../json4s/StringWrapTypeAdapter.scala | 75 - .../model/ClassFieldMember.scala | 20 - .../model/HintValueModifier.scala | 38 - .../model/JackFlavor.scala | 115 - .../model/LazyTypeAdapter.scala | 38 - .../co.blocke.scalajack/model/Parser.scala | 50 - .../model/StringBuilder.scala | 18 - .../model/TypeAdapaterCache.scala | 140 - .../model/TypeAdapter.scala | 72 - .../model/TypeAdapterFactory.scala | 8 - .../model/ViewSplice.scala | 84 - .../co.blocke.scalajack/model/Writer.scala | 34 - .../typeadapter/AnyTypeAdapter.scala | 101 - .../typeadapter/ArrayTypeAdatper.scala | 71 - .../typeadapter/CollectionTypeAdapter.scala | 191 -- .../typeadapter/EitherTypeAdapter.scala | 78 - .../typeadapter/EnumTypeAdapter.scala | 178 - .../typeadapter/FallbackTypeAdapter.scala | 30 - .../typeadapter/IntersectionTypeAdapter.scala | 55 - .../typeadapter/JavaPrimitives.scala | 237 -- .../MaybeStringWrapTypeAadapter.scala | 49 - .../typeadapter/OptionTypeAdapter.scala | 118 - .../PermissiveJavaPrimitives.scala | 191 -- .../PermissiveScalaPrimitives.scala | 169 - .../typeadapter/ScalaPrimitives.scala | 236 -- .../typeadapter/SealedTraitTypeAdapter.scala | 118 - .../typeadapter/StringWrapTypeAdapter.scala | 44 - .../typeadapter/TimePrimitives.scala | 323 -- .../typeadapter/TraitTypeAdapter.scala | 75 - .../typeadapter/TryTypeAdapter.scala | 48 - .../typeadapter/TupleTypeAdapter.scala | 54 - .../typeadapter/UUIDTypeAdapter.scala | 44 - .../typeadapter/UnionTypeAdapter.scala | 67 - .../typeadapter/ValueClassTypeAdapter.scala | 43 - .../classes/CaseClassTypeAdapter.scala | 41 - .../classes/ClassTypeAdapterBase.scala | 25 - .../classes/JavaClassTypeAdapter.scala | 116 - .../classes/NonCaseClassTypeAdapter.scala | 52 - .../classes/ScalaClassTypeAdapter.scala | 161 - .../ScalaClassTypeAdapterFactory.scala | 116 - .../collection/JavaMapLikeTypeAdapter.scala | 73 - .../collection/JavaSeqLikeTypeAdapter.scala | 57 - .../collection/JavaStackTypeAdapter.scala | 59 - .../collection/MapLikeTypeAdapter.scala | 70 - .../collection/SeqLikeTypeAdapter.scala | 53 - .../util/BijectiveFunction.scala | 74 - .../co.blocke.scalajack/util/FixFloat.scala | 11 - .../yaml/YamlBuilder.scala | 20 - .../co.blocke.scalajack/yaml/YamlFlavor.scala | 77 - .../co.blocke.scalajack/yaml/YamlParser.scala | 278 -- .../co.blocke.scalajack/yaml/YamlWriter.scala | 177 - .../java/co/blocke/scalajack/JavaArray.java | 53 - .../java/co/blocke/scalajack/JavaCap.java | 12 - .../java/co/blocke/scalajack/JavaEnum.java | 9 - .../co/blocke/scalajack/JavaSimpleBase.java | 22 - .../co/blocke/scalajack/JavaSimpleChild.java | 4 - .../test/java/co/blocke/scalajack/Maybe.java | 15 - .../test/java/co/blocke/scalajack/Maybe2.java | 15 - .../java/co/blocke/scalajack/OnSetter.java | 11 - .../java/co/blocke/scalajack/Temperature.java | 6 - .../java/co/blocke/scalajack/Unsupported.java | 5 - .../co.blocke.scalajack/ConverterSpec.scala | 173 - .../scala/co.blocke.scalajack/JsonDiff.scala | 58 - .../co.blocke.scalajack/JsonMatcher.scala | 21 - .../scala/co.blocke.scalajack/TestUtil.scala | 42 - .../delimited/DelimitedSpec.scala | 374 -- .../co.blocke.scalajack/delimited/Model.scala | 36 - .../json/collections/AnyCollections.scala | 66 - .../json/collections/Arrays.scala | 244 -- .../json/collections/Maps.scala | 342 -- .../json/collections/Model.scala | 105 - .../json/collections/Options.scala | 435 --- .../json/collections/Seqs.scala | 272 -- .../json/collections/Tuples.scala | 52 - .../json/custom/CustomAdapter.scala | 20 - .../json/custom/CustomTypeHints.scala | 134 - .../json/custom/Model.scala | 59 - .../json/custom/ParseOrElse.scala | 19 - .../json/mapkeys/ClassPrimKeys.scala | 319 -- .../json/mapkeys/JavaPrimKeys.scala | 500 --- .../json/mapkeys/ListCollKeys.scala | 125 - .../json/mapkeys/MapCollKeys.scala | 166 - .../json/mapkeys/Model.scala | 176 - .../json/mapkeys/ScalaPrimKeys.scala | 248 -- .../json/mapkeys/TupleCollKeys.scala | 236 -- .../json/mapkeys/ValueClassKeys.scala | 454 --- .../json/parameters/ClassParams.scala | 103 - .../json/parameters/Model.scala | 60 - .../json/parameters/TraitParams.scala | 143 - .../json/plainclass/Inheritance.scala | 141 - .../json/plainclass/Misc.scala | 81 - .../json/plainclass/Model.scala | 132 - .../json/plainclass/TryAndCapture.scala | 80 - .../json/plainclass/ValueClass.scala | 31 - .../json/primitives/AnyPrim.scala | 159 - .../json/primitives/Enums.scala | 136 - .../json/primitives/JavaPrim.scala | 326 -- .../json/primitives/Model.scala | 197 -- .../primitives/PermissivePrimitives.scala | 233 -- .../json/primitives/ScalaPrim.scala | 334 -- .../json/primitives/TimePrim.scala | 299 -- .../json/primitives/ValueClassPrim.scala | 179 - .../json/structures/Eithers.scala | 96 - .../json/structures/Model.scala | 55 - .../json/structures/SealedTraits.scala | 104 - .../json/structures/TryAndCapture.scala | 60 - .../json/structures/TypeMembers.scala | 92 - .../structures/UnionsAndIntersections.scala | 111 - .../co.blocke.scalajack/json4s/AnyColl.scala | 77 - .../co.blocke.scalajack/json4s/Custom.scala | 90 - .../json4s/Json4sSpec.scala | 238 -- .../co.blocke.scalajack/json4s/Model.scala | 118 - .../co.blocke.scalajack/json4s/Parsing.scala | 103 - .../json4s/ScalaPrim.scala | 154 - .../yaml/collections/AnyColl.scala | 115 - .../yaml/mapkeys/ClassPrimKeys.scala | 271 -- .../yaml/mapkeys/JavaPrimKeys.scala | 357 -- .../yaml/mapkeys/ListCollKeys.scala | 212 -- .../yaml/mapkeys/MapCollKeys.scala | 311 -- .../yaml/mapkeys/Model.scala | 175 - .../yaml/mapkeys/ScalaPrimKeys.scala | 308 -- .../yaml/mapkeys/TupleCollKeys.scala | 450 --- .../yaml/mapkeys/ValueClassKeys.scala | 569 ---- .../yaml/misc/ReadWriterSpec.scala | 95 - .../yaml/misc/TypeMembers.scala | 164 - .../yaml/misc/YamlFlavorSpec.scala | 89 - .../yaml/parameters/ClassParams.scala | 170 - .../yaml/parameters/Model.scala | 57 - .../yaml/parameters/TraitParams.scala | 161 - .../yaml/primitives.plain/Misc.scala | 84 - .../yaml/primitives.plain/Model.scala | 114 - .../yaml/primitives.plain/TryAndCapture.scala | 119 - .../primitives.plain/ValueClassPrim.scala | 34 - .../yaml/primitives/AnyPrim.scala | 163 - .../yaml/primitives/JavaPrim.scala | 400 --- .../yaml/primitives/Model.scala | 97 - .../yaml/primitives/ScalaPrim.scala | 424 --- .../yaml/primitives/TimePrim.scala | 321 -- .../yaml/primitives/ValueClassPrim.scala | 161 - 187 files changed, 297 insertions(+), 25625 deletions(-) delete mode 100644 TODO.txt delete mode 100644 rnd/build.sbt delete mode 100644 rnd/project/build.properties delete mode 100644 rnd/project/metals.sbt delete mode 100644 rnd/project/plugins.sbt delete mode 100644 rnd/src/main/java/co/blocke/scalajack/ByteArrayAccess.java delete mode 100644 rnd/src/main/scala/co.blocke.scalajack/JsonWriter.scala delete mode 100644 rnd/src/main/scala/co.blocke.scalajack/JsonWriterException.scala delete mode 100644 rnd/src/main/scala/co.blocke.scalajack/Run.scala delete mode 100644 rnd/src/main/scala/co.blocke.scalajack/WriterConfig.scala delete mode 100644 src/main/scala/co.blocke.scalajack/json/readers/ClassReader.scalax delete mode 100644 src/main/scala/co.blocke.scalajack/json/readers/CollectionReader.scalax delete mode 100644 src/main/scala/co.blocke.scalajack/json/readers/EnumReader.scalax delete mode 100644 src/main/scala/co.blocke.scalajack/json/readers/MiscReader.scalax delete mode 100644 src/main/scala/co.blocke.scalajack/json/readers/PrimitiveReader.scalax delete mode 100644 src/main/scala/co.blocke.scalajack/json/writing/WriteCodec.scalax rename src/test/scala/co.blocke.scalajack/json/collections/{SeqSetArraySpec.scalax => SeqSetArraySpec.scala} (58%) delete mode 100644 src_old/main/java/co/blocke/scalajack/Change.java delete mode 100644 src_old/main/java/co/blocke/scalajack/Collection.java delete mode 100644 src_old/main/java/co/blocke/scalajack/DBKey.java delete mode 100644 src_old/main/java/co/blocke/scalajack/Ignore.java delete mode 100644 src_old/main/java/co/blocke/scalajack/JavaStuff.java delete mode 100644 src_old/main/java/co/blocke/scalajack/Optional.java delete mode 100644 src_old/main/scala/co.blocke.scalajack/Converters.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/Main.scalax delete mode 100644 src_old/main/scala/co.blocke.scalajack/Performance.scalax delete mode 100644 src_old/main/scala/co.blocke.scalajack/SJCapture.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/ScalaJack.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/delimited/DelimitedEitherTypeAdapterFactory.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/delimited/DelimitedFlavor.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/delimited/DelimitedOptionTypeAdapterFactory.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/delimited/DelimitedParser.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/delimited/DelimitedWriter.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/json/JsonFlavor.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/json/JsonParser.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/json/JsonWriter.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/json4s/JValueBuilder.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/json4s/Json4sFlavor.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/json4s/Json4sParser.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/json4s/Json4sWriter.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/json4s/StringWrapTypeAdapter.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/model/ClassFieldMember.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/model/HintValueModifier.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/model/JackFlavor.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/model/LazyTypeAdapter.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/model/Parser.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/model/StringBuilder.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/model/TypeAdapaterCache.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/model/TypeAdapter.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/model/TypeAdapterFactory.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/model/ViewSplice.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/model/Writer.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/typeadapter/AnyTypeAdapter.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/typeadapter/ArrayTypeAdatper.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/typeadapter/CollectionTypeAdapter.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/typeadapter/EitherTypeAdapter.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/typeadapter/EnumTypeAdapter.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/typeadapter/FallbackTypeAdapter.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/typeadapter/IntersectionTypeAdapter.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/typeadapter/JavaPrimitives.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/typeadapter/MaybeStringWrapTypeAadapter.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/typeadapter/OptionTypeAdapter.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/typeadapter/PermissiveJavaPrimitives.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/typeadapter/PermissiveScalaPrimitives.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/typeadapter/ScalaPrimitives.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/typeadapter/SealedTraitTypeAdapter.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/typeadapter/StringWrapTypeAdapter.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/typeadapter/TimePrimitives.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/typeadapter/TraitTypeAdapter.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/typeadapter/TryTypeAdapter.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/typeadapter/TupleTypeAdapter.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/typeadapter/UUIDTypeAdapter.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/typeadapter/UnionTypeAdapter.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/typeadapter/ValueClassTypeAdapter.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/typeadapter/classes/CaseClassTypeAdapter.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/typeadapter/classes/ClassTypeAdapterBase.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/typeadapter/classes/JavaClassTypeAdapter.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/typeadapter/classes/NonCaseClassTypeAdapter.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/typeadapter/classes/ScalaClassTypeAdapter.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/typeadapter/classes/ScalaClassTypeAdapterFactory.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/typeadapter/collection/JavaMapLikeTypeAdapter.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/typeadapter/collection/JavaSeqLikeTypeAdapter.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/typeadapter/collection/JavaStackTypeAdapter.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/typeadapter/collection/MapLikeTypeAdapter.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/typeadapter/collection/SeqLikeTypeAdapter.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/util/BijectiveFunction.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/util/FixFloat.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/yaml/YamlBuilder.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/yaml/YamlFlavor.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/yaml/YamlParser.scala delete mode 100644 src_old/main/scala/co.blocke.scalajack/yaml/YamlWriter.scala delete mode 100644 src_old/test/java/co/blocke/scalajack/JavaArray.java delete mode 100644 src_old/test/java/co/blocke/scalajack/JavaCap.java delete mode 100644 src_old/test/java/co/blocke/scalajack/JavaEnum.java delete mode 100644 src_old/test/java/co/blocke/scalajack/JavaSimpleBase.java delete mode 100644 src_old/test/java/co/blocke/scalajack/JavaSimpleChild.java delete mode 100644 src_old/test/java/co/blocke/scalajack/Maybe.java delete mode 100644 src_old/test/java/co/blocke/scalajack/Maybe2.java delete mode 100644 src_old/test/java/co/blocke/scalajack/OnSetter.java delete mode 100644 src_old/test/java/co/blocke/scalajack/Temperature.java delete mode 100644 src_old/test/java/co/blocke/scalajack/Unsupported.java delete mode 100644 src_old/test/scala/co.blocke.scalajack/ConverterSpec.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/JsonDiff.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/JsonMatcher.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/TestUtil.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/delimited/DelimitedSpec.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/delimited/Model.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json/collections/AnyCollections.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json/collections/Arrays.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json/collections/Maps.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json/collections/Model.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json/collections/Options.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json/collections/Seqs.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json/collections/Tuples.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json/custom/CustomAdapter.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json/custom/CustomTypeHints.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json/custom/Model.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json/custom/ParseOrElse.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json/mapkeys/ClassPrimKeys.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json/mapkeys/JavaPrimKeys.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json/mapkeys/ListCollKeys.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json/mapkeys/MapCollKeys.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json/mapkeys/Model.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json/mapkeys/ScalaPrimKeys.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json/mapkeys/TupleCollKeys.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json/mapkeys/ValueClassKeys.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json/parameters/ClassParams.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json/parameters/Model.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json/parameters/TraitParams.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json/plainclass/Inheritance.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json/plainclass/Misc.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json/plainclass/Model.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json/plainclass/TryAndCapture.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json/plainclass/ValueClass.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json/primitives/AnyPrim.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json/primitives/Enums.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json/primitives/JavaPrim.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json/primitives/Model.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json/primitives/PermissivePrimitives.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json/primitives/ScalaPrim.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json/primitives/TimePrim.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json/primitives/ValueClassPrim.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json/structures/Eithers.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json/structures/Model.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json/structures/SealedTraits.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json/structures/TryAndCapture.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json/structures/TypeMembers.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json/structures/UnionsAndIntersections.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json4s/AnyColl.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json4s/Custom.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json4s/Json4sSpec.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json4s/Model.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json4s/Parsing.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/json4s/ScalaPrim.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/yaml/collections/AnyColl.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/ClassPrimKeys.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/JavaPrimKeys.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/ListCollKeys.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/MapCollKeys.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/Model.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/ScalaPrimKeys.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/TupleCollKeys.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/ValueClassKeys.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/yaml/misc/ReadWriterSpec.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/yaml/misc/TypeMembers.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/yaml/misc/YamlFlavorSpec.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/yaml/parameters/ClassParams.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/yaml/parameters/Model.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/yaml/parameters/TraitParams.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/yaml/primitives.plain/Misc.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/yaml/primitives.plain/Model.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/yaml/primitives.plain/TryAndCapture.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/yaml/primitives.plain/ValueClassPrim.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/yaml/primitives/AnyPrim.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/yaml/primitives/JavaPrim.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/yaml/primitives/Model.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/yaml/primitives/ScalaPrim.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/yaml/primitives/TimePrim.scala delete mode 100644 src_old/test/scala/co.blocke.scalajack/yaml/primitives/ValueClassPrim.scala diff --git a/README.md b/README.md index 16632ebc..e59c8e00 100644 --- a/README.md +++ b/README.md @@ -1,84 +1,78 @@ - # ScalaJack [![license](https://img.shields.io/github/license/mashape/apistatus.svg?maxAge=86400)](https://opensource.org/licenses/MIT) -[![Maven Central](https://maven-badges.herokuapp.com/maven-central/co.blocke/scalajack_3/badge.svg)](https://search.maven.org/artifact/co.blocke/scalajack_3/7.0.0/jar) -ScalaJack 7 is an all-new ScalaJack implmenation built on Scala 3. For Scala 2.13 ScalaJack, please use (frozen) version 6.2.0. ScalaJack 7 is built on JDK 13+. +[![Maven Central](https://maven-badges.herokuapp.com/maven-central/co.blocke/scalajack_3/badge.svg)](https://search.maven.org/artifact/co.blocke/scalajack_3/8.0.0/jar) + +ScalaJack 8 is an all-new ScalaJack implemenation built on Scala 3. For Scala 2.13 ScalaJack, please use (frozen) version 6.2.0. ScalaJack 8 is built using Scala 3.4.1 on JDK 21 LTS version. -ScalaJack is a very fast, seamless serialization engine for JSON, and other protocols, designed to require the minimum amount of help possible when serializing a class. +ScalaJack is a very fast, seamless serialization engine for JSON designed to require a bare minimum of extra code to serialize a class. Advanced Features: - - Handles tuples - - 'Any' support - - Handles default values for case class fields - - Rich configuration of trait type hint/value - - Supports value classes - - Sealed trait-style enumerations - - Extensible to other encodings (JSON, CSV, MongoDB, and DynamoDB are provided by ScalaJack, but you can roll your own too!) -## Use +- Handles tuples +- 'Any' support +- Handles default values for case class fields +- Rich configuration of trait type hint/value +- Supports value classes +- Sealed trait-style enumerations +## Use ScalaJack is extremely simple to use. Include the following in your build.sbt: ``` - libraryDependencies ++= Seq("co.blocke" %% "scalajack" % SJ_VERSION) -``` - -To use the **highly-recommended** reflection compiler plug-in, add to build.sbt: -``` -addCompilerPlugin("co.blocke" %% "scala-reflection" % VERSION) -``` -where VERSION is the latest scala-reflection version found by looking at the 'maven central' badge from this repo: [www.blocke.co/scala-reflection](http://www.blocke.co/scala-reflection) - -If you want to use the optional MongoDB serialization support include this as well: -``` - libraryDependencies ++= Seq("co.blocke" %% "scalajack_mongo" % SJ_VERSION) -``` - -DynamoDB helpers are available here: -``` - libraryDependencies ++= Seq("co.blocke" %% "scalajack_dynamo" % SJ_VERSION) +libraryDependencies ++= Seq("co.blocke" %% "scalajack" % SJ_VERSION) ``` -where SJ_VERSION is this version of ScalaJack (see 'maven central' badge of this repo). - -Now you're good to go! Let's use ScalaJack in your project to serialize/de-serialize a case class object into JSON: - +Now you're good to go! Let's use ScalaJack in your project to serialize/de-serialize a case class object into JSON: ```scala import co.blocke.scalajack._ case class Person(name: String, age: Int) -val sj = ScalaJack() -val js = sj.render(Person("Mike",34)) // js == """{"name":"Mike","age":34}""" -val inst = sj.read[Person](js) // re-constitutes original Person +given sjPerson: ScalaJack[Person] = sjCodecOf[Person] // create a re-usable Person codec +... +val inst = Person("Mike",34) +val js = sjPerson.toJson(inst) // """{"name":"Mike","age":34}""" +val inst = sjPerson.fromJson(js) // re-constitutes original Person ``` - Couldn't be simpler! ### A word about performance... -Compared to pre-7.0 ScalaJack, which used Scala 2.x runtime reflection, ScalaJack is up to 30% faster in many cases when used with the highly-recommended scala-reflection compiler plugin. + +Compared to pre-8.0 ScalaJack, which used Scala 2.x runtime reflection, ScalaJack is dramatically faster in almost every case. How's this work? ScalaJack 8 uses macros, that at compile-time generate all the serialization code for you (the codecs). It's very much like writing hand-tooled, field-by-field serialization code yourself, except ScalaJack does it at compile-time. Wherever you see ```sjCodecOf``` is where the compiler will generate all the serialization code. **(That also means try not to use sjCodecOf more than once for any given class or you'll generate a lot of redundant code!)** + +You only need to worry about generating codecs for your top-most level classes. Some serialization libraries require all classes to be specifically called out. ScalaJack doesn't require this. For example: + +```scala +case class Dog(name: String, numLegs: Int) +case class Person(name: String, age: Int, dog: Dog) + +// create a re-usable Person codec (includes Dog for free!) +given sjPerson: ScalaJack[Person] = sjCodecOf[Person] +``` +In this example, the contained Dog class is automatically detected and genrated by ScalaJack, so if all you care about is Person, that's the only codec you need. ### A word about macros... -ScalaJack 7 uses Scala 3 macros to the fullest extent possible to do the hard work of reflecting on types. Macros impact the compile/test cycle in ways that are non-intuitive at first. Think of this example: + +ScalaJack 8 uses Scala 3 macros to the fullest extent possible to do the hard work of reflecting on types. Macros impact the compile/test cycle in ways that are non-intuitive at first. Think of this example: ```scala // File1.scala -case class Foo(name: String) +case class Foo(name: String) // File2.scala -val js = sj.read[Foo](someJson) +given sjFoo: ScalaJack[Foo] = sjCodecOf[Foo] +val js = sjFoo.fromJson(someJson) ``` -In a non-macro implementation (e.g. Scala 2 runtime reflection) if you update Foo in File1.scala you naturally expect sbt to re-compile this file, and anything that depends on Foo, and the changes will be picked up in your program, and all will be well. +In a non-macro program (e.g. something using Scala 2 runtime reflection) if you update Foo in File1.scala you naturally expect sbt to re-compile this file, and anything that depends on Foo, and the changes will be picked up in your program, and all will be well. -That's **not** necessarily what happens with macros! Remember, the macro code is run at compile-time. File2.scala needs to be re-compiled because the macro needs to be re-run to pick up your changes to Foo class in File1.scala. **Unfortunately sbt doesn't pick up this dependency!** If you don't know any better you'll just re-run your program after a change to File1.scala, like normal, and get a **spectacular exception with exotic errors** that won't mean much to you. The solution is you need to also recompile File2.scala. +That's **not** necessarily what happens with macros! Remember, the macro code is run at compile-time. File2.scala needs to be re-compiled because the macro needs to be re-run to pick up your changes to Foo class in File1.scala. **Unfortunately sbt doesn't pick up this dependency!** If you don't know any better you'll just re-run your program after a change to File1.scala, like normal, and get a **spectacular exception with exotic errors** that won't mean much to you. The simple, but non-intuitive, solution is you need to also recompile File2.scala. This means you will be doing more re-compiling with macro-based code than you would without the macros. It's an unfortunate cost of inconvenience and time, but the payoff is a *dramatic* gain in speed at runtime, and in the case of reflection in Scala 3, using macros is really the only way to accomplish reflection. ## Features - * [Case Classes and Traits](doc/classesAndTraits.md) * [Non-Case Classes and Java Class Support](doc/noncase.md) * [Re-name Case Class Fields](doc/mapname.md) @@ -98,14 +92,9 @@ This means you will be doing more re-compiling with macro-based code than you wo * [ScalaJack Configuration](doc/config.md) * [Gimme Speed!](doc/speed.md) -Non-JSON Formats: -* [YAML](doc/yaml.md) -* [MongoDB](doc/mongo.md) -* [Delimited (e.g. CSV)](doc/delimited.md) -* [DynamoDB](doc/dynamo.md) -* [Json4s](doc/json4s.md) - ### Notes: -* 7.0.3 -- Rebuild on Scala 3.2.1 -* 7.0.1 -- GA release of ScalaJack 7 for Scala 3. -* 7.0.0-M2 -- Initial release for Scala 3 + +* 8.0.0 -- Rebuild on Scala 3.4.1 and major refactor of ScalaJack 7.0 +* 7.0.3 -- Rebuild on Scala 3.2.1 +* 7.0.1 -- GA release of ScalaJack 7 for Scala 3. +* 7.0.0-M2 -- Initial release for Scala 3 \ No newline at end of file diff --git a/TODO.txt b/TODO.txt deleted file mode 100644 index 7a754c6a..00000000 --- a/TODO.txt +++ /dev/null @@ -1,52 +0,0 @@ -Path to Performance -------------------- - - Fature Support: - Code Test - [*] [W] - Primitive types - [*] [W] - Simple Types - - [*] [W] - Seq/Set/Array - [*] [W] - Map - [*] [W] - Java Collections - [*] [W] - Java Map - [*] [W] - Tuple - - [*] [W] - Scala case class - [*] [W] - Scala non-case class - [*] [W] - Java class - [*] [W] - SelfRef - [*] [W] - Sealed Trait - [*] [W] - Sealed Abstract Class (handle like sealed trait....) - - [*] [W] - Option/Optional - [*] [W] - Either - [*] [W] - Intersection - [*] [W] - Union - [*] [W] - Enum (tested as part of Map tests) - [*] [W] - Enumeration (tested as part of Map tests) - [*] [W] - Java Enum (tested as part of Map tests) - [*] [W] - Alias type - [*] [X] - Object (recommend do not use except for case objects for sealed traits/classes) - [*] [W] - TryRef - [*] [X] - Unknown (throw exception) - [*] [X] - Scala 2 (throw exception) - [*] [X] - TypeSymbol (throw exception) - [*] [W] - Value class (tested in MapSpec) - - [*] [W] - Any type -- No longer supported. Can't generate codec code for unknown types - [X] [X] - SJCapture support (optional by config) -- unnecessary! - [*] [W] - NeoType integration - - [*] [W] - type hint label mapping - [*] [W] - type hint value mapping - [*] [W] - @Change - field name change - - [ ] -- Streaming JSON write support - [ ] -- BigJSON support (eg. multi-gig file) - - - - [*] -- Handle required fields, incl generating mapping fns for err: - [ ] -- Handle special chars in strings (Json parsing) - \ No newline at end of file diff --git a/rnd/build.sbt b/rnd/build.sbt deleted file mode 100644 index 705aed1f..00000000 --- a/rnd/build.sbt +++ /dev/null @@ -1,53 +0,0 @@ -inThisBuild(List( - organization := "co.blocke", - homepage := Some(url("https://github.com/gzoller/ScalaJack")), - licenses := List("MIT" -> url("https://opensource.org/licenses/MIT")), - developers := List( - Developer( - "gzoller", - "Greg Zoller", - "gzoller@blocke.co", - url("http://www.blocke.co") - ) - ) -)) - -name := "rnd" -ThisBuild / organization := "co.blocke" -ThisBuild / scalaVersion := "3.3.0" - -lazy val root = project - .in(file(".")) - .settings(settings) - .settings( - name := "serializer", - Compile / packageBin / mappings += { - (baseDirectory.value / "plugin.properties") -> "plugin.properties" - }, - doc := null, // disable dottydoc for now - Compile / doc / sources := Seq(), - //sources in (Compile, doc) := Seq(), - Test / parallelExecution := false, - libraryDependencies ++= Seq( - "org.scalatest" %% "scalatest" % "3.2.17" % Test - ) - ) - -//========================== -// Settings -//========================== -lazy val settings = Seq( - javacOptions ++= Seq("-source", "1.8", "-target", "1.8"), - scalacOptions ++= compilerOptions -) - -lazy val compilerOptions = Seq( - "-unchecked", - "-feature", - "-language:implicitConversions", - "-deprecation", - // "-explain", - "-encoding", - "utf8" -) - diff --git a/rnd/project/build.properties b/rnd/project/build.properties deleted file mode 100644 index 27430827..00000000 --- a/rnd/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version=1.9.6 diff --git a/rnd/project/metals.sbt b/rnd/project/metals.sbt deleted file mode 100644 index cbb25c6a..00000000 --- a/rnd/project/metals.sbt +++ /dev/null @@ -1,6 +0,0 @@ -// DO NOT EDIT! This file is auto-generated. - -// This file enables sbt-bloop to create bloop config files. - -addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.5.11") - diff --git a/rnd/project/plugins.sbt b/rnd/project/plugins.sbt deleted file mode 100644 index 7d517ef3..00000000 --- a/rnd/project/plugins.sbt +++ /dev/null @@ -1 +0,0 @@ -addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.2") diff --git a/rnd/src/main/java/co/blocke/scalajack/ByteArrayAccess.java b/rnd/src/main/java/co/blocke/scalajack/ByteArrayAccess.java deleted file mode 100644 index 58e4313f..00000000 --- a/rnd/src/main/java/co/blocke/scalajack/ByteArrayAccess.java +++ /dev/null @@ -1,46 +0,0 @@ -package co.blocke.scalajack; - -import java.lang.invoke.MethodHandles; -import java.lang.invoke.VarHandle; -import java.nio.ByteOrder; - -class ByteArrayAccess { // FIXME: Use Java wrapper as w/a for missing support of @PolymorphicSignature methods in Scala 3, see: https://github.com/lampepfl/dotty/issues/11332 - private static final VarHandle VH_LONG = - MethodHandles.byteArrayViewVarHandle(long[].class, ByteOrder.LITTLE_ENDIAN); - private static final VarHandle VH_INT = - MethodHandles.byteArrayViewVarHandle(int[].class, ByteOrder.LITTLE_ENDIAN); - private static final VarHandle VH_SHORT = - MethodHandles.byteArrayViewVarHandle(short[].class, ByteOrder.LITTLE_ENDIAN); - private static final VarHandle VH_LONG_REVERSED = - MethodHandles.byteArrayViewVarHandle(long[].class, ByteOrder.BIG_ENDIAN); - private static final VarHandle VH_INT_REVERSED = - MethodHandles.byteArrayViewVarHandle(int[].class, ByteOrder.BIG_ENDIAN); - - static void setLong(byte[] buf, int pos, long value) { - VH_LONG.set(buf, pos, value); - } - - static long getLong(byte[] buf, int pos) { - return (long) VH_LONG.get(buf, pos); - } - - static void setInt(byte[] buf, int pos, int value) { - VH_INT.set(buf, pos, value); - } - - static int getInt(byte[] buf, int pos) { - return (int) VH_INT.get(buf, pos); - } - - static void setShort(byte[] buf, int pos, short value) { - VH_SHORT.set(buf, pos, value); - } - - static void setLongReversed(byte[] buf, int pos, long value) { - VH_LONG_REVERSED.set(buf, pos, value); - } - - static int getIntReversed(byte[] buf, int pos) { - return (int) VH_INT_REVERSED.get(buf, pos); - } -} \ No newline at end of file diff --git a/rnd/src/main/scala/co.blocke.scalajack/JsonWriter.scala b/rnd/src/main/scala/co.blocke.scalajack/JsonWriter.scala deleted file mode 100644 index 61b621a0..00000000 --- a/rnd/src/main/scala/co.blocke.scalajack/JsonWriter.scala +++ /dev/null @@ -1,3000 +0,0 @@ -package co.blocke.scalajack - -import java.io.OutputStream -import java.math.BigInteger -import java.nio.charset.StandardCharsets -import java.nio.{BufferOverflowException, ByteBuffer} -import java.time._ -import java.util.UUID -import JsonWriter._ -import scala.annotation.tailrec -import scala.{specialized => sp} - -/** - * A writer for iterative serialization of JSON keys and values. - * - * @param buf an internal buffer for writing JSON data - * @param count the current position in the internal buffer - * @param limit the last position in the internal buffer - * @param indention the current indention level - * @param comma a flag indicating if the next element should be preceded by comma - * @param disableBufGrowing a flag indicating if growing of the internal buffer is disabled - * @param bbuf a byte buffer for writing JSON data - * @param out the output stream for writing JSON data - * @param config a writer configuration - */ -final class JsonWriter ( - private[this] var buf: Array[Byte] = new Array[Byte](32768), - private[this] var count: Int = 0, - private[this] var limit: Int = 32768, - private[this] var indention: Int = 0, - private[this] var comma: Boolean = false, - private[this] var disableBufGrowing: Boolean = false, - private[this] var bbuf: ByteBuffer = null, - private[this] var out: OutputStream = null, - private[this] var config: WriterConfig = null) { - - // Within any given thread, lets re-use this writer/buffer! Why re-allocate all this machinery many times? - def result = new String(buf, 0, count, StandardCharsets.UTF_8) - def reset = - count = 0 - comma = false - indention = 0 - - /** - * Writes a `Boolean` value as a JSON key. - * - * @param x the `Boolean` value to write - */ - def writeKey(x: Boolean): Unit = { - writeOptionalCommaAndIndentionBeforeKey() - writeBytes('"') - writeBoolean(x) - writeParenthesesWithColon() - } - - /** - * Writes a `Byte` value as a JSON key. - * - * @param x the `Byte` value to write - */ - def writeKey(x: Byte): Unit = { - writeOptionalCommaAndIndentionBeforeKey() - writeBytes('"') - writeByte(x) - writeParenthesesWithColon() - } - - /** - * Writes a `Char` value as a JSON key. - * - * @param x the `Char` value to write - * @throws JsonWriterException in case of `Char` value is a part of surrogate pair - */ - def writeKey(x: Char): Unit = { - writeOptionalCommaAndIndentionBeforeKey() - writeChar(x) - writeColon() - } - - /** - * Writes a `Short` value as a JSON key. - * - * @param x the `Short` value to write - */ - def writeKey(x: Short): Unit = { - writeOptionalCommaAndIndentionBeforeKey() - writeBytes('"') - writeShort(x) - writeParenthesesWithColon() - } - - /** - * Writes a `Int` value as a JSON key. - * - * @param x the `Int` value to write - */ - def writeKey(x: Int): Unit = { - writeOptionalCommaAndIndentionBeforeKey() - writeBytes('"') - writeInt(x) - writeParenthesesWithColon() - } - - /** - * Writes a `Long` value as a JSON key. - * - * @param x the `Long` value to write - */ - def writeKey(x: Long): Unit = { - writeOptionalCommaAndIndentionBeforeKey() - writeBytes('"') - writeLong(x) - writeParenthesesWithColon() - } - - /** - * Writes a `Float` value as a JSON key. - * - * @param x the `Float` value to write - * @throws JsonWriterException if the value is non-finite - */ - def writeKey(x: Float): Unit = { - writeOptionalCommaAndIndentionBeforeKey() - writeBytes('"') - writeFloat(x) - writeParenthesesWithColon() - } - - /** - * Writes a `Double` value as a JSON key. - * - * @param x the `Double` value to write - * @throws JsonWriterException if the value is non-finite - */ - def writeKey(x: Double): Unit = { - writeOptionalCommaAndIndentionBeforeKey() - writeBytes('"') - writeDouble(x) - writeParenthesesWithColon() - } - - /** - * Writes a `BigInt` value as a JSON key. - * - * @param x the `BigInt` value to write - */ - def writeKey(x: BigInt): Unit = { - writeOptionalCommaAndIndentionBeforeKey() - writeBytes('"') - if (x.isValidLong) writeLong(x.longValue) - else writeBigInteger(x.bigInteger, null) - writeParenthesesWithColon() - } - - /** - * Writes a `BigDecimal` value as a JSON key. - * - * @param x the `BigDecimal` value to write - */ - def writeKey(x: BigDecimal): Unit = { - writeOptionalCommaAndIndentionBeforeKey() - writeBytes('"') - writeBigDecimal(x.bigDecimal) - writeParenthesesWithColon() - } - - /** - * Writes a [[java.util.UUID]] value as a JSON key. - * - * @param x the [[java.util.UUID]] value to write - */ - def writeKey(x: UUID): Unit = { - writeOptionalCommaAndIndentionBeforeKey() - writeUUID(x.getMostSignificantBits, x.getLeastSignificantBits) - writeColon() - } - - /** - * Writes a `String` value as a JSON key. - * - * @param x the `String` value to write - * @throws JsonWriterException if the provided string has an illegal surrogate pair - */ - def writeKey(x: String): Unit = count = { - val indention = this.indention - var pos = ensureBufCapacity(indention + 10) - val buf = this.buf - if (comma) { - comma = false - buf(pos) = ',' - pos += 1 - if (indention != 0) pos = writeIndention(buf, pos, indention) - } - buf(pos) = '"' - pos += 1 - pos = writeString(x, 0, pos, Math.min(x.length, limit - pos - 1) + pos, escapedChars) - if (pos + 4 >= limit) pos = flushAndGrowBuf(4, pos) - ByteArrayAccess.setInt(this.buf, pos, 0x203A22) - if (indention > 0) pos += 1 - pos + 2 - } - - /** - * Writes a `String` value that doesn't require encoding or escaping as a JSON key. - * - * @note Use [[JsonWriter.isNonEscapedAscii]] for validation if the string is eligable for writing by this method. - * - * @param x the `String` value to write - */ - def writeNonEscapedAsciiKey(x: String): Unit = { - val len = x.length - val indention = this.indention - val required = indention + len + 10 - if (required <= config.preferredBufSize) { - var pos = ensureBufCapacity(required) - val buf = this.buf - if (comma) { - comma = false - buf(pos) = ',' - pos += 1 - if (indention != 0) pos = writeIndention(buf, pos, indention) - } - buf(pos) = '"' - pos += 1 - var i = 0 - while (i < len) { - buf(pos) = x.charAt(i).toByte - pos += 1 - i += 1 - } - ByteArrayAccess.setInt(buf, pos, 0x203A22) - if (indention > 0) pos += 1 - count = pos + 2 - } else writeLongNonEscapedAsciiKey(x) - } - - /** - * Writes a [[java.time.Duration]] value as a JSON key. - * - * @param x the [[java.time.Duration]] value to write - */ - def writeKey(x: Duration): Unit = { - writeOptionalCommaAndIndentionBeforeKey() - writeDuration(x) - writeColon() - } - - /** - * Writes a [[java.time.Duration]] value as a JSON key. - * - * @param x the [[java.time.Duration]] value to write - */ - def writeKey(x: Instant): Unit = { - writeOptionalCommaAndIndentionBeforeKey() - writeInstant(x) - writeColon() - } - - /** - * Writes a [[java.time.LocalDate]] value as a JSON key. - * - * @param x the [[java.time.LocalDate]] value to write - */ - def writeKey(x: LocalDate): Unit = { - writeOptionalCommaAndIndentionBeforeKey() - writeLocalDate(x) - writeColon() - } - - /** - * Writes a [[java.time.LocalDateTime]] value as a JSON key. - * - * @param x the [[java.time.LocalDateTime]] value to write - */ - def writeKey(x: LocalDateTime): Unit = { - writeOptionalCommaAndIndentionBeforeKey() - writeLocalDateTime(x) - writeColon() - } - - /** - * Writes a [[java.time.LocalTime]] value as a JSON key. - * - * @param x the [[java.time.LocalTime]] value to write - */ - def writeKey(x: LocalTime): Unit = { - writeOptionalCommaAndIndentionBeforeKey() - writeLocalTime(x) - writeColon() - } - - /** - * Writes a [[java.time.MonthDay]] value as a JSON key. - * - * @param x the [[java.time.MonthDay]] value to write - */ - def writeKey(x: MonthDay): Unit = { - writeOptionalCommaAndIndentionBeforeKey() - writeMonthDay(x) - writeColon() - } - - /** - * Writes a [[java.time.OffsetDateTime]] value as a JSON key. - * - * @param x the [[java.time.OffsetDateTime]] value to write - */ - def writeKey(x: OffsetDateTime): Unit = { - writeOptionalCommaAndIndentionBeforeKey() - writeOffsetDateTime(x) - writeColon() - } - - /** - * Writes a [[java.time.OffsetTime]] value as a JSON key. - * - * @param x the [[java.time.OffsetTime]] value to write - */ - def writeKey(x: OffsetTime): Unit = { - writeOptionalCommaAndIndentionBeforeKey() - writeOffsetTime(x) - writeColon() - } - - /** - * Writes a [[java.time.Period]] value as a JSON key. - * - * @param x the [[java.time.Period]] value to write - */ - def writeKey(x: Period): Unit = { - writeOptionalCommaAndIndentionBeforeKey() - writePeriod(x) - writeColon() - } - - /** - * Writes a [[java.time.Year]] value as a JSON key. - * - * @param x the [[java.time.Year]] value to write - */ - def writeKey(x: Year): Unit = { - writeOptionalCommaAndIndentionBeforeKey() - writeYear(x) - writeColon() - } - - /** - * Writes a [[java.time.YearMonth]] value as a JSON key. - * - * @param x the [[java.time.YearMonth]] value to write - */ - def writeKey(x: YearMonth): Unit = { - writeOptionalCommaAndIndentionBeforeKey() - writeYearMonth(x) - writeColon() - } - - /** - * Writes a [[java.time.ZonedDateTime]] value as a JSON key. - * - * @param x the [[java.time.ZonedDateTime]] value to write - */ - def writeKey(x: ZonedDateTime): Unit = { - writeOptionalCommaAndIndentionBeforeKey() - writeZonedDateTime(x) - writeColon() - } - - /** - * Writes a [[java.time.ZoneId]] value as a JSON key. - * - * @param x the [[java.time.ZoneId]] value to write - */ - def writeKey(x: ZoneId): Unit = { - writeOptionalCommaAndIndentionBeforeKey() - writeZoneId(x) - writeColon() - } - - /** - * Writes a [[java.time.ZoneOffset]] value as a JSON key. - * - * @param x the [[java.time.ZoneOffset]] value to write - */ - def writeKey(x: ZoneOffset): Unit = { - writeOptionalCommaAndIndentionBeforeKey() - writeZoneOffset(x) - writeColon() - } - - /** - * Throws a [[JsonWriterException]] with the given error message. - * - * @param msg the error message - * @throws JsonWriterException always - */ - def encodeError(msg: String): Nothing = - throw new JsonWriterException(msg, null, config.throwWriterExceptionWithStackTrace) - - /** - * Writes a `BigDecimal` value as a JSON value. - * - * @param x the `BigDecimal` value to write - */ - def writeVal(x: BigDecimal): Unit = { - writeOptionalCommaAndIndentionBeforeValue() - writeBigDecimal(x.bigDecimal) - } - - /** - * Writes a `BigInt` value as a JSON value. - * - * @param x the `BigInt` value to write - */ - def writeVal(x: BigInt): Unit = { - writeOptionalCommaAndIndentionBeforeValue() - if (x.isValidLong) writeLong(x.longValue) - else writeBigInteger(x.bigInteger, null) - } - - /** - * Writes a [[java.util.UUID]] value as a JSON value. - * - * @param x the [[java.util.UUID]] value to write - */ - def writeVal(x: UUID): Unit = { - writeOptionalCommaAndIndentionBeforeValue() - writeUUID(x.getMostSignificantBits, x.getLeastSignificantBits) - } - - /** - * Writes a `String` value as a JSON value. - * - * @param x the `String` value to write - * @throws JsonWriterException if the provided string has an illegal surrogate pair - */ - def writeVal(x: String): Unit = count = { - val indention = this.indention - var pos = ensureBufCapacity(indention + 10) - if (comma) { - buf(pos) = ',' - pos += 1 - if (indention != 0) pos = writeIndention(buf, pos, indention) - } else comma = true - buf(pos) = '"' - pos += 1 - pos = writeString(x, 0, pos, Math.min(x.length, limit - pos - 1) + pos, escapedChars) - buf(pos) = '"' - pos + 1 - } - - /** - * Writes a `String` value that doesn't require encoding or escaping as a JSON value. - * - * @note Use [[JsonWriter.isNonEscapedAscii]] for validation if the string is eligable for writing by this method. - * - * @param x the `String` value to write - */ - def writeNonEscapedAsciiVal(x: String): Unit = { - val len = x.length - val indention = this.indention - val required = indention + len + 10 - if (required <= config.preferredBufSize) { - var pos = ensureBufCapacity(required) - val buf = this.buf - if (comma) { - buf(pos) = ',' - pos += 1 - if (indention != 0) pos = writeIndention(buf, pos, indention) - } else comma = true - buf(pos) = '"' - pos += 1 - var i = 0 - while (i < len) { - buf(pos) = x.charAt(i).toByte - pos += 1 - i += 1 - } - buf(pos) = '"' - count = pos + 1 - } else writeLongNonEscapedAsciiVal(x) - } - - /** - * Writes a [[java.time.Duration]] value as a JSON value. - * - * @param x the [[java.time.Duration]] value to write - */ - def writeVal(x: Duration): Unit = { - writeOptionalCommaAndIndentionBeforeValue() - writeDuration(x) - } - - /** - * Writes a [[java.time.Instant]] value as a JSON value. - * - * @param x the [[java.time.Instant]] value to write - */ - def writeVal(x: Instant): Unit = { - writeOptionalCommaAndIndentionBeforeValue() - writeInstant(x) - } - - /** - * Writes a [[java.time.LocalDate]] value as a JSON value. - * - * @param x the [[java.time.LocalDate]] value to write - */ - def writeVal(x: LocalDate): Unit = { - writeOptionalCommaAndIndentionBeforeValue() - writeLocalDate(x) - } - - /** - * Writes a [[java.time.LocalDateTime]] value as a JSON value. - * - * @param x the [[java.time.LocalDateTime]] value to write - */ - def writeVal(x: LocalDateTime): Unit = { - writeOptionalCommaAndIndentionBeforeValue() - writeLocalDateTime(x) - } - - /** - * Writes a [[java.time.LocalTime]] value as a JSON value. - * - * @param x the [[java.time.LocalTime]] value to write - */ - def writeVal(x: LocalTime): Unit = { - writeOptionalCommaAndIndentionBeforeValue() - writeLocalTime(x) - } - - /** - * Writes a [[java.time.MonthDay]] value as a JSON value. - * - * @param x the [[java.time.MonthDay]] value to write - */ - def writeVal(x: MonthDay): Unit = { - writeOptionalCommaAndIndentionBeforeValue() - writeMonthDay(x) - } - - /** - * Writes a [[java.time.OffsetDateTime]] value as a JSON value. - * - * @param x the [[java.time.OffsetDateTime]] value to write - */ - def writeVal(x: OffsetDateTime): Unit = { - writeOptionalCommaAndIndentionBeforeValue() - writeOffsetDateTime(x) - } - - /** - * Writes a [[java.time.OffsetTime]] value as a JSON value. - * - * @param x the [[java.time.OffsetTime]] value to write - */ - def writeVal(x: OffsetTime): Unit = { - writeOptionalCommaAndIndentionBeforeValue() - writeOffsetTime(x) - } - - /** - * Writes a [[java.time.Period]] value as a JSON value. - * - * @param x the [[java.time.Period]] value to write - */ - def writeVal(x: Period): Unit = { - writeOptionalCommaAndIndentionBeforeValue() - writePeriod(x) - } - - /** - * Writes a [[java.time.Year]] value as a JSON value. - * - * @param x the [[java.time.Year]] value to write - */ - def writeVal(x: Year): Unit = { - writeOptionalCommaAndIndentionBeforeValue() - writeYear(x) - } - - /** - * Writes a [[java.time.YearMonth]] value as a JSON value. - * - * @param x the [[java.time.YearMonth]] value to write - */ - def writeVal(x: YearMonth): Unit = { - writeOptionalCommaAndIndentionBeforeValue() - writeYearMonth(x) - } - - /** - * Writes a [[java.time.ZonedDateTime]] value as a JSON value. - * - * @param x the [[java.time.ZonedDateTime]] value to write - */ - def writeVal(x: ZonedDateTime): Unit = { - writeOptionalCommaAndIndentionBeforeValue() - writeZonedDateTime(x) - } - - /** - * Writes a [[java.time.ZoneId]] value as a JSON value. - * - * @param x the [[java.time.ZoneId]] value to write - */ - def writeVal(x: ZoneId): Unit = { - writeOptionalCommaAndIndentionBeforeValue() - writeZoneId(x) - } - - /** - * Writes a [[java.time.ZoneOffset]] value as a JSON value. - * - * @param x the [[java.time.ZoneOffset]] value to write - */ - def writeVal(x: ZoneOffset): Unit = { - writeOptionalCommaAndIndentionBeforeValue() - writeZoneOffset(x) - } - - /** - * Writes a `Boolean` value as a JSON value. - * - * @param x the `Boolean` value to write - */ - def writeVal(x: Boolean): Unit = { - writeOptionalCommaAndIndentionBeforeValue() - writeBoolean(x) - } - - /** - * Writes a `Byte` value as a JSON value. - * - * @param x the `Byte` value to write - */ - def writeVal(x: Byte): Unit = { - writeOptionalCommaAndIndentionBeforeValue() - writeByte(x) - } - - /** - * Writes a `Short` value as a JSON value. - * - * @param x the `Short` value to write - */ - def writeVal(x: Short): Unit = { - writeOptionalCommaAndIndentionBeforeValue() - writeShort(x) - } - - /** - * Writes a `Char` value as a JSON key. - * - * @param x the `Char` value to write - * @throws JsonWriterException in case of `Char` value is a part of surrogate pair - */ - def writeVal(x: Char): Unit = { - writeOptionalCommaAndIndentionBeforeValue() - writeChar(x) - } - - /** - * Writes a `Int` value as a JSON value. - * - * @param x the `Int` value to write - */ - def writeVal(x: Int): Unit = { - writeOptionalCommaAndIndentionBeforeValue() - writeInt(x) - } - - /** - * Writes a `Long` value as a JSON value. - * - * @param x the `Long` value to write - */ - def writeVal(x: Long): Unit = { - writeOptionalCommaAndIndentionBeforeValue() - writeLong(x) - } - - /** - * Writes a `Float` value as a JSON value. - * - * @param x the `Float` value to write - * @throws JsonWriterException if the value is non-finite - */ - def writeVal(x: Float): Unit = { - writeOptionalCommaAndIndentionBeforeValue() - writeFloat(x) - } - - /** - * Writes a `Double` value as a JSON value. - * - * @param x the `Double` value to write - * @throws JsonWriterException if the value is non-finite - */ - def writeVal(x: Double): Unit = { - writeOptionalCommaAndIndentionBeforeValue() - writeDouble(x) - } - - /** - * Writes a `BigDecimal` value as a JSON string value. - * - * @param x the `BigDecimal` value to write - */ - def writeValAsString(x: BigDecimal): Unit = { - writeOptionalCommaAndIndentionBeforeValue() - writeBytes('"') - writeBigDecimal(x.bigDecimal) - writeBytes('"') - } - - /** - * Writes a `BigInt` value as a JSON string value. - * - * @param x the `BigInt` value to write - */ - def writeValAsString(x: BigInt): Unit = { - writeOptionalCommaAndIndentionBeforeValue() - writeBytes('"') - if (x.isValidLong) writeLong(x.longValue) - else writeBigInteger(x.bigInteger, null) - writeBytes('"') - } - - /** - * Writes a `Boolean` value as a JSON string value. - * - * @param x the `Boolean` value to write - */ - def writeValAsString(x: Boolean): Unit = { - writeOptionalCommaAndIndentionBeforeValue() - writeBytes('"') - writeBoolean(x) - writeBytes('"') - } - - /** - * Writes a `Byte` value as a JSON string value. - * - * @param x the `Byte` value to write - */ - def writeValAsString(x: Byte): Unit = { - writeOptionalCommaAndIndentionBeforeValue() - writeBytes('"') - writeByte(x) - writeBytes('"') - } - - /** - * Writes a `Short` value as a JSON string value. - * - * @param x the `Short` value to write - */ - def writeValAsString(x: Short): Unit = { - writeOptionalCommaAndIndentionBeforeValue() - writeBytes('"') - writeShort(x) - writeBytes('"') - } - - /** - * Writes a `Int` value as a JSON string value. - * - * @param x the `Int` value to write - */ - def writeValAsString(x: Int): Unit = { - writeOptionalCommaAndIndentionBeforeValue() - writeBytes('"') - writeInt(x) - writeBytes('"') - } - - /** - * Writes a `Long` value as a JSON string value. - * - * @param x the `Long` value to write - */ - def writeValAsString(x: Long): Unit = { - writeOptionalCommaAndIndentionBeforeValue() - writeBytes('"') - writeLong(x) - writeBytes('"') - } - - /** - * Writes a `Float` value as a JSON string value. - * - * @param x the `Float` value to write - * @throws JsonWriterException if the value is non-finite - */ - def writeValAsString(x: Float): Unit = { - writeOptionalCommaAndIndentionBeforeValue() - writeBytes('"') - writeFloat(x) - writeBytes('"') - } - - /** - * Writes a `Double` value as a JSON string value. - * - * @param x the `Double` value to write - * @throws JsonWriterException if the value is non-finite - */ - def writeValAsString(x: Double): Unit = { - writeOptionalCommaAndIndentionBeforeValue() - writeBytes('"') - writeDouble(x) - writeBytes('"') - } - - /** - * Writes a byte array as a JSON hexadecimal string value. - * - * @param bs the byte array to write - * @param lowerCase if `true`, outputs lowercase hexadecimal digits - */ - def writeBase16Val(bs: Array[Byte], lowerCase: Boolean): Unit = { - writeOptionalCommaAndIndentionBeforeValue() - val ds = - if (lowerCase) lowerCaseHexDigits - else upperCaseHexDigits - writeBase16Bytes(bs, ds) - } - - /** - * Writes a byte array as a JSON string value encoded in a base-64 format. - * - * @param bs the byte array to write - * @param doPadding if `true`, outputs padding characters (`=`) as needed - */ - def writeBase64Val(bs: Array[Byte], doPadding: Boolean): Unit = { - writeOptionalCommaAndIndentionBeforeValue() - writeBase64Bytes(bs, base64Digits, doPadding) - } - - /** - * Writes a byte array as a JSON string value encoded in a base-64 format for URLs. - * - * @param bs the byte array to write - * @param doPadding if `true`, outputs padding characters (`=`) as needed - */ - def writeBase64UrlVal(bs: Array[Byte], doPadding: Boolean): Unit = { - writeOptionalCommaAndIndentionBeforeValue() - writeBase64Bytes(bs, base64UrlDigits, doPadding) - } - - /** - * Writes a byte array as a JSON raw binary value. - * - * @param bs the byte array to write - */ - def writeRawVal(bs: Array[Byte]): Unit = { - writeOptionalCommaAndIndentionBeforeValue() - writeRawBytes(bs) - } - - /** - * Writes a JSON `null` value. - */ - def writeNull(): Unit = count = { - writeOptionalCommaAndIndentionBeforeValue() - val pos = ensureBufCapacity(4) - ByteArrayAccess.setInt(buf, pos, 0x6C6C756E) - pos + 4 - } - - /** - * Writes a JSON array start marker (`[`). - */ - def writeArrayStart(): Unit = writeNestedStart('[') - - - /** - * Writes a JSON array end marker (`]`). - */ - def writeArrayEnd(): Unit = writeNestedEnd(']') - - /** - * Writes a JSON array start marker (`{`). - */ - def writeObjectStart(): Unit = writeNestedStart('{') - - /** - * Writes a JSON array end marker (`}`). - */ - def writeObjectEnd(): Unit = writeNestedEnd('}') - - /* GWZ -- disable Jsoniter's codec facilities... - /** - * Writes JSON-encoded value of type `A` to an output stream. - * - * @param codec a JSON value codec for type `A` - * @param x the value to encode - * @param out the output stream to write to - * @param config the writer configuration - */ - def write[@sp A](codec: JsonValueCodec[A], x: A, out: OutputStream, config: WriterConfig): Unit = - try { - this.out = out - this.config = config - count = 0 - indention = 0 - comma = false - disableBufGrowing = false - if (limit < config.preferredBufSize) reallocateBufToPreferredSize() - codec.encodeValue(x, this) - out.write(buf, 0, count) - } finally { - this.out = null // don't close output stream - if (limit > config.preferredBufSize) reallocateBufToPreferredSize() - } - - /** - * Encodes a value of type `A` to a byte array. - * - * @param codec a JSON value codec for type `A` - * @param x the value to encode - * @param config the writer configuration - * @return the encoded JSON as a byte array - */ - def write[@sp A](codec: JsonValueCodec[A], x: A, config: WriterConfig): Array[Byte] = - try { - this.config = config - count = 0 - indention = 0 - comma = false - disableBufGrowing = false - codec.encodeValue(x, this) - java.util.Arrays.copyOf(buf, count) - } finally { - if (limit > config.preferredBufSize) reallocateBufToPreferredSize() - } - - /** - * Encodes a value of type `A` to a string. - * - * @param codec a JSON value codec for type `A` - * @param x the value to encode - * @param config the writer configuration - * @return the encoded JSON as a string - */ - def writeToString[@sp A](codec: JsonValueCodec[A], x: A, config: WriterConfig): String = - try { - this.config = config - count = 0 - indention = 0 - comma = false - disableBufGrowing = false - codec.encodeValue(x, this) - new String(buf, 0, count, StandardCharsets.UTF_8) - } finally { - if (limit > config.preferredBufSize) reallocateBufToPreferredSize() - } - - /** - * Encodes a value of type `A` to a string without buffer reallocation. - * - * @note Use only once with a newly allocated writer, so buffer reallocation is not required. - * - * @param codec a JSON value codec for type `A` - * @param x the value to encode - * @param config the writer configuration - * @return the encoded JSON as a string - */ - def writeToStringWithoutBufReallocation[@sp A](codec: JsonValueCodec[A], x: A, config: WriterConfig): String = { - this.config = config - count = 0 - indention = 0 - comma = false - disableBufGrowing = false - codec.encodeValue(x, this) - new String(buf, 0, count, StandardCharsets.UTF_8) - } - - /** - * Encodes a value of type `A` into a pre-allocated byte array slice. - * - * @param codec a JSON value codec for type `A` - * @param x the value to encode - * @param buf the target byte array - * @param from the start index of the target slice (inclusive) - * @param to the end index of the target slice (exclusive) - * @param config the writer configuration - * @return the number of bytes written to the target slice - */ - def write[@sp A](codec: JsonValueCodec[A], x: A, buf: Array[Byte], from: Int, to: Int, config: WriterConfig): Int = { - val currBuf = this.buf - try { - this.buf = buf - this.config = config - count = from - limit = to - indention = 0 - comma = false - disableBufGrowing = true - codec.encodeValue(x, this) - count - } finally { - setBuf(currBuf) - } - } - - /** - * Encodes a value of type `A` into a byte buffer. - * - * @param codec JSON value codec for type `A` - * @param x the value to encode - * @param bbuf the target byte buffer - * @param config the writer configuration - */ - def write[@sp A](codec: JsonValueCodec[A], x: A, bbuf: ByteBuffer, config: WriterConfig): Unit = - if (bbuf.hasArray) { - val offset = bbuf.arrayOffset - val currBuf = this.buf - try { - this.buf = bbuf.array - this.config = config - count = bbuf.position() + offset - limit = bbuf.limit() + offset - indention = 0 - comma = false - disableBufGrowing = true - codec.encodeValue(x, this) - } catch { - case _: ArrayIndexOutOfBoundsException => throw new BufferOverflowException - } finally { - setBuf(currBuf) - bbuf.position(count - offset) - } - } else { - try { - this.bbuf = bbuf - this.config = config - count = 0 - indention = 0 - comma = false - disableBufGrowing = false - if (limit < config.preferredBufSize) reallocateBufToPreferredSize() - codec.encodeValue(x, this) - bbuf.put(buf, 0, count) - } finally { - this.bbuf = null - if (limit > config.preferredBufSize) reallocateBufToPreferredSize() - } - } - */ - - private[this] def writeNestedStart(b: Byte): Unit = { - writeOptionalCommaAndIndentionBeforeKey() - writeBytes(b) - val indentionStep = config.indentionStep - if (indentionStep != 0) { - indention += indentionStep - writeIndention() - } - } - - private[this] def writeNestedEnd(b: Byte): Unit = { - comma = true - if (indention != 0) { - indention -= config.indentionStep - writeIndention() - } - writeBytes(b) - } - - private[this] def writeOptionalCommaAndIndentionBeforeValue(): Unit = - if (comma) { - writeBytes(',') - if (indention != 0) writeIndention() - } else comma = true - - private[this] def writeOptionalCommaAndIndentionBeforeKey(): Unit = - if (comma) { - comma = false - writeBytes(',') - if (indention != 0) writeIndention() - } - - private[this] def writeIndention(): Unit = count = { - val n = indention - val pos = ensureBufCapacity(n + 8) - writeIndention(buf, pos, n) - } - - private[this] def writeIndention(buf: Array[Byte], p: Int, n: Int): Int = { - var pos = p - buf(pos) = '\n' - pos += 1 - val posLim = pos + n - while (pos < posLim) { - ByteArrayAccess.setLong(buf, pos, 0x2020202020202020L) - pos += 8 - } - posLim - } - - private[this] def writeParenthesesWithColon(): Unit = count = { - var pos = ensureBufCapacity(4) // 4 == size of Int in bytes - ByteArrayAccess.setInt(buf, pos, 0x203A22) - if (indention > 0) pos += 1 - pos + 2 - } - - private[this] def writeColon(): Unit = count = { - var pos = ensureBufCapacity(2) - ByteArrayAccess.setShort(buf, pos, 0x203A) - if (indention > 0) pos += 1 - pos + 1 - } - - private[this] def writeBytes(b: Byte): Unit = count = { - var pos = count - if (pos >= limit) pos = flushAndGrowBuf(1, pos) - buf(pos) = b - pos + 1 - } - - private[this] def writeBase16Bytes(bs: Array[Byte], ds: Array[Short]): Unit = count = { - val lenM1 = bs.length - 1 - var posLim = limit - 6 - var pos = count - if (pos >= posLim) { - pos = flushAndGrowBuf(6, pos) - posLim = limit - 5 - } - var buf = this.buf - buf(pos) = '"' - pos += 1 - var offset = 0 - while (offset < lenM1) { - val offsetLim = Math.min((posLim - pos + 1 >> 1) + offset, lenM1) - while (offset < offsetLim) { - val d1 = ds(bs(offset) & 0xFF) - val d2 = ds(bs(offset + 1) & 0xFF) << 16 - ByteArrayAccess.setInt(buf, pos, d1 | d2) - pos += 4 - offset += 2 - } - if (pos >= posLim) { - pos = flushAndGrowBuf(5, pos) - buf = this.buf - posLim = limit - 5 - } - } - if (offset == lenM1) { - ByteArrayAccess.setShort(buf, pos, ds(bs(offset) & 0xFF)) - pos += 2 - } - buf(pos) = '"' - pos + 1 - } - - private[this] def writeBase64Bytes(bs: Array[Byte], ds: Array[Byte], doPadding: Boolean): Unit = count = { - val lenM3 = bs.length - 3 - var posLim = limit - 6 - var pos = count - if (pos >= posLim) { - pos = flushAndGrowBuf(6, pos) - posLim = limit - 5 - } - var buf = this.buf - buf(pos) = '"' - pos += 1 - var offset = 0 - while (offset < lenM3) { - val offsetLim = Math.min((posLim - pos + 3 >> 2) * 3 + offset, lenM3) - while (offset < offsetLim) { - val p = ByteArrayAccess.getIntReversed(bs, offset) - ByteArrayAccess.setInt(buf, pos, - ds(p >> 8 & 0x3F) << 24 | ds(p >> 14 & 0x3F) << 16 | ds(p >> 20 & 0x3F) << 8 | ds(p >>> 26)) - pos += 4 - offset += 3 - } - if (pos >= posLim) { - pos = flushAndGrowBuf(5, pos) - buf = this.buf - posLim = limit - 5 - } - } - if (offset == lenM3) { - val p = (bs(offset) & 0xFF) << 16 | (bs(offset + 1) & 0xFF) << 8 | (bs(offset + 2) & 0xFF) - ByteArrayAccess.setInt(buf, pos, - ds(p & 0x3F) << 24 | ds(p >> 6 & 0x3F) << 16 | ds(p >> 12 & 0x3F) << 8 | ds(p >> 18)) - pos += 4 - } else if (offset == lenM3 + 1) { - val p = (bs(offset) & 0xFF) << 10 | (bs(offset + 1) & 0xFF) << 2 - ByteArrayAccess.setInt(buf, pos, ds(p & 0x3F) << 16 | ds(p >> 6 & 0x3F) << 8 | ds(p >> 12) | 0x3D000000) - pos += 3 - if (doPadding) pos += 1 - } else if (offset == lenM3 + 2) { - val p = bs(offset) - ByteArrayAccess.setInt(buf, pos, ds(p << 4 & 0x3F) << 8 | ds(p >> 2 & 0x3F) | 0x3D3D0000) - pos += 2 - if (doPadding) pos += 2 - } - buf(pos) = '"' - pos + 1 - } - - private[this] def writeRawBytes(bs: Array[Byte]): Unit = count = { - var pos = count - var step = Math.max(config.preferredBufSize, limit - pos) - var remaining = bs.length - var offset = 0 - while (remaining > 0) { - step = Math.min(step, remaining) - if (pos + step > limit) pos = flushAndGrowBuf(step, pos) - System.arraycopy(bs, offset, buf, pos, step) - offset += step - pos += step - remaining -= step - } - pos - } - - private[this] def writeLongNonEscapedAsciiKey(x: String): Unit = { - writeOptionalCommaAndIndentionBeforeKey() - writeBytes('"') - var pos = count - var step = Math.max(config.preferredBufSize, limit - pos) - var remaining = x.length - var offset = 0 - while (remaining > 0) { - step = Math.min(step, remaining) - if (pos + step > limit) pos = flushAndGrowBuf(step, pos) - val newOffset = offset + step - x.getBytes(offset, newOffset, buf, pos) - offset = newOffset - pos += step - remaining -= step - } - count = pos - writeBytes('"') - writeColon() - } - - private[this] def writeLongNonEscapedAsciiVal(x: String): Unit = { - writeOptionalCommaAndIndentionBeforeValue() - writeBytes('"') - var pos = count - var step = Math.max(config.preferredBufSize, limit - pos) - var remaining = x.length - var offset = 0 - while (remaining > 0) { - step = Math.min(step, remaining) - if (pos + step > limit) pos = flushAndGrowBuf(step, pos) - val newOffset = offset + step - x.getBytes(offset, newOffset, buf, pos) - offset = newOffset - pos += step - remaining -= step - } - count = pos - writeBytes('"') - } - - private[this] def writeZoneId(x: ZoneId): Unit = count = { - val s = x.getId - val len = s.length - var pos = ensureBufCapacity(len + 2) - val buf = this.buf - buf(pos) = '"' - pos += 1 - s.getBytes(0, len, buf, pos) - pos += len - buf(pos) = '"' - pos + 1 - } - - private[this] def writeUUID(mostSigBits: Long, leastSigBits: Long): Unit = count = { - val pos = ensureBufCapacity(40) // 40 == 5 * size of Long in bytes - val buf = this.buf - val ds = lowerCaseHexDigits - val mostSigBits1 = (mostSigBits >> 32).toInt - val d1 = ds(mostSigBits1 >>> 24) << 8 - val d2 = ds(mostSigBits1 >> 16 & 0xFF).toLong << 24 - val d3 = ds(mostSigBits1 >> 8 & 0xFF).toLong << 40 - val d4 = ds(mostSigBits1 & 0xFF) - ByteArrayAccess.setLong(buf, pos, d1 | d2 | d3 | d4.toLong << 56 | 0x22) - val mostSigBits2 = mostSigBits.toInt - val d5 = ds(mostSigBits2 >>> 24) << 16 - val d6 = ds(mostSigBits2 >> 16 & 0xFF).toLong << 32 - val d7 = ds(mostSigBits2 >> 8 & 0xFF) - ByteArrayAccess.setLong(buf, pos + 8, d4 >> 8 | d5 | d6 | d7.toLong << 56 | 0x2D000000002D00L) - val d8 = ds(mostSigBits2 & 0xFF) << 8 - val leastSigBits1 = (leastSigBits >> 32).toInt - val d9 = ds(leastSigBits1 >>> 24).toLong << 32 - val d10 = ds(leastSigBits1 >> 16 & 0xFF).toLong << 48 - ByteArrayAccess.setLong(buf, pos + 16, d7 >> 8 | d8 | d9 | d10 | 0x2D000000) - val d11 = ds(leastSigBits1 >> 8 & 0xFF) << 8 - val d12 = ds(leastSigBits1 & 0xFF).toLong << 24 - val leastSigBits2 = leastSigBits.toInt - val d13 = ds(leastSigBits2 >>> 24).toLong << 40 - val d14 = ds(leastSigBits2 >> 16 & 0xFF) - ByteArrayAccess.setLong(buf, pos + 24, d11 | d12| d13 | d14.toLong << 56 | 0x2D) - val d15 = ds(leastSigBits2 >> 8 & 0xFF) << 8 - val d16 = ds(leastSigBits2 & 0xFF).toLong << 24 - ByteArrayAccess.setLong(buf, pos + 32, d14 >> 8 | d15 | d16 | 0x220000000000L) - pos + 38 - } - - @tailrec - private[this] def writeString(s: String, from: Int, pos: Int, minLim: Int, escapedChars: Array[Byte]): Int = - if (pos < minLim) { - val ch = s.charAt(from) - buf(pos) = ch.toByte - if (ch >= 0x80 || escapedChars(ch) != 0) writeEscapedOrEncodedString(s, from, pos, escapedChars) - else writeString(s, from + 1, pos + 1, minLim, escapedChars) - } else { - val remaining = s.length - from - if (remaining > 0) { - val newPos = flushAndGrowBuf(2, pos) - writeString(s, from, newPos, Math.min(remaining, limit - newPos - 1) + newPos, escapedChars) - } else pos - } - - private[this] def writeEscapedOrEncodedString(s: String, from: Int, pos: Int, escapedChars: Array[Byte]): Int = - if (config.escapeUnicode) writeEscapedString(s, from, s.length, pos, limit - 13, escapedChars) - else writeEncodedString(s, from, s.length, pos, limit - 7, escapedChars) - - @tailrec - private[this] def writeEncodedString(s: String, from: Int, to: Int, pos: Int, posLim: Int, escapedChars: Array[Byte]): Int = - if (from >= to) pos - else if (pos >= posLim) writeEncodedString(s, from, to, flushAndGrowBuf(7, pos), limit - 6, escapedChars) - else { - val ch1 = s.charAt(from) - if (ch1 < 0x80) { - val esc = escapedChars(ch1) - if (esc == 0) { // 000000000aaaaaaa (UTF-16 char) -> 0aaaaaaa (UTF-8 byte) - buf(pos) = ch1.toByte - writeEncodedString(s, from + 1, to, pos + 1, posLim, escapedChars) - } else if (esc > 0) { - ByteArrayAccess.setShort(buf, pos, (esc << 8 | 0x5C).toShort) - writeEncodedString(s, from + 1, to, pos + 2, posLim, escapedChars) - } else writeEncodedString(s, from + 1, to, writeEscapedUnicode(ch1.toByte, pos, buf), posLim, escapedChars) - } else if (ch1 < 0x800) { // 00000bbbbbaaaaaa (UTF-16 char) -> 110bbbbb 10aaaaaa (UTF-8 bytes) - ByteArrayAccess.setShort(buf, pos, (ch1 >> 6 | (ch1 << 8 & 0x3F00) | 0x80C0).toShort) - writeEncodedString(s, from + 1, to, pos + 2, posLim, escapedChars) - } else if (ch1 < 0xD800 || ch1 > 0xDFFF) { // ccccbbbbbbaaaaaa (UTF-16 char) -> 1110cccc 10bbbbbb 10aaaaaa (UTF-8 bytes) - ByteArrayAccess.setInt(buf, pos, ch1 >> 12 | (ch1 << 2 & 0x3F00) | (ch1 << 16 & 0x3F0000) | 0x8080E0) - writeEncodedString(s, from + 1, to, pos + 3, posLim, escapedChars) - } else { // 110110uuuuccccbb 110111bbbbaaaaaa (UTF-16 chars) -> 11110ddd 10ddcccc 10bbbbbb 10aaaaaa (UTF-8 bytes), where ddddd = uuuu + 1 - if (ch1 >= 0xDC00 || from + 1 >= to) illegalSurrogateError() - val ch2 = s.charAt(from + 1) - if (ch2 < 0xDC00 || ch2 > 0xDFFF) illegalSurrogateError() - val cp = (ch1 << 10) + (ch2 - 56613888) // -56613888 == 0x10000 - (0xD800 << 10) - 0xDC00 - ByteArrayAccess.setInt(buf, pos, cp >> 18 | (cp >> 4 & 0x3F00) | (cp << 10 & 0x3F0000) | (cp << 24 & 0x3F000000) | 0x808080F0) - writeEncodedString(s, from + 2, to, pos + 4, posLim, escapedChars) - } - } - - @tailrec - private[this] def writeEscapedString(s: String, from: Int, to: Int, pos: Int, posLim: Int, escapedChars: Array[Byte]): Int = - if (from >= to) pos - else if (pos >= posLim) writeEscapedString(s, from, to, flushAndGrowBuf(13, pos), limit - 12, escapedChars) - else { - val ch1 = s.charAt(from) - if (ch1 < 0x80) { - val esc = escapedChars(ch1) - if (esc == 0) { - buf(pos) = ch1.toByte - writeEscapedString(s, from + 1, to, pos + 1, posLim, escapedChars) - } else if (esc > 0) { - ByteArrayAccess.setShort(buf, pos, (esc << 8 | 0x5C).toShort) - writeEscapedString(s, from + 1, to, pos + 2, posLim, escapedChars) - } else writeEscapedString(s, from + 1, to, writeEscapedUnicode(ch1.toByte, pos, buf), posLim, escapedChars) - } else if (ch1 < 0xD800 || ch1 > 0xDFFF) { - writeEscapedString(s, from + 1, to, writeEscapedUnicode(ch1, pos, buf), posLim, escapedChars) - } else { - if (ch1 >= 0xDC00 || from + 1 >= to) illegalSurrogateError() - val ch2 = s.charAt(from + 1) - if (ch2 < 0xDC00 || ch2 > 0xDFFF) illegalSurrogateError() - writeEscapedString(s, from + 2, to, writeEscapedUnicode(ch2, writeEscapedUnicode(ch1, pos, buf), buf), posLim, escapedChars) - } - } - - private[this] def writeChar(ch: Char): Unit = count = { - val pos = ensureBufCapacity(8) // 8 = size of Long in bytes - if (ch < 0x80) { - val esc = escapedChars(ch) - if (esc == 0) { // 000000000aaaaaaa (UTF-16 char) -> 0aaaaaaa (UTF-8 byte) - ByteArrayAccess.setInt(buf, pos, ch << 8 | 0x220022) - pos + 3 - } else if (esc > 0) { - ByteArrayAccess.setInt(buf, pos, esc << 16 | 0x22005C22) - pos + 4 - } else { - val ds = lowerCaseHexDigits - ByteArrayAccess.setLong(buf, pos, ds(ch).toLong << 40 | 0x2200003030755C22L) - pos + 8 - } - } else if (config.escapeUnicode) { - if (ch >= 0xD800 && ch <= 0xDFFF) illegalSurrogateError() - val ds = lowerCaseHexDigits - val d1 = ds(ch >> 8).toLong << 24 - val d2 = ds(ch & 0xFF).toLong << 40 - ByteArrayAccess.setLong(buf, pos, d1 | d2 | 0x2200000000755C22L) - pos + 8 - } else if (ch < 0x800) { // 00000bbbbbaaaaaa (UTF-16 char) -> 110bbbbb 10aaaaaa (UTF-8 bytes) - ByteArrayAccess.setInt(buf, pos, (ch & 0x3F) << 16 | (ch & 0xFC0) << 2 | 0x2280C022) - pos + 4 - } else if (ch < 0xD800 || ch > 0xDFFF) { // ccccbbbbbbaaaaaa (UTF-16 char) -> 1110cccc 10bbbbbb 10aaaaaa (UTF-8 bytes) - ByteArrayAccess.setLong(buf, pos, ((ch & 0x3F) << 24 | (ch & 0xFC0) << 10 | (ch & 0xF000) >> 4) | 0x228080E022L) - pos + 5 - } else illegalSurrogateError() - } - - private[this] def writeEscapedUnicode(ch: Char, pos: Int, buf: Array[Byte]): Int = { - val ds = lowerCaseHexDigits - ByteArrayAccess.setShort(buf, pos, 0x755C) - val d1 = ds(ch >> 8) - val d2 = ds(ch & 0xFF) << 16 - ByteArrayAccess.setInt(buf, pos + 2, d1 | d2) - pos + 6 - } - - private[this] def writeEscapedUnicode(b: Byte, pos: Int, buf: Array[Byte]): Int = { - val ds = lowerCaseHexDigits - ByteArrayAccess.setInt(buf, pos, 0x3030755C) - ByteArrayAccess.setShort(buf, pos + 4, ds(b & 0xFF)) - pos + 6 - } - - private[this] def illegalSurrogateError(): Nothing = encodeError("illegal char sequence of surrogate pair") - - private[this] def writeBigInteger(x: BigInteger, ss: Array[BigInteger]): Unit = - if (x.bitLength < 64) writeLong(x.longValue) - else { - val n = calculateTenPow18SquareNumber(x) - val ss1 = - if (ss eq null) getTenPow18Squares(n) - else ss - val qr = x.divideAndRemainder(ss1(n)) - writeBigInteger(qr(0), ss1) - writeBigIntegerRemainder(qr(1), n - 1, ss1) - } - - private[this] def writeBigIntegerRemainder(x: BigInteger, n: Int, ss: Array[BigInteger]): Unit = - if (n < 0) count = write18Digits(Math.abs(x.longValue), ensureBufCapacity(18), buf, digits) - else { - val qr = x.divideAndRemainder(ss(n)) - writeBigIntegerRemainder(qr(0), n - 1, ss) - writeBigIntegerRemainder(qr(1), n - 1, ss) - } - - private[this] def writeBigDecimal(x: java.math.BigDecimal): Unit = { - val exp = writeBigDecimal(x.unscaledValue, x.scale, 0, null) - if (exp != 0) { - var pos = ensureBufCapacity(12) - val buf = this.buf - val ds = digits - var m: Short = 0x2B45 - var q0 = exp - if (exp < 0) { - m = 0x2D45 - q0 = -exp - } - ByteArrayAccess.setShort(buf, pos, m) - pos += 2 - var q = 0 - var lastPos = pos - if (q0 < 100000000) { - q = q0.toInt - lastPos += digitCount(q0) - count = lastPos - } else { - val q1 = (q0 >> 8) * 1441151881 >> 49 // divide a small positive long by 100000000 - q = q1.toInt - lastPos += digitCount(q1) - count = write8Digits(q0 - q1 * 100000000, lastPos, buf, ds) - } - writePositiveIntDigits(q, lastPos, buf, ds) - } - } - - private[this] def writeBigDecimal(x: BigInteger, scale: Int, blockScale: Int, ss: Array[BigInteger]): Long = - if (x.bitLength < 64) { - val v = x.longValue - val pos = ensureBufCapacity(28) // Long.MinValue.toString.length + 8 (for a leading zero, dot, and padding zeroes) - count = pos - writeLong(v) - val blockLen = (v >> 63).toInt + count - pos - val dotOff = scale.toLong - blockScale - val exp = (blockLen - 1) - dotOff - if (scale >= 0 && exp >= -6) { - if (exp < 0) insertDotWithZeroes(blockLen, -1 - exp.toInt) - else if (dotOff > 0) insertDot(count - dotOff.toInt) - 0 - } else { - if (blockLen > 1 || blockScale > 0) insertDot(count - blockLen + 1) - exp - } - } else { - val n = calculateTenPow18SquareNumber(x) - val ss1 = - if (ss eq null) getTenPow18Squares(n) - else ss - val qr = x.divideAndRemainder(ss1(n)) - val exp = writeBigDecimal(qr(0), scale, blockScale + (18 << n), ss1) - writeBigDecimalRemainder(qr(1), scale, blockScale, n - 1, ss1) - exp - } - - private[this] def writeBigDecimalRemainder(x: BigInteger, scale: Int, blockScale: Int, n: Int, - ss: Array[BigInteger]): Unit = - if (n < 0) { - count = write18Digits(Math.abs(x.longValue), ensureBufCapacity(19), buf, digits) // 18 digits and a place for optional dot - val dotOff = scale - blockScale - if (dotOff > 0 && dotOff <= 18) insertDot(count - dotOff) - } else { - val qr = x.divideAndRemainder(ss(n)) - writeBigDecimalRemainder(qr(0), scale, blockScale + (18 << n), n - 1, ss) - writeBigDecimalRemainder(qr(1), scale, blockScale, n - 1, ss) - } - - private[this] def calculateTenPow18SquareNumber(x: BigInteger): Int = { - val m = Math.max((x.bitLength * 71828554L >> 32).toInt - 1, 1) // Math.max((x.bitLength * Math.log(2) / Math.log(1e18)).toInt - 1, 1) - 31 - java.lang.Integer.numberOfLeadingZeros(m) - } - - private[this] def insertDotWithZeroes(len: Int, pad: Int): Unit = count = { - var pos = count + pad + 1 - val buf = this.buf - val numPos = pos - len - val off = pad + 2 - while (pos > numPos) { - buf(pos) = buf(pos - off) - pos -= 1 - } - val dotPos = pos - pad - while (pos > dotPos) { - buf(pos) = '0' - pos -= 1 - } - ByteArrayAccess.setShort(buf, dotPos - 1, 0x2E30) - count + off - } - - private[this] def insertDot(dotPos: Int): Unit = count = { - var pos = count - val buf = this.buf - while (pos > dotPos) { - buf(pos) = buf(pos - 1) - pos -= 1 - } - buf(dotPos) = '.' - count + 1 - } - - private[this] def writeBoolean(x: Boolean): Unit = count = { - val pos = ensureBufCapacity(8) // bytes in Long - if (x) { - ByteArrayAccess.setInt(buf, pos, 0x65757274) - pos + 4 - } else { - ByteArrayAccess.setLong(buf, pos, 0x65736c6166L) - pos + 5 - } - } - - private[this] def writeByte(x: Byte): Unit = count = { - var pos = ensureBufCapacity(5) // size of Int in bytes + one byte for the sign - val buf = this.buf - val ds = digits - var q0: Int = x - if (q0 < 0) { - buf(pos) = '-' - pos += 1 - q0 = -q0 - } - if (q0 < 10) { - buf(pos) = (q0 + '0').toByte - pos + 1 - } else if (q0 < 100) { - ByteArrayAccess.setShort(buf, pos, ds(q0)) - pos + 2 - } else { - ByteArrayAccess.setInt(buf, pos, ds(q0 - 100) << 8 | 0x31) - pos + 3 - } - } - - private[this] def writeDuration(x: Duration): Unit = count = { - var pos = ensureBufCapacity(40) // 40 == "PT-1111111111111111H-11M-11.111111111S".length + 2 - val buf = this.buf - val totalSecs = x.getSeconds - var nano = x.getNano - ByteArrayAccess.setLong(buf, pos, 0x225330545022L) - if ((totalSecs | nano) == 0) pos + 6 - else { - pos += 3 - val effectiveTotalSecs = - if (totalSecs < 0) (-nano >> 31) - totalSecs - else totalSecs - val hours = Math.multiplyHigh(effectiveTotalSecs >> 4, 655884233731895169L) >> 3 // divide a positive long by 3600 - val secsOfHour = (effectiveTotalSecs - hours * 3600).toInt - val minutes = secsOfHour * 17477 >> 20 // divide a small positive int by 60 - val seconds = secsOfHour - minutes * 60 - val ds = digits - if (hours != 0) { - if (totalSecs < 0) { - buf(pos) = '-' - pos += 1 - } - var q = 0 - var lastPos = pos - if (hours < 100000000) { - q = hours.toInt - lastPos += digitCount(hours) - pos = lastPos - } else { - val q1 = Math.multiplyHigh(hours, 6189700196426901375L) >>> 25 // divide a positive long by 100000000 - q = q1.toInt - lastPos += digitCount(q1) - pos = write8Digits(hours - q1 * 100000000, lastPos, buf, ds) - } - writePositiveIntDigits(q, lastPos, buf, ds) - ByteArrayAccess.setShort(buf, pos, 0x2248) - pos += 1 - } - if (minutes != 0) { - if (totalSecs < 0) { - buf(pos) = '-' - pos += 1 - } - if (minutes < 10) { - buf(pos) = (minutes + '0').toByte - pos += 1 - } else pos = write2Digits(minutes, pos, buf, ds) - ByteArrayAccess.setShort(buf, pos, 0x224D) - pos += 1 - } - if ((seconds | nano) != 0) { - if (totalSecs < 0) { - buf(pos) = '-' - pos += 1 - } - if (seconds < 10) { - buf(pos) = (seconds + '0').toByte - pos += 1 - } else pos = write2Digits(seconds, pos, buf, ds) - if (nano != 0) { - if (totalSecs < 0) nano = 1000000000 - nano - val dotPos = pos - pos = writeSignificantFractionDigits(nano, pos + 9, pos, buf, ds) - buf(dotPos) = '.' - } - ByteArrayAccess.setShort(buf, pos, 0x2253) - pos += 1 - } - pos + 1 - } - } - - private[this] def writeInstant(x: Instant): Unit = { - val epochSecond = x.getEpochSecond - if (epochSecond < 0) writeBeforeEpochInstant(epochSecond, x.getNano) - else { - val epochDay = Math.multiplyHigh(epochSecond, 1749024623285053783L) >> 13 // epochSecond / 86400 - val marchZeroDay = epochDay + 719468 // 719468 == 719528 - 60 == days 0000 to 1970 - days 1st Jan to 1st Mar - var year = (Math.multiplyHigh(marchZeroDay * 400 + 591, 4137408090565272301L) >> 15).toInt // ((marchZeroDay * 400 + 591) / 146097).toInt - var year365 = year * 365L - var year1374389535 = year * 1374389535L - var century = (year1374389535 >> 37).toInt - var marchDayOfYear = (marchZeroDay - year365).toInt - (year >> 2) + century - (century >> 2) - if (marchDayOfYear < 0) { - year365 -= 365 - year1374389535 -= 1374389535 - year -= 1 - century = (year1374389535 >> 37).toInt - marchDayOfYear = (marchZeroDay - year365).toInt - (year >> 2) + century - (century >> 2) - } - val marchMonth = marchDayOfYear * 17135 + 6854 >> 19 // (marchDayOfYear * 5 + 2) / 153 - val day = marchDayOfYear - (marchMonth * 1002762 - 16383 >> 15) // marchDayOfYear - (marchMonth * 306 + 5) / 10 + 1 - val m = 9 - marchMonth >> 4 - val month = (m & -9 | 3) + marchMonth - year -= m - writeInstant(year, month, day, (epochSecond - epochDay * 86400).toInt, x.getNano) - } - } - - private[this] def writeBeforeEpochInstant(epochSecond: Long, nano: Int): Unit = { - val epochDay = (Math.multiplyHigh(epochSecond - 86399, 1749024623285053783L) >> 13) + 1 // (epochSecond - 86399) / 86400 - var marchZeroDay = epochDay + 719468 // 719468 == 719528 - 60 == days 0000 to 1970 - days 1st Jan to 1st Mar - val adjust400YearCycles = ((marchZeroDay + 1) * 7525902 >> 40).toInt // ((marchZeroDay + 1) / 146097).toInt - 1 - marchZeroDay -= adjust400YearCycles * 146097L - var year = { // ((marchZeroDay * 400 + 591) / 146097).toInt - val pa = marchZeroDay * 400 + 591 - ((Math.multiplyHigh(pa, 4137408090565272301L) >> 15) + (pa >> 63)).toInt - } - var year365 = year * 365L - var year1374389535 = year * 1374389535L - var century = (year1374389535 >> 37).toInt - var marchDayOfYear = (marchZeroDay - year365).toInt - (year >> 2) + century - (century >> 2) - if (marchDayOfYear < 0) { - year365 -= 365 - year1374389535 -= 1374389535 - year -= 1 - century = (year1374389535 >> 37).toInt - marchDayOfYear = (marchZeroDay - year365).toInt - (year >> 2) + century - (century >> 2) - } - val marchMonth = marchDayOfYear * 17135 + 6854 >> 19 // (marchDayOfYear * 5 + 2) / 153 - val day = marchDayOfYear - (marchMonth * 1002762 - 16383 >> 15) // marchDayOfYear - (marchMonth * 306 + 5) / 10 + 1 - val m = 9 - marchMonth >> 4 - val month = (m & -9 | 3) + marchMonth - year += adjust400YearCycles * 400 - m - writeInstant(year, month, day, (epochSecond - epochDay * 86400).toInt, nano) - } - - private[this] def writeInstant(year: Int, month: Int, day: Int, secsOfDay: Int, nano: Int): Unit = count = { - var pos = ensureBufCapacity(39) // 39 == Instant.MAX.toString.length + 2 - val buf = this.buf - val ds = digits - buf(pos) = '"' - pos = writeYear(year, pos + 1, buf, ds) - ByteArrayAccess.setLong(buf, pos, ds(month) << 8 | ds(day).toLong << 32 | 0x5400002D00002DL) - pos += 7 - val y1 = secsOfDay * 37283 // Based on James Anhalt's algorithm: https://jk-jeon.github.io/posts/2022/02/jeaiii-algorithm/ - val y2 = (y1 & 0x7FFFFFF) * 15 - val y3 = (y2 & 0x1FFFFFF) * 15 - ByteArrayAccess.setLong(buf, pos, ds(y1 >>> 27) | ds(y2 >> 25).toLong << 24 | ds(y3 >> 23).toLong << 48 | 0x3A00003A0000L) - pos += 8 - if (nano != 0) pos = writeNanos(nano, pos, buf, ds) - ByteArrayAccess.setShort(buf, pos, 0x225A) - pos + 2 - } - - private[this] def writeLocalDate(x: LocalDate): Unit = count = { - var pos = ensureBufCapacity(19) // 19 == java.time.Year.MAX_VALUE.toString.length + 9 - val buf = this.buf - val ds = digits - buf(pos) = '"' - pos = writeYear(x.getYear, pos + 1, buf, ds) - val d1 = ds(x.getMonthValue) << 8 - val d2 = ds(x.getDayOfMonth).toLong << 32 - ByteArrayAccess.setLong(buf, pos, d1 | d2 | 0x2200002D00002DL) - pos + 7 - } - - private[this] def writeLocalDateTime(x: LocalDateTime): Unit = count = { - var pos = ensureBufCapacity(37) // 37 == LocalDateTime.MAX.toString.length + 2 - val buf = this.buf - val ds = digits - buf(pos) = '"' - pos = writeLocalTime(x.toLocalTime, writeLocalDateWithT(x.toLocalDate, pos + 1, buf, ds), buf, ds) - buf(pos) = '"' - pos + 1 - } - - private[this] def writeLocalTime(x: LocalTime): Unit = count = { - var pos = ensureBufCapacity(20) // 20 == LocalTime.MAX.toString.length + 2 - val buf = this.buf - val ds = digits - buf(pos) = '"' - pos = writeLocalTime(x, pos + 1, buf, ds) - buf(pos) = '"' - pos + 1 - } - - private[this] def writeMonthDay(x: MonthDay): Unit = count = { - val pos = ensureBufCapacity(9) // 9 == "--01-01".length + 2 - val buf = this.buf - val ds = digits - buf(pos) = '"' - val d1 = ds(x.getMonthValue) << 16 - val d2 = ds(x.getDayOfMonth).toLong << 40 - ByteArrayAccess.setLong(buf, pos + 1, d1 | d2 | 0x2200002D00002D2DL) - pos + 9 - } - - private[this] def writeOffsetDateTime(x: OffsetDateTime): Unit = count = { - val pos = ensureBufCapacity(46) // 46 == "+999999999-12-31T23:59:59.999999999+00:00:01".length + 2 - val buf = this.buf - val ds = digits - buf(pos) = '"' - writeOffset(x.getOffset, - writeLocalTime(x.toLocalTime, writeLocalDateWithT(x.toLocalDate, pos + 1, buf, ds), buf, ds), buf, ds) - } - - private[this] def writeOffsetTime(x: OffsetTime): Unit = count = { - val pos = ensureBufCapacity(29) // 29 == "00:00:07.999999998+00:00:08".length + 2 - val buf = this.buf - val ds = digits - buf(pos) = '"' - writeOffset(x.getOffset, writeLocalTime(x.toLocalTime, pos + 1, buf, ds), buf, ds) - } - - private[this] def writePeriod(x: Period): Unit = count = { - var pos = ensureBufCapacity(39) // 39 == "P-2147483648Y-2147483648M-2147483648D".length + 2 - val buf = this.buf - val years = x.getYears - val months = x.getMonths - val days = x.getDays - ByteArrayAccess.setLong(buf, pos, 0x2244305022L) - if ((years | months | days) == 0) pos + 5 - else { - pos += 2 - val ds = digits - if (years != 0) pos = writePeriod(years, pos, buf, ds, 0x2259) - if (months != 0) pos = writePeriod(months, pos, buf, ds, 0x224D) - if (days != 0) pos = writePeriod(days, pos, buf, ds, 0x2244) - pos + 1 - } - } - - private[this] def writePeriod(x: Int, p: Int, buf: Array[Byte], ds: Array[Short], bs: Short): Int = { - var pos = p - val q0 = - if (x >= 0) x - else if (x != -2147483648) { - buf(pos) = '-' - pos += 1 - -x - } else { - ByteArrayAccess.setShort(buf, pos, 0x322D) - pos += 2 - 147483648 - } - pos += digitCount(q0) - writePositiveIntDigits(q0, pos, buf, ds) - ByteArrayAccess.setShort(buf, pos, bs) - pos + 1 - } - - private[this] def writeYear(x: Year): Unit = count = { - var pos = ensureBufCapacity(12) // 12 == "+999999999".length + 2 - val buf = this.buf - buf(pos) = '"' - pos = writeYear(x.getValue, pos + 1, buf, digits) - buf(pos) = '"' - pos + 1 - } - - private[this] def writeYearMonth(x: YearMonth): Unit = count = { - var pos = ensureBufCapacity(15) // 15 == "+999999999-12".length + 2 - val buf = this.buf - val ds = digits - buf(pos) = '"' - pos = writeYear(x.getYear, pos + 1, buf, ds) - ByteArrayAccess.setInt(buf, pos, ds(x.getMonthValue) << 8 | 0x2200002D) - pos + 4 - } - - private[this] def writeZonedDateTime(x: ZonedDateTime): Unit = count = { - var pos = ensureBufCapacity(46) // 46 == "+999999999-12-31T23:59:59.999999999+00:00:01".length + 2 - var buf = this.buf - val ds = digits - buf(pos) = '"' - pos = writeOffset(x.getOffset, - writeLocalTime(x.toLocalTime, writeLocalDateWithT(x.toLocalDate, pos + 1, buf, ds), buf, ds), buf, ds) - val zone = x.getZone - if (!zone.isInstanceOf[ZoneOffset]) { - val zoneId = zone.getId - val len = zoneId.length - val required = len + 3 - if (pos + required > limit) { - pos = flushAndGrowBuf(required, pos) - buf = this.buf - } - buf(pos - 1) = '[' - zoneId.getBytes(0, len, buf, pos) - pos += len - ByteArrayAccess.setShort(buf, pos, 0x225D) - pos += 2 - } - pos - } - - private[this] def writeZoneOffset(x: ZoneOffset): Unit = count = { - val pos = ensureBufCapacity(12) // 12 == number of bytes in Long and Int - val buf = this.buf - var y = x.getTotalSeconds - if (y == 0) { - ByteArrayAccess.setInt(buf, pos, 0x225A22) - pos + 3 - } else { - val ds = digits - var m = 0x2230303A00002B22L - if (y < 0) { - y = -y - m = 0x2230303A00002D22L - } - y *= 37283 // Based on James Anhalt's algorithm: https://jk-jeon.github.io/posts/2022/02/jeaiii-algorithm/ - m |= ds(y >>> 27) << 16 - if ((y & 0x7FF8000) == 0) { // check if totalSeconds is divisible by 3600 - ByteArrayAccess.setLong(buf, pos, m) - pos + 8 - } else { - y = (y & 0x7FFFFFF) * 15 - ByteArrayAccess.setLong(buf, pos, ds(y >> 25).toLong << 40 | m) - if ((y & 0x1F80000) == 0) pos + 8 // check if totalSeconds is divisible by 60 - else { - ByteArrayAccess.setInt(buf, pos + 7, ds((y & 0x1FFFFFF) * 15 >> 23) << 8 | 0x2200003A) - pos + 11 - } - } - } - } - - private[this] def writeLocalDateWithT(x: LocalDate, p: Int, buf: Array[Byte], ds: Array[Short]): Int = { - val pos = writeYear(x.getYear, p, buf, ds) - val d1 = ds(x.getMonthValue) << 8 - val d2 = ds(x.getDayOfMonth).toLong << 32 - ByteArrayAccess.setLong(buf, pos, d1 | d2 | 0x5400002D00002DL) - pos + 7 - } - - private[this] def writeYear(year: Int, pos: Int, buf: Array[Byte], ds: Array[Short]): Int = - if (year >= 0 && year < 10000) write4Digits(year, pos, buf, ds) - else writeYearWithSign(year, pos, buf, ds) - - private[this] def writeYearWithSign(year: Int, p: Int, buf: Array[Byte], ds: Array[Short]): Int = { - var q0 = year - var pos = p - var b: Byte = '+' - if (q0 < 0) { - q0 = -q0 - b = '-' - } - buf(pos) = b - pos += 1 - if (q0 < 10000) write4Digits(q0, pos, buf, ds) - else { - pos += digitCount(q0) - writePositiveIntDigits(q0, pos, buf, ds) - pos - } - } - - private[this] def writeLocalTime(x: LocalTime, pos: Int, buf: Array[Byte], ds: Array[Short]): Int = { - val second = x.getSecond - val nano = x.getNano - val d1 = ds(x.getHour) | 0x3A00003A0000L - val d2 = ds(x.getMinute).toLong << 24 - if ((second | nano) == 0) { - ByteArrayAccess.setLong(buf, pos, d1 | d2) - pos + 5 - } else { - val d3 = ds(second).toLong << 48 - ByteArrayAccess.setLong(buf, pos, d1 | d2 | d3) - if (nano == 0) pos + 8 - else writeNanos(nano, pos + 8, buf, ds) - } - } - - private[this] def writeNanos(q0: Long, pos: Int, buf: Array[Byte], ds: Array[Short]): Int = { - val y1 = q0 * 1441151881 // Based on James Anhalt's algorithm for 9 digits: https://jk-jeon.github.io/posts/2022/02/jeaiii-algorithm/ - val y2 = (y1 & 0x1FFFFFFFFFFFFFFL) * 100 - var m = y1 >>> 57 << 8 | ds((y2 >>> 57).toInt) << 16 | 0x302E - if ((y2 & 0x1FFFFF800000000L) == 0) { // check if q0 is divisible by 1000000 - ByteArrayAccess.setInt(buf, pos, m.toInt) - pos + 4 - } else { - val y3 = (y2 & 0x1FFFFFFFFFFFFFFL) * 100 - val y4 = (y3 & 0x1FFFFFFFFFFFFFFL) * 100 - m |= ds((y3 >>> 57).toInt).toLong << 32 - val d = ds((y4 >>> 57).toInt) - ByteArrayAccess.setLong(buf, pos, m | d.toLong << 48) - if ((y4 & 0x1FF000000000000L) == 0 && d <= 0x3039) pos + 7 // check if q0 is divisible by 1000 - else { - ByteArrayAccess.setShort(buf, pos + 8, ds(((y4 & 0x1FFFFFFFFFFFFFFL) * 100 >>> 57).toInt)) - pos + 10 - } - } - } - - private[this] def writeOffset(x: ZoneOffset, pos: Int, buf: Array[Byte], ds: Array[Short]): Int = { - var y = x.getTotalSeconds - if (y == 0) { - ByteArrayAccess.setShort(buf, pos, 0x225A) - pos + 2 - } else { - var m = 0x2230303A00002BL - if (y < 0) { - y = -y - m = 0x2230303A00002DL - } - y *= 37283 // Based on James Anhalt's algorithm: https://jk-jeon.github.io/posts/2022/02/jeaiii-algorithm/ - m |= ds(y >>> 27) << 8 - if ((y & 0x7FF8000) == 0) { // check if totalSeconds is divisible by 3600 - ByteArrayAccess.setLong(buf, pos, m) - pos + 7 - } else { - y = (y & 0x7FFFFFF) * 15 - ByteArrayAccess.setLong(buf, pos, ds(y >> 25).toLong << 32 | m) - if ((y & 0x1F80000) == 0) pos + 7 // check if totalSeconds is divisible by 60 - else { - ByteArrayAccess.setInt(buf, pos + 6, ds((y & 0x1FFFFFF) * 15 >> 23) << 8 | 0x2200003A) - pos + 10 - } - } - } - } - - private[this] def write2Digits(q0: Int, pos: Int, buf: Array[Byte], ds: Array[Short]): Int = { - ByteArrayAccess.setShort(buf, pos, ds(q0)) - pos + 2 - } - - private[this] def write3Digits(q0: Int, pos: Int, buf: Array[Byte], ds: Array[Short]): Int = { - val q1 = q0 * 1311 >> 17 // divide a small positive int by 100 - ByteArrayAccess.setInt(buf, pos, ds(q0 - q1 * 100) << 8 | q1 + '0') - pos + 3 - } - - private[this] def write4Digits(q0: Int, pos: Int, buf: Array[Byte], ds: Array[Short]): Int = { - val q1 = q0 * 5243 >> 19 // divide a small positive int by 100 - val d1 = ds(q0 - q1 * 100) << 16 - val d2 = ds(q1) - ByteArrayAccess.setInt(buf, pos, d1 | d2) - pos + 4 - } - - private[this] def write8Digits(q0: Long, pos: Int, buf: Array[Byte], ds: Array[Short]): Int = { - val y1 = q0 * 140737489 // Based on James Anhalt's algorithm for 8 digits: https://jk-jeon.github.io/posts/2022/02/jeaiii-algorithm/ - val y2 = (y1 & 0x7FFFFFFFFFFFL) * 100 - val y3 = (y2 & 0x7FFFFFFFFFFFL) * 100 - val y4 = (y3 & 0x7FFFFFFFFFFFL) * 100 - val d1 = ds((y1 >> 47).toInt) - val d2 = ds((y2 >> 47).toInt) << 16 - val d3 = ds((y3 >> 47).toInt).toLong << 32 - val d4 = ds((y4 >> 47).toInt).toLong << 48 - ByteArrayAccess.setLong(buf, pos, d1 | d2 | d3 | d4) - pos + 8 - } - - private[this] def write18Digits(q0: Long, pos: Int, buf: Array[Byte], ds: Array[Short]): Int = { - val q1 = Math.multiplyHigh(q0, 6189700196426901375L) >>> 25 // divide a positive long by 100000000 - write8Digits(q0 - q1 * 100000000, { - val q2 = (q1 >> 8) * 1441151881 >> 49 // divide a small positive long by 100000000 - write8Digits(q1 - q2 * 100000000, write2Digits(q2.toInt, pos, buf, ds), buf, ds) - }, buf, ds) - } - - private[this] def writeShort(x: Short): Unit = count = { - var pos = ensureBufCapacity(9) // 8 bytes in long + a byte for the sign - val buf = this.buf - val ds = digits - var q0: Int = x - if (q0 < 0) { - buf(pos) = '-' - pos += 1 - q0 = -q0 - } - if (q0 < 100) { - if (q0 < 10) { - buf(pos) = (q0 + '0').toByte - pos + 1 - } else { - ByteArrayAccess.setShort(buf, pos, ds(q0)) - pos + 2 - } - } else if (q0 < 10000) { - val q1 = q0 * 5243 >> 19 // divide a small positive int by 100 - val d2 = ds(q0 - q1 * 100) - if (q0 < 1000) { - ByteArrayAccess.setInt(buf, pos, q1 + '0' | d2 << 8) - pos + 3 - } else { - ByteArrayAccess.setInt(buf, pos, ds(q1) | d2 << 16) - pos + 4 - } - } else { - val y1 = q0 * 429497L // Based on James Anhalt's algorithm for 5 digits: https://jk-jeon.github.io/posts/2022/02/jeaiii-algorithm/ - val y2 = (y1 & 0xFFFFFFFFL) * 100 - val y3 = (y2 & 0xFFFFFFFFL) * 100 - val d1 = (y1 >> 32).toInt + '0' - val d2 = ds((y2 >> 32).toInt) << 8 - val d3 = ds((y3 >> 32).toInt).toLong << 24 - ByteArrayAccess.setLong(buf, pos, d1 | d2 | d3) - pos + 5 - } - } - - private[this] def writeInt(x: Int): Unit = count = { - var pos = ensureBufCapacity(11) // Int.MinValue.toString.length - val buf = this.buf - val ds = digits - val q0 = - if (x >= 0) x - else if (x != -2147483648) { - buf(pos) = '-' - pos += 1 - -x - } else { - ByteArrayAccess.setShort(buf, pos, 0x322D) - pos += 2 - 147483648 - } - pos += digitCount(q0) - writePositiveIntDigits(q0, pos, buf, ds) - pos - } - - private[this] def writeLong(x: Long): Unit = count = { - var pos = ensureBufCapacity(20) // Long.MinValue.toString.length - val buf = this.buf - val ds = digits - val q0 = - if (x >= 0) x - else if (x != -9223372036854775808L) { - buf(pos) = '-' - pos += 1 - -x - } else { - ByteArrayAccess.setShort(buf, pos, 0x392D) - pos += 2 - 223372036854775808L - } - var q = 0 - var lastPos = pos - if (q0 < 100000000) { - q = q0.toInt - lastPos += digitCount(q0) - pos = lastPos - } else { - val q1 = Math.multiplyHigh(q0, 6189700196426901375L) >>> 25 // divide a positive long by 100000000 - pos = write8Digits(q0 - q1 * 100000000, { - if (q1 < 100000000) { - q = q1.toInt - lastPos += digitCount(q1) - lastPos - } else { - val q2 = (q1 >> 8) * 1441151881 >> 49 // divide a small positive long by 100000000 - q = q2.toInt - lastPos += digitCount(q2) - write8Digits(q1 - q2 * 100000000, lastPos, buf, ds) - } - }, buf, ds) - } - writePositiveIntDigits(q, lastPos, buf, ds) - pos - } - - // Based on the amazing work of Raffaello Giulietti - // "The Schubfach way to render doubles": https://drive.google.com/file/d/1luHhyQF9zKlM8yJ1nebU0OgVYhfC6CBN/view - // Sources with the license are here: https://github.com/c4f7fcce9cb06515/Schubfach/blob/3c92d3c9b1fead540616c918cdfef432bca53dfa/todec/src/math/FloatToDecimal.java - private[this] def writeFloat(x: Float): Unit = count = { - val bits = java.lang.Float.floatToRawIntBits(x) - var pos = ensureBufCapacity(15) - val buf = this.buf - if (bits < 0) { - buf(pos) = '-' - pos += 1 - } - if (x == 0.0f) { - ByteArrayAccess.setInt(buf, pos, 0x302E30) - pos + 3 - } else { - val ieeeExponent = bits >> 23 & 0xFF - val ieeeMantissa = bits & 0x7FFFFF - var e2 = ieeeExponent - 150 - var m2 = ieeeMantissa | 0x800000 - var m10, e10 = 0 - if (e2 == 0) m10 = m2 - else if (e2 >= -23 && e2 < 0 && m2 << e2 == 0) m10 = m2 >> -e2 - else { - var e10Corr, e2Corr = 0 - var cblCorr = 2 - if (ieeeExponent == 0) { - e2 = -149 - m2 = ieeeMantissa - if (ieeeMantissa < 8) { - m2 *= 10 - e10Corr = 1 - } - } else if (ieeeExponent == 255) illegalNumberError(x) - else if (ieeeMantissa == 0 && ieeeExponent > 1) { - e2Corr = 131007 - cblCorr = 1 - } - e10 = e2 * 315653 - e2Corr >> 20 - val g = gs(e10 + 324 << 1) + 1 - val h = (e10 * -108853 >> 15) + e2 + 1 - val cb = m2 << 2 - val vbCorr = (m2 & 0x1) - 1 - val vb = rop(g, cb << h) - val vbl = rop(g, cb - cblCorr << h) + vbCorr - val vbr = rop(g, cb + 2 << h) - vbCorr - if (vb < 400 || { - m10 = (vb * 107374183L >> 32).toInt // divide a positive int by 40 - val vb40 = m10 * 40 - val diff = vbl - vb40 - (vb40 - vbr + 40 ^ diff) >= 0 || { - m10 += ~diff >>> 31 - e10 += 1 - false - } - }) { - m10 = vb >> 2 - val vb4 = vb & 0xFFFFFFFC - var diff = vbl - vb4 - if ((vb4 - vbr + 4 ^ diff) >= 0) diff = (vb & 0x3) + (m10 & 0x1) - 3 - m10 += ~diff >>> 31 - e10 -= e10Corr - } - } - val ds = digits - val len = digitCount(m10) - e10 += len - 1 - if (e10 < -3 || e10 >= 7) { - val lastPos = writeSignificantFractionDigits(m10, pos + len, pos, buf, ds) - ByteArrayAccess.setShort(buf, pos, (buf(pos + 1) | 0x2E00).toShort) - pos = - if (lastPos < pos + 3) { - buf(lastPos) = '0' - lastPos + 1 - } else lastPos - ByteArrayAccess.setShort(buf, pos, 0x2D45) - pos += 1 - if (e10 < 0) { - e10 = -e10 - pos += 1 - } - if (e10 < 10) { - buf(pos) = (e10 + '0').toByte - pos + 1 - } else write2Digits(e10, pos, buf, ds) - } else if (e10 < 0) { - val dotPos = pos + 1 - ByteArrayAccess.setInt(buf, pos, 0x30303030) - pos -= e10 - val lastPos = writeSignificantFractionDigits(m10, pos + len, pos, buf, ds) - buf(dotPos) = '.' - lastPos - } else if (e10 < len - 1) { - val lastPos = writeSignificantFractionDigits(m10, pos + len, pos, buf, ds) - val bs = ByteArrayAccess.getLong(buf, pos) - val s = e10 << 3 - val m = 0xFFFFFFFFFFFF0000L << s - val d1 = (~m & bs) >> 8 - val d2 = 0x2E00L << s - val d3 = m & bs - ByteArrayAccess.setLong(buf, pos, d1 | d2 | d3) - lastPos - } else { - pos += len - writePositiveIntDigits(m10, pos, buf, ds) - ByteArrayAccess.setShort(buf, pos, 0x302E) - pos + 2 - } - } - } - - private[this] def rop(g: Long, cp: Int): Int = { - val x = Math.multiplyHigh(g, cp.toLong << 32) - (x >>> 31).toInt | -x.toInt >>> 31 - } - - // Based on the amazing work of Raffaello Giulietti - // "The Schubfach way to render doubles": https://drive.google.com/file/d/1luHhyQF9zKlM8yJ1nebU0OgVYhfC6CBN/view - // Sources with the license are here: https://github.com/c4f7fcce9cb06515/Schubfach/blob/3c92d3c9b1fead540616c918cdfef432bca53dfa/todec/src/math/DoubleToDecimal.java - private[this] def writeDouble(x: Double): Unit = count = { - val bits = java.lang.Double.doubleToRawLongBits(x) - var pos = ensureBufCapacity(24) - val buf = this.buf - if (bits < 0) { - buf(pos) = '-' - pos += 1 - } - if (x == 0.0) { - ByteArrayAccess.setInt(buf, pos, 0x302E30) - pos + 3 - } else { - val ieeeExponent = (bits >> 52).toInt & 0x7FF - val ieeeMantissa = bits & 0xFFFFFFFFFFFFFL - var e2 = ieeeExponent - 1075 - var m2 = ieeeMantissa | 0x10000000000000L - var m10 = 0L - var e10 = 0 - if (e2 == 0) m10 = m2 - else if (e2 >= -52 && e2 < 0 && m2 << e2 == 0) m10 = m2 >> -e2 - else { - var e10Corr, e2Corr = 0 - var cblCorr = 2 - if (ieeeExponent == 0) { - e2 = -1074 - m2 = ieeeMantissa - if (ieeeMantissa < 3) { - m2 *= 10 - e10Corr = 1 - } - } else if (ieeeExponent == 2047) illegalNumberError(x) - else if (ieeeMantissa == 0 && ieeeExponent > 1) { - e2Corr = 131007 - cblCorr = 1 - } - e10 = e2 * 315653 - e2Corr >> 20 - val i = e10 + 324 << 1 - val g1 = gs(i) - val g0 = gs(i + 1) - val h = (e10 * -108853 >> 15) + e2 + 2 - val cb = m2 << 2 - val vbCorr = (m2.toInt & 0x1) - 1 - val vb = rop(g1, g0, cb << h) - val vbl = rop(g1, g0, cb - cblCorr << h) + vbCorr - val vbr = rop(g1, g0, cb + 2 << h) - vbCorr - if (vb < 400 || { - m10 = Math.multiplyHigh(vb, 461168601842738792L) // divide a positive long by 40 - val vb40 = m10 * 40 - val diff = (vbl - vb40).toInt - ((vb40 - vbr).toInt + 40 ^ diff) >= 0 || { - m10 += ~diff >>> 31 - e10 += 1 - false - } - }) { - m10 = vb >> 2 - val vb4 = vb & 0xFFFFFFFFFFFFFFFCL - var diff = (vbl - vb4).toInt - if (((vb4 - vbr).toInt + 4 ^ diff) >= 0) diff = (vb.toInt & 0x3) + (m10.toInt & 0x1) - 3 - m10 += ~diff >>> 31 - e10 -= e10Corr - } - } - val ds = digits - val len = digitCount(m10) - e10 += len - 1 - if (e10 < -3 || e10 >= 7) { - val lastPos = writeSignificantFractionDigits(m10, pos + len, pos, buf, ds) - ByteArrayAccess.setShort(buf, pos, (buf(pos + 1) | 0x2E00).toShort) - pos = - if (lastPos < pos + 3) { - buf(lastPos) = '0' - lastPos + 1 - } else lastPos - ByteArrayAccess.setShort(buf, pos, 0x2D45) - pos += 1 - if (e10 < 0) { - e10 = -e10 - pos += 1 - } - if (e10 < 10) { - buf(pos) = (e10 + '0').toByte - pos + 1 - } else if (e10 < 100) write2Digits(e10, pos, buf, ds) - else write3Digits(e10, pos, buf, ds) - } else if (e10 < 0) { - val dotPos = pos + 1 - ByteArrayAccess.setInt(buf, pos, 0x30303030) - pos -= e10 - val lastPos = writeSignificantFractionDigits(m10, pos + len, pos, buf, ds) - buf(dotPos) = '.' - lastPos - } else if (e10 < len - 1) { - val lastPos = writeSignificantFractionDigits(m10, pos + len, pos, buf, ds) - val bs = ByteArrayAccess.getLong(buf, pos) - val s = e10 << 3 - val m = 0xFFFFFFFFFFFF0000L << s - val d1 = (~m & bs) >> 8 - val d2 = 0x2E00L << s - val d3 = m & bs - ByteArrayAccess.setLong(buf, pos, d1 | d2 | d3) - lastPos - } else { - pos += len - writePositiveIntDigits(m10.toInt, pos, buf, ds) - ByteArrayAccess.setShort(buf, pos, 0x302E) - pos + 2 - } - } - } - - private[this] def rop(g1: Long, g0: Long, cp: Long): Long = { - val x = Math.multiplyHigh(g0, cp) + (g1 * cp >>> 1) - Math.multiplyHigh(g1, cp) + (x >>> 63) | (-x ^ x) >>> 63 - } - - // Adoption of a nice trick from Daniel Lemire's blog that works for numbers up to 10^18: - // https://lemire.me/blog/2021/06/03/computing-the-number-of-digits-of-an-integer-even-faster/ - private[this] def digitCount(q0: Long): Int = (offsets(java.lang.Long.numberOfLeadingZeros(q0)) + q0 >> 58).toInt - - private[this] def writeSignificantFractionDigits(q: Long, p: Int, pl: Int, buf: Array[Byte], ds: Array[Short]): Int = { - var q0 = q.toInt - var pos = p - var posLim = pl - if (q0 != q) { - val q1 = (Math.multiplyHigh(q, 6189700196426901375L) >>> 25).toInt // divide a positive long by 100000000 - val r1 = (q - q1 * 100000000L).toInt - val posm8 = pos - 8 - if (r1 == 0) { - q0 = q1 - pos = posm8 - } else { - writeFractionDigits(q1, posm8, posLim, buf, ds) - q0 = r1 - posLim = posm8 - } - } - writeSignificantFractionDigits(q0, pos, posLim, buf, ds) - } - - private[this] def writeSignificantFractionDigits(q: Int, p: Int, posLim: Int, buf: Array[Byte], ds: Array[Short]): Int = { - var q0 = q - var q1 = 0 - var pos = p - while ({ - val qp = q0 * 1374389535L - q1 = (qp >> 37).toInt // divide a positive int by 100 - (qp & 0x1FC0000000L) == 0 // check if q is divisible by 100 - }) { - q0 = q1 - pos -= 2 - } - val d = ds(q0 - q1 * 100) - ByteArrayAccess.setShort(buf, pos - 1, d) - writeFractionDigits(q1, pos - 2, posLim, buf, ds) - pos + ((0x3039 - d) >>> 31) - } - - private[this] def writeFractionDigits(q: Int, p: Int, posLim: Int, buf: Array[Byte], ds: Array[Short]): Unit = { - var q0 = q - var pos = p - while (pos > posLim) { - val q1 = (q0 * 1374389535L >> 37).toInt // divide a positive int by 100 - ByteArrayAccess.setShort(buf, pos - 1, ds(q0 - q1 * 100)) - q0 = q1 - pos -= 2 - } - } - - private[this] def writePositiveIntDigits(q: Int, p: Int, buf: Array[Byte], ds: Array[Short]): Unit = { - var q0 = q - var pos = p - while ({ - pos -= 2 - q0 >= 100 - }) { - val q1 = (q0 * 1374389535L >> 37).toInt // divide a positive int by 100 - ByteArrayAccess.setShort(buf, pos, ds(q0 - q1 * 100)) - q0 = q1 - } - if (q0 < 10) buf(pos + 1) = (q0 + '0').toByte - else ByteArrayAccess.setShort(buf, pos, ds(q0)) - } - - private[this] def illegalNumberError(x: Double): Nothing = encodeError("illegal number: " + x) - - private[this] def ensureBufCapacity(required: Int): Int = { - val pos = count - if (pos + required <= limit) pos - else flushAndGrowBuf(required, pos) - } - - private[this] def flushAndGrowBuf(required: Int, pos: Int): Int = - if (bbuf ne null) { - bbuf.put(buf, 0, pos) - if (required > limit) growBuf(required) - 0 - } else if (out ne null) { - out.write(buf, 0, pos) - if (required > limit) growBuf(required) - 0 - } else if (disableBufGrowing) throw new ArrayIndexOutOfBoundsException("`buf` length exceeded") - else { - growBuf(pos + required) - pos - } - - private[this] def growBuf(required: Int): Unit = - setBuf(java.util.Arrays.copyOf(buf, (-1 >>> Integer.numberOfLeadingZeros(limit | required)) + 1)) - - private[this] def reallocateBufToPreferredSize(): Unit = setBuf(new Array[Byte](config.preferredBufSize)) - - private[this] def setBuf(buf: Array[Byte]): Unit = { - this.buf = buf - limit = buf.length - } -} - -object JsonWriter { - /* Use the following code to generate `escapedChars` in Scala REPL: - val es = new Array[Byte](128) - java.util.Arrays.fill(es, 0, 32, -1: Byte) - es('\n') = 'n' - es('\r') = 'r' - es('\t') = 't' - es('\b') = 'b' - es('\f') = 'f' - es('\\') = '\\' - es('\"') = '"' - es(127) = -1 - es.grouped(16).map(_.mkString(", ")).mkString("Array(\n", ",\n", "\n)") - */ - private final val escapedChars: Array[Byte] = Array( - -1, -1, -1, -1, -1, -1, -1, -1, 98, 116, 110, -1, 102, 114, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 0, 0, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 92, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1 - ) - private final val offsets = Array( - 5088146770730811392L, 5088146770730811392L, 5088146770730811392L, 5088146770730811392L, - 5088146770730811392L, 5088146770730811392L, 5088146770730811392L, 5088146770730811392L, - 4889916394579099648L, 4889916394579099648L, 4889916394579099648L, 4610686018427387904L, - 4610686018427387904L, 4610686018427387904L, 4610686018427387904L, 4323355642275676160L, - 4323355642275676160L, 4323355642275676160L, 4035215266123964416L, 4035215266123964416L, - 4035215266123964416L, 3746993889972252672L, 3746993889972252672L, 3746993889972252672L, - 3746993889972252672L, 3458764413820540928L, 3458764413820540928L, 3458764413820540928L, - 3170534127668829184L, 3170534127668829184L, 3170534127668829184L, 2882303760517117440L, - 2882303760517117440L, 2882303760517117440L, 2882303760517117440L, 2594073385265405696L, - 2594073385265405696L, 2594073385265405696L, 2305843009203693952L, 2305843009203693952L, - 2305843009203693952L, 2017612633060982208L, 2017612633060982208L, 2017612633060982208L, - 2017612633060982208L, 1729382256910170464L, 1729382256910170464L, 1729382256910170464L, - 1441151880758548720L, 1441151880758548720L, 1441151880758548720L, 1152921504606845976L, - 1152921504606845976L, 1152921504606845976L, 1152921504606845976L, 864691128455135132L, - 864691128455135132L, 864691128455135132L, 576460752303423478L, 576460752303423478L, - 576460752303423478L, 576460752303423478L, 576460752303423478L, 576460752303423478L, - 576460752303423478L) - /* Use the following code to generate `digits` in Scala REPL: - val ds = new Array[Short](100) - var i, j = 0 - while (j < 10) { - var k = 0 - while (k < 10) { - ds(i) = (((k + '0') << 8) + (j + '0')).toShort - i += 1 - k += 1 - } - j += 1 - } - ds.grouped(10).map(_.mkString(", ")).mkString("Array(\n", ",\n", "\n)") - */ - private final val digits: Array[Short] = Array( - 12336, 12592, 12848, 13104, 13360, 13616, 13872, 14128, 14384, 14640, - 12337, 12593, 12849, 13105, 13361, 13617, 13873, 14129, 14385, 14641, - 12338, 12594, 12850, 13106, 13362, 13618, 13874, 14130, 14386, 14642, - 12339, 12595, 12851, 13107, 13363, 13619, 13875, 14131, 14387, 14643, - 12340, 12596, 12852, 13108, 13364, 13620, 13876, 14132, 14388, 14644, - 12341, 12597, 12853, 13109, 13365, 13621, 13877, 14133, 14389, 14645, - 12342, 12598, 12854, 13110, 13366, 13622, 13878, 14134, 14390, 14646, - 12343, 12599, 12855, 13111, 13367, 13623, 13879, 14135, 14391, 14647, - 12344, 12600, 12856, 13112, 13368, 13624, 13880, 14136, 14392, 14648, - 12345, 12601, 12857, 13113, 13369, 13625, 13881, 14137, 14393, 14649 - ) - /* Use the following code to generate `lowerCaseHexDigits` in Scala REPL: - val ds = new Array[Short](256) - var i, j = 0 - while (j < 16) { - val d1 = - if (j <= 9) j + '0' - else j + 'a' - 10 - var k = 0 - while (k < 16) { - val d2 = - if (k <= 9) k + '0' - else k + 'a' - 10 - ds(i) = ((d2 << 8) + d1).toShort - i += 1 - k += 1 - } - j += 1 - } - ds.grouped(16).map(_.mkString(", ")).mkString("Array(\n", ",\n", "\n)") - */ - private final val lowerCaseHexDigits: Array[Short] = Array( - 12336, 12592, 12848, 13104, 13360, 13616, 13872, 14128, 14384, 14640, 24880, 25136, 25392, 25648, 25904, 26160, - 12337, 12593, 12849, 13105, 13361, 13617, 13873, 14129, 14385, 14641, 24881, 25137, 25393, 25649, 25905, 26161, - 12338, 12594, 12850, 13106, 13362, 13618, 13874, 14130, 14386, 14642, 24882, 25138, 25394, 25650, 25906, 26162, - 12339, 12595, 12851, 13107, 13363, 13619, 13875, 14131, 14387, 14643, 24883, 25139, 25395, 25651, 25907, 26163, - 12340, 12596, 12852, 13108, 13364, 13620, 13876, 14132, 14388, 14644, 24884, 25140, 25396, 25652, 25908, 26164, - 12341, 12597, 12853, 13109, 13365, 13621, 13877, 14133, 14389, 14645, 24885, 25141, 25397, 25653, 25909, 26165, - 12342, 12598, 12854, 13110, 13366, 13622, 13878, 14134, 14390, 14646, 24886, 25142, 25398, 25654, 25910, 26166, - 12343, 12599, 12855, 13111, 13367, 13623, 13879, 14135, 14391, 14647, 24887, 25143, 25399, 25655, 25911, 26167, - 12344, 12600, 12856, 13112, 13368, 13624, 13880, 14136, 14392, 14648, 24888, 25144, 25400, 25656, 25912, 26168, - 12345, 12601, 12857, 13113, 13369, 13625, 13881, 14137, 14393, 14649, 24889, 25145, 25401, 25657, 25913, 26169, - 12385, 12641, 12897, 13153, 13409, 13665, 13921, 14177, 14433, 14689, 24929, 25185, 25441, 25697, 25953, 26209, - 12386, 12642, 12898, 13154, 13410, 13666, 13922, 14178, 14434, 14690, 24930, 25186, 25442, 25698, 25954, 26210, - 12387, 12643, 12899, 13155, 13411, 13667, 13923, 14179, 14435, 14691, 24931, 25187, 25443, 25699, 25955, 26211, - 12388, 12644, 12900, 13156, 13412, 13668, 13924, 14180, 14436, 14692, 24932, 25188, 25444, 25700, 25956, 26212, - 12389, 12645, 12901, 13157, 13413, 13669, 13925, 14181, 14437, 14693, 24933, 25189, 25445, 25701, 25957, 26213, - 12390, 12646, 12902, 13158, 13414, 13670, 13926, 14182, 14438, 14694, 24934, 25190, 25446, 25702, 25958, 26214 - ) - /* Use the following code to generate `upperCaseHexDigits` in Scala REPL: - val ds = new Array[Short](256) - var i, j = 0 - while (j < 16) { - val d1 = - if (j <= 9) j + '0' - else j + 'A' - 10 - var k = 0 - while (k < 16) { - val d2 = - if (k <= 9) k + '0' - else k + 'A' - 10 - ds(i) = ((d2 << 8) + d1).toShort - i += 1 - k += 1 - } - j += 1 - } - ds.grouped(16).map(_.mkString(", ")).mkString("Array(\n", ",\n", "\n)") - */ - private final val upperCaseHexDigits: Array[Short] = Array( - 12336, 12592, 12848, 13104, 13360, 13616, 13872, 14128, 14384, 14640, 16688, 16944, 17200, 17456, 17712, 17968, - 12337, 12593, 12849, 13105, 13361, 13617, 13873, 14129, 14385, 14641, 16689, 16945, 17201, 17457, 17713, 17969, - 12338, 12594, 12850, 13106, 13362, 13618, 13874, 14130, 14386, 14642, 16690, 16946, 17202, 17458, 17714, 17970, - 12339, 12595, 12851, 13107, 13363, 13619, 13875, 14131, 14387, 14643, 16691, 16947, 17203, 17459, 17715, 17971, - 12340, 12596, 12852, 13108, 13364, 13620, 13876, 14132, 14388, 14644, 16692, 16948, 17204, 17460, 17716, 17972, - 12341, 12597, 12853, 13109, 13365, 13621, 13877, 14133, 14389, 14645, 16693, 16949, 17205, 17461, 17717, 17973, - 12342, 12598, 12854, 13110, 13366, 13622, 13878, 14134, 14390, 14646, 16694, 16950, 17206, 17462, 17718, 17974, - 12343, 12599, 12855, 13111, 13367, 13623, 13879, 14135, 14391, 14647, 16695, 16951, 17207, 17463, 17719, 17975, - 12344, 12600, 12856, 13112, 13368, 13624, 13880, 14136, 14392, 14648, 16696, 16952, 17208, 17464, 17720, 17976, - 12345, 12601, 12857, 13113, 13369, 13625, 13881, 14137, 14393, 14649, 16697, 16953, 17209, 17465, 17721, 17977, - 12353, 12609, 12865, 13121, 13377, 13633, 13889, 14145, 14401, 14657, 16705, 16961, 17217, 17473, 17729, 17985, - 12354, 12610, 12866, 13122, 13378, 13634, 13890, 14146, 14402, 14658, 16706, 16962, 17218, 17474, 17730, 17986, - 12355, 12611, 12867, 13123, 13379, 13635, 13891, 14147, 14403, 14659, 16707, 16963, 17219, 17475, 17731, 17987, - 12356, 12612, 12868, 13124, 13380, 13636, 13892, 14148, 14404, 14660, 16708, 16964, 17220, 17476, 17732, 17988, - 12357, 12613, 12869, 13125, 13381, 13637, 13893, 14149, 14405, 14661, 16709, 16965, 17221, 17477, 17733, 17989, - 12358, 12614, 12870, 13126, 13382, 13638, 13894, 14150, 14406, 14662, 16710, 16966, 17222, 17478, 17734, 17990 - ) - /* Use the following code to generate `base64Digits` in Scala REPL: - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".getBytes - .grouped(16).map(_.mkString(", ")).mkString("Array(\n", ",\n", "\n)") - */ - private final val base64Digits: Array[Byte] = Array( - 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, - 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, - 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, - 119, 120, 121, 122, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 43, 47 - ) - /* Use the following code to generate `base64UrlDigits` in Scala REPL: - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".getBytes - .grouped(16).map(_.mkString(", ")).mkString("Array(\n", ",\n", "\n)") - */ - private final val base64UrlDigits: Array[Byte] = Array( - 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, - 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, - 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, - 119, 120, 121, 122, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 45, 95 - ) - /* Use the following code to generate `gs` in Scala REPL: - val gs = new Array[Long](1234) - var i = 0 - var pow5 = BigInt(1) - while (i < 650) { - val av = (pow5 >> (pow5.bitLength - 126)) + 1 - gs(648 - i) = (av >> 63).longValue & 0x7FFFFFFFFFFFFFFFL - gs(649 - i) = av.longValue & 0x7FFFFFFFFFFFFFFFL - pow5 *= 5 - i += 2 - } - pow5 = BigInt(5) - while (i < 1234) { - val inv = ((BigInt(1) << (pow5.bitLength + 125)) / pow5) + 1 - gs(i) = (inv >> 63).longValue & 0x7FFFFFFFFFFFFFFFL - gs(i + 1) = inv.longValue & 0x7FFFFFFFFFFFFFFFL - pow5 *= 5 - i += 2 - } - gs.grouped(4).map(_.mkString("L, ")).mkString("Array(\n", "L,\n", "L\n)") - */ - private final val gs: Array[Long] = Array( - 5696189077778435540L, 6557778377634271669L, 9113902524445496865L, 1269073367360058862L, - 7291122019556397492L, 1015258693888047090L, 5832897615645117993L, 6346230177223303157L, - 4666318092516094394L, 8766332956520552849L, 7466108948025751031L, 8492109508320019073L, - 5972887158420600825L, 4949013199285060097L, 4778309726736480660L, 3959210559428048077L, - 7645295562778369056L, 6334736895084876923L, 6116236450222695245L, 3223115108696946377L, - 4892989160178156196L, 2578492086957557102L, 7828782656285049914L, 436238524390181040L, - 6263026125028039931L, 2193665226883099993L, 5010420900022431944L, 9133629810990300641L, - 8016673440035891111L, 9079784475471615541L, 6413338752028712889L, 5419153173006337271L, - 5130671001622970311L, 6179996945776024979L, 8209073602596752498L, 6198646298499729642L, - 6567258882077401998L, 8648265853541694037L, 5253807105661921599L, 1384589460720489745L, - 8406091369059074558L, 5904691951894693915L, 6724873095247259646L, 8413102376257665455L, - 5379898476197807717L, 4885807493635177203L, 8607837561916492348L, 438594360332462878L, - 6886270049533193878L, 4040224303007880625L, 5509016039626555102L, 6921528257148214824L, - 8814425663402488164L, 3695747581953323071L, 7051540530721990531L, 4801272472933613619L, - 5641232424577592425L, 1996343570975935733L, 9025971879324147880L, 3194149713561497173L, - 7220777503459318304L, 2555319770849197738L, 5776622002767454643L, 3888930224050313352L, - 4621297602213963714L, 6800492993982161005L, 7394076163542341943L, 5346765568258592123L, - 5915260930833873554L, 7966761269348784022L, 4732208744667098843L, 8218083422849982379L, - 7571533991467358150L, 2080887032334240837L, 6057227193173886520L, 1664709625867392670L, - 4845781754539109216L, 1331767700693914136L, 7753250807262574745L, 7664851543223128102L, - 6202600645810059796L, 6131881234578502482L, 4962080516648047837L, 3060830580291846824L, - 7939328826636876539L, 6742003335837910079L, 6351463061309501231L, 7238277076041283225L, - 5081170449047600985L, 3945947253462071419L, 8129872718476161576L, 6313515605539314269L, - 6503898174780929261L, 3206138077060496254L, 5203118539824743409L, 720236054277441842L, - 8324989663719589454L, 4841726501585817270L, 6659991730975671563L, 5718055608639608977L, - 5327993384780537250L, 8263793301653597505L, 8524789415648859601L, 3998697245790980200L, - 6819831532519087681L, 1354283389261828999L, 5455865226015270144L, 8462124340893283845L, - 8729384361624432231L, 8005375723316388668L, 6983507489299545785L, 4559626171282155773L, - 5586805991439636628L, 3647700937025724618L, 8938889586303418605L, 3991647091870204227L, - 7151111669042734884L, 3193317673496163382L, 5720889335234187907L, 4399328546167885867L, - 9153422936374700651L, 8883600081239572549L, 7322738349099760521L, 5262205657620702877L, - 5858190679279808417L, 2365090118725607140L, 4686552543423846733L, 7426095317093351197L, - 7498484069478154774L, 813706063123630946L, 5998787255582523819L, 2495639257869859918L, - 4799029804466019055L, 3841185813666843096L, 7678447687145630488L, 6145897301866948954L, - 6142758149716504390L, 8606066656235469486L, 4914206519773203512L, 6884853324988375589L, - 7862730431637125620L, 3637067690497580296L, 6290184345309700496L, 2909654152398064237L, - 5032147476247760397L, 483048914547496228L, 8051435961996416635L, 2617552670646949126L, - 6441148769597133308L, 2094042136517559301L, 5152919015677706646L, 5364582523955957764L, - 8244670425084330634L, 4893983223587622099L, 6595736340067464507L, 5759860986241052841L, - 5276589072053971606L, 918539974250931950L, 8442542515286354569L, 7003687180914356604L, - 6754034012229083655L, 7447624152102440445L, 5403227209783266924L, 5958099321681952356L, - 8645163535653227079L, 3998935692578258285L, 6916130828522581663L, 5043822961433561789L, - 5532904662818065330L, 7724407183888759755L, 8852647460508904529L, 3135679457367239799L, - 7082117968407123623L, 4353217973264747001L, 5665694374725698898L, 7171923193353707924L, - 9065110999561118238L, 407030665140201709L, 7252088799648894590L, 4014973346854071690L, - 5801671039719115672L, 3211978677483257352L, 4641336831775292537L, 8103606164099471367L, - 7426138930840468060L, 5587072233075333540L, 5940911144672374448L, 4469657786460266832L, - 4752728915737899558L, 7265075043910123789L, 7604366265180639294L, 556073626030467093L, - 6083493012144511435L, 2289533308195328836L, 4866794409715609148L, 1831626646556263069L, - 7786871055544974637L, 1085928227119065748L, 6229496844435979709L, 6402765803808118083L, - 4983597475548783767L, 6966887050417449628L, 7973755960878054028L, 3768321651184098759L, - 6379004768702443222L, 6704006135689189330L, 5103203814961954578L, 1673856093809441141L, - 8165126103939127325L, 833495342724150664L, 6532100883151301860L, 666796274179320531L, - 5225680706521041488L, 533437019343456425L, 8361089130433666380L, 8232196860433350926L, - 6688871304346933104L, 6585757488346680741L, 5351097043477546483L, 7113280398048299755L, - 8561755269564074374L, 313202192651548637L, 6849404215651259499L, 2095236161492194072L, - 5479523372521007599L, 3520863336564710419L, 8767237396033612159L, 99358116390671185L, - 7013789916826889727L, 1924160900483492110L, 5611031933461511781L, 7073351942499659173L, - 8977651093538418850L, 7628014293257544353L, 7182120874830735080L, 6102411434606035483L, - 5745696699864588064L, 4881929147684828386L, 9193114719783340903L, 2277063414182859933L, - 7354491775826672722L, 5510999546088198270L, 5883593420661338178L, 719450822128648293L, - 4706874736529070542L, 4264909472444828957L, 7530999578446512867L, 8668529563282681493L, - 6024799662757210294L, 3245474835884234871L, 4819839730205768235L, 4441054276078343059L, - 7711743568329229176L, 7105686841725348894L, 6169394854663383341L, 3839875066009323953L, - 4935515883730706673L, 1227225645436504001L, 7896825413969130677L, 118886625327451240L, - 6317460331175304541L, 5629132522374826477L, 5053968264940243633L, 2658631610528906020L, - 8086349223904389813L, 2409136169475294470L, 6469079379123511850L, 5616657750322145900L, - 5175263503298809480L, 4493326200257716720L, 8280421605278095168L, 7189321920412346751L, - 6624337284222476135L, 217434314217011916L, 5299469827377980908L, 173947451373609533L, - 8479151723804769452L, 7657013551681595899L, 6783321379043815562L, 2436262026603366396L, - 5426657103235052449L, 7483032843395558602L, 8682651365176083919L, 6438829327320028278L, - 6946121092140867135L, 6995737869226977784L, 5556896873712693708L, 5596590295381582227L, - 8891034997940309933L, 7109870065239576402L, 7112827998352247947L, 153872830078795637L, - 5690262398681798357L, 5657121486175901994L, 9104419837890877372L, 1672696748397622544L, - 7283535870312701897L, 6872180620830963520L, 5826828696250161518L, 1808395681922860493L, - 4661462957000129214L, 5136065360280198718L, 7458340731200206743L, 2683681354335452463L, - 5966672584960165394L, 5836293898210272294L, 4773338067968132315L, 6513709525939172997L, - 7637340908749011705L, 1198563204647900987L, 6109872726999209364L, 958850563718320789L, - 4887898181599367491L, 2611754858345611793L, 7820637090558987986L, 489458958611068546L, - 6256509672447190388L, 7770264796372675483L, 5005207737957752311L, 682188614985274902L, - 8008332380732403697L, 6625525006089305327L, 6406665904585922958L, 1611071190129533939L, - 5125332723668738366L, 4978205766845537474L, 8200532357869981386L, 4275780412210949635L, - 6560425886295985109L, 1575949922397804547L, 5248340709036788087L, 3105434345289198799L, - 8397345134458860939L, 6813369359833673240L, 6717876107567088751L, 7295369895237893754L, - 5374300886053671001L, 3991621508819359841L, 8598881417685873602L, 2697245599369065423L, - 6879105134148698881L, 7691819701608117823L, 5503284107318959105L, 4308781353915539097L, - 8805254571710334568L, 6894050166264862555L, 7044203657368267654L, 9204588947753800367L, - 5635362925894614123L, 9208345565573995455L, 9016580681431382598L, 3665306460692661759L, - 7213264545145106078L, 6621593983296039730L, 5770611636116084862L, 8986624001378742108L, - 4616489308892867890L, 3499950386361083363L, 7386382894228588624L, 5599920618177733380L, - 5909106315382870899L, 6324610901913141866L, 4727285052306296719L, 6904363128901468655L, - 7563656083690074751L, 5512957784129484362L, 6050924866952059801L, 2565691819932632328L, - 4840739893561647841L, 207879048575150701L, 7745183829698636545L, 5866629699833106606L, - 6196147063758909236L, 4693303759866485285L, 4956917651007127389L, 1909968600522233067L, - 7931068241611403822L, 6745298575577483229L, 6344854593289123058L, 1706890045720076260L, - 5075883674631298446L, 5054860851317971332L, 8121413879410077514L, 4398428547366843807L, - 6497131103528062011L, 5363417245264430207L, 5197704882822449609L, 2446059388840589004L, - 8316327812515919374L, 7603043836886852730L, 6653062250012735499L, 7927109476880437346L, - 5322449800010188399L, 8186361988875305038L, 8515919680016301439L, 7564155960087622576L, - 6812735744013041151L, 7895999175441053223L, 5450188595210432921L, 4472124932981887417L, - 8720301752336692674L, 3466051078029109543L, 6976241401869354139L, 4617515269794242796L, - 5580993121495483311L, 5538686623206349399L, 8929588994392773298L, 5172549782388248714L, - 7143671195514218638L, 7827388640652509295L, 5714936956411374911L, 727887690409141951L, - 9143899130258199857L, 6698643526767492606L, 7315119304206559886L, 1669566006672083762L, - 5852095443365247908L, 8714350434821487656L, 4681676354692198327L, 1437457125744324640L, - 7490682167507517323L, 4144605808561874585L, 5992545734006013858L, 7005033461591409992L, - 4794036587204811087L, 70003547160262509L, 7670458539527697739L, 1956680082827375175L, - 6136366831622158191L, 3410018473632855302L, 4909093465297726553L, 883340371535329080L, - 7854549544476362484L, 8792042223940347174L, 6283639635581089987L, 8878308186523232901L, - 5026911708464871990L, 3413297734476675998L, 8043058733543795184L, 5461276375162681596L, - 6434446986835036147L, 6213695507501100438L, 5147557589468028918L, 1281607591258970028L, - 8236092143148846269L, 205897738643396882L, 6588873714519077015L, 2009392598285672668L, - 5271098971615261612L, 1607514078628538134L, 8433758354584418579L, 4416696933176616176L, - 6747006683667534863L, 5378031953912248102L, 5397605346934027890L, 7991774377871708805L, - 8636168555094444625L, 3563466967739958280L, 6908934844075555700L, 2850773574191966624L, - 5527147875260444560L, 2280618859353573299L, 8843436600416711296L, 3648990174965717279L, - 7074749280333369037L, 1074517732601618662L, 5659799424266695229L, 6393637408194160414L, - 9055679078826712367L, 4695796630997791177L, 7244543263061369894L, 67288490056322619L, - 5795634610449095915L, 1898505199416013257L, 4636507688359276732L, 1518804159532810606L, - 7418412301374842771L, 4274761062623452130L, 5934729841099874217L, 1575134442727806543L, - 4747783872879899373L, 6794130776295110719L, 7596454196607838997L, 9025934834701221989L, - 6077163357286271198L, 3531399053019067268L, 4861730685829016958L, 6514468057157164137L, - 7778769097326427133L, 8578474484080507458L, 6223015277861141707L, 1328756365151540482L, - 4978412222288913365L, 6597028314234097870L, 7965459555662261385L, 1331873265919780784L, - 6372367644529809108L, 1065498612735824627L, 5097894115623847286L, 4541747704930570025L, - 8156630584998155658L, 3577447513147001717L, 6525304467998524526L, 6551306825259511697L, - 5220243574398819621L, 3396371052836654196L, 8352389719038111394L, 1744844869796736390L, - 6681911775230489115L, 3240550303208344274L, 5345529420184391292L, 2592440242566675419L, - 8552847072295026067L, 5992578795477635832L, 6842277657836020854L, 1104714221640198342L, - 5473822126268816683L, 2728445784683113836L, 8758115402030106693L, 2520838848122026975L, - 7006492321624085354L, 5706019893239531903L, 5605193857299268283L, 6409490321962580684L, - 8968310171678829253L, 8410510107769173933L, 7174648137343063403L, 1194384864102473662L, - 5739718509874450722L, 4644856706023889253L, 9183549615799121156L, 53073100154402158L, - 7346839692639296924L, 7421156109607342373L, 5877471754111437539L, 7781599295056829060L, - 4701977403289150031L, 8069953843416418410L, 7523163845262640050L, 9222577334724359132L, - 6018531076210112040L, 7378061867779487306L, 4814824860968089632L, 5902449494223589845L, - 7703719777548943412L, 2065221561273923105L, 6162975822039154729L, 7186200471132003969L, - 4930380657631323783L, 7593634784276558337L, 7888609052210118054L, 1081769210616762369L, - 6310887241768094443L, 2710089775864365057L, 5048709793414475554L, 5857420635433402369L, - 8077935669463160887L, 3837849794580578305L, 6462348535570528709L, 8604303057777328129L, - 5169878828456422967L, 8728116853592817665L, 8271806125530276748L, 6586289336264687617L, - 6617444900424221398L, 8958380283753660417L, 5293955920339377119L, 1632681004890062849L, - 8470329472543003390L, 6301638422566010881L, 6776263578034402712L, 5041310738052808705L, - 5421010862427522170L, 343699775700336641L, 8673617379884035472L, 549919641120538625L, - 6938893903907228377L, 5973958935009296385L, 5551115123125782702L, 1089818333265526785L, - 8881784197001252323L, 3588383740595798017L, 7105427357601001858L, 6560055807218548737L, - 5684341886080801486L, 8937393460516749313L, 9094947017729282379L, 1387108685230112769L, - 7275957614183425903L, 2954361355555045377L, 5820766091346740722L, 6052837899185946625L, - 4656612873077392578L, 1152921504606846977L, 7450580596923828125L, 1L, - 5960464477539062500L, 1L, 4768371582031250000L, 1L, - 7629394531250000000L, 1L, 6103515625000000000L, 1L, - 4882812500000000000L, 1L, 7812500000000000000L, 1L, - 6250000000000000000L, 1L, 5000000000000000000L, 1L, - 8000000000000000000L, 1L, 6400000000000000000L, 1L, - 5120000000000000000L, 1L, 8192000000000000000L, 1L, - 6553600000000000000L, 1L, 5242880000000000000L, 1L, - 8388608000000000000L, 1L, 6710886400000000000L, 1L, - 5368709120000000000L, 1L, 8589934592000000000L, 1L, - 6871947673600000000L, 1L, 5497558138880000000L, 1L, - 8796093022208000000L, 1L, 7036874417766400000L, 1L, - 5629499534213120000L, 1L, 9007199254740992000L, 1L, - 7205759403792793600L, 1L, 5764607523034234880L, 1L, - 4611686018427387904L, 1L, 7378697629483820646L, 3689348814741910324L, - 5902958103587056517L, 1106804644422573097L, 4722366482869645213L, 6419466937650923963L, - 7555786372591432341L, 8426472692870523179L, 6044629098073145873L, 4896503746925463381L, - 4835703278458516698L, 7606551812282281028L, 7737125245533626718L, 1102436455425918676L, - 6189700196426901374L, 4571297979082645264L, 4951760157141521099L, 5501712790637071373L, - 7922816251426433759L, 3268717242906448711L, 6338253001141147007L, 4459648201696114131L, - 5070602400912917605L, 9101741783469756789L, 8112963841460668169L, 5339414816696835055L, - 6490371073168534535L, 6116206260728423206L, 5192296858534827628L, 4892965008582738565L, - 8307674973655724205L, 5984069606361426541L, 6646139978924579364L, 4787255685089141233L, - 5316911983139663491L, 5674478955442268148L, 8507059173023461586L, 5389817513965718714L, - 6805647338418769269L, 2467179603801619810L, 5444517870735015415L, 3818418090412251009L, - 8711228593176024664L, 6109468944659601615L, 6968982874540819731L, 6732249563098636453L, - 5575186299632655785L, 3541125243107954001L, 8920298079412249256L, 5665800388972726402L, - 7136238463529799405L, 2687965903807225960L, 5708990770823839524L, 2150372723045780768L, - 9134385233318143238L, 7129945171615159552L, 7307508186654514591L, 169932915179262157L, - 5846006549323611672L, 7514643961627230372L, 4676805239458889338L, 2322366354559873974L, - 7482888383134222941L, 1871111759924843197L, 5986310706507378352L, 8875587037423695204L, - 4789048565205902682L, 3411120815197045840L, 7662477704329444291L, 7302467711686228506L, - 6129982163463555433L, 3997299761978027643L, 4903985730770844346L, 6887188624324332438L, - 7846377169233350954L, 7330152984177021577L, 6277101735386680763L, 7708796794712572423L, - 5021681388309344611L, 633014213657192454L, 8034690221294951377L, 6546845963964373411L, - 6427752177035961102L, 1548127956429588405L, 5142201741628768881L, 6772525587256536209L, - 8227522786606030210L, 7146692124868547611L, 6582018229284824168L, 5717353699894838089L, - 5265614583427859334L, 8263231774657780795L, 8424983333484574935L, 7687147617339583786L, - 6739986666787659948L, 6149718093871667029L, 5391989333430127958L, 8609123289839243947L, - 8627182933488204734L, 2706550819517059345L, 6901746346790563787L, 4009915062984602637L, - 5521397077432451029L, 8741955272500547595L, 8834235323891921647L, 8453105213888010667L, - 7067388259113537318L, 3073135356368498210L, 5653910607290829854L, 6147857099836708891L, - 9046256971665327767L, 4302548137625868741L, 7237005577332262213L, 8976061732213560478L, - 5789604461865809771L, 1646826163657982898L, 4631683569492647816L, 8696158560410206965L, - 7410693711188236507L, 1001132845059645012L, 5928554968950589205L, 6334929498160581494L, - 4742843975160471364L, 5067943598528465196L, 7588550360256754183L, 2574686535532678828L, - 6070840288205403346L, 5749098043168053386L, 4856672230564322677L, 2754604027163487547L, - 7770675568902916283L, 6252040850832535236L, 6216540455122333026L, 8690981495407938512L, - 4973232364097866421L, 5108110788955395648L, 7957171782556586274L, 4483628447586722714L, - 6365737426045269019L, 5431577165440333333L, 5092589940836215215L, 6189936139723221828L, - 8148143905337944345L, 680525786702379117L, 6518515124270355476L, 544420629361903293L, - 5214812099416284380L, 7814234132973343281L, 8343699359066055009L, 3279402575902573442L, - 6674959487252844007L, 4468196468093013915L, 5339967589802275205L, 9108580396587276617L, - 8543948143683640329L, 5350356597684866779L, 6835158514946912263L, 6124959685518848585L, - 5468126811957529810L, 8589316563156989191L, 8749002899132047697L, 4519534464196406897L, - 6999202319305638157L, 9149650793469991003L, 5599361855444510526L, 3630371820034082479L, - 8958978968711216842L, 2119246097312621643L, 7167183174968973473L, 7229420099962962799L, - 5733746539975178779L, 249512857857504755L, 9173994463960286046L, 4088569387313917931L, - 7339195571168228837L, 1426181102480179183L, 5871356456934583069L, 6674968104097008831L, - 4697085165547666455L, 7184648890648562227L, 7515336264876266329L, 2272066188182923754L, - 6012269011901013063L, 3662327357917294165L, 4809815209520810450L, 6619210701075745655L, - 7695704335233296721L, 1367365084866417240L, 6156563468186637376L, 8472589697376954439L, - 4925250774549309901L, 4933397350530608390L, 7880401239278895842L, 4204086946107063100L, - 6304320991423116673L, 8897292778998515965L, 5043456793138493339L, 1583811001085947287L, - 8069530869021589342L, 6223446416479425982L, 6455624695217271474L, 1289408318441630463L, - 5164499756173817179L, 2876201062124259532L, 8263199609878107486L, 8291270514140725574L, - 6610559687902485989L, 4788342003941625298L, 5288447750321988791L, 5675348010524255400L, - 8461516400515182066L, 5391208002096898316L, 6769213120412145653L, 2468291994306563491L, - 5415370496329716522L, 5663982410187161116L, 8664592794127546436L, 1683674226815637140L, - 6931674235302037148L, 8725637010936330358L, 5545339388241629719L, 1446486386636198802L, - 8872543021186607550L, 6003727033359828406L, 7098034416949286040L, 4802981626687862725L, - 5678427533559428832L, 3842385301350290180L, 9085484053695086131L, 7992490889531419449L, - 7268387242956068905L, 4549318304254180398L, 5814709794364855124L, 3639454643403344318L, - 4651767835491884099L, 4756238122093630616L, 7442828536787014559L, 2075957773236943501L, - 5954262829429611647L, 3505440625960509963L, 4763410263543689317L, 8338375722881273455L, - 7621456421669902908L, 5962703527126216881L, 6097165137335922326L, 8459511636442883828L, - 4877732109868737861L, 4922934901783351901L, 7804371375789980578L, 4187347028111452718L, - 6243497100631984462L, 7039226437231072498L, 4994797680505587570L, 1942032335042947675L, - 7991676288808940112L, 3107251736068716280L, 6393341031047152089L, 8019824610967838509L, - 5114672824837721671L, 8260534096145225969L, 8183476519740354675L, 304133702235675419L, - 6546781215792283740L, 243306961788540335L, 5237424972633826992L, 194645569430832268L, - 8379879956214123187L, 2156107318460286790L, 6703903964971298549L, 7258909076881094917L, - 5363123171977038839L, 7651801668875831096L, 8580997075163262143L, 6708859448088464268L, - 6864797660130609714L, 9056436373212681737L, 5491838128104487771L, 9089823505941100552L, - 8786941004967180435L, 1630996757909074751L, 7029552803973744348L, 1304797406327259801L, - 5623642243178995478L, 4733186739803718164L, 8997827589086392765L, 5728424376314993901L, - 7198262071269114212L, 4582739501051995121L, 5758609657015291369L, 9200214822954461581L, - 9213775451224466191L, 9186320494614273045L, 7371020360979572953L, 5504381988320463275L, - 5896816288783658362L, 8092854405398280943L, 4717453031026926690L, 2784934709576714431L, - 7547924849643082704L, 4455895535322743090L, 6038339879714466163L, 5409390835629149634L, - 4830671903771572930L, 8016861483245230030L, 7729075046034516689L, 3603606336337592240L, - 6183260036827613351L, 4727559476441028954L, 4946608029462090681L, 1937373173781868001L, - 7914572847139345089L, 8633820300163854287L, 6331658277711476071L, 8751730647502038591L, - 5065326622169180857L, 5156710110630675711L, 8104522595470689372L, 872038547525260492L, - 6483618076376551497L, 6231654060133073878L, 5186894461101241198L, 1295974433364548779L, - 8299031137761985917L, 228884686012322885L, 6639224910209588733L, 5717130970922723793L, - 5311379928167670986L, 8263053591480089358L, 8498207885068273579L, 308164894771456841L, - 6798566308054618863L, 2091206323188120634L, 5438853046443695090L, 5362313873292406831L, - 8702164874309912144L, 8579702197267850929L, 6961731899447929715L, 8708436165185235905L, - 5569385519558343772L, 6966748932148188724L, 8911016831293350036L, 3768100661953281312L, - 7128813465034680029L, 1169806122191669888L, 5703050772027744023L, 2780519305124291072L, - 9124881235244390437L, 2604156480827910553L, 7299904988195512349L, 7617348406775193928L, - 5839923990556409879L, 7938553132791110304L, 4671939192445127903L, 8195516913603843405L, - 7475102707912204646L, 2044780617540418478L, 5980082166329763716L, 9014522123516155429L, - 4784065733063810973L, 5366943291441969181L, 7654505172902097557L, 6742434858936195528L, - 6123604138321678046L, 1704599072407046100L, 4898883310657342436L, 8742376887409457526L, - 7838213297051747899L, 1075082168258445910L, 6270570637641398319L, 2704740141977711890L, - 5016456510113118655L, 4008466520953124674L, 8026330416180989848L, 6413546433524999478L, - 6421064332944791878L, 8820185961561909905L, 5136851466355833503L, 1522125547136662440L, - 8218962346169333605L, 590726468047704741L, 6575169876935466884L, 472581174438163793L, - 5260135901548373507L, 2222739346921486196L, 8416217442477397611L, 5401057362445333075L, - 6732973953981918089L, 2476171482585311299L, 5386379163185534471L, 3825611593439204201L, - 8618206661096855154L, 2431629734760816398L, 6894565328877484123L, 3789978195179608280L, - 5515652263101987298L, 6721331370885596947L, 8825043620963179677L, 8909455786045999954L, - 7060034896770543742L, 3438215814094889640L, 5648027917416434993L, 8284595873388777197L, - 9036844667866295990L, 2187306953196312545L, 7229475734293036792L, 1749845562557050036L, - 5783580587434429433L, 6933899672158505514L, 4626864469947543547L, 13096515613938926L, - 7402983151916069675L, 1865628832353257443L, 5922386521532855740L, 1492503065882605955L, - 4737909217226284592L, 1194002452706084764L, 7580654747562055347L, 3755078331700690783L, - 6064523798049644277L, 8538085887473418112L, 4851619038439715422L, 3141119895236824166L, - 7762590461503544675L, 6870466239749873827L, 6210072369202835740L, 5496372991799899062L, - 4968057895362268592L, 4397098393439919250L, 7948892632579629747L, 8880031836874825961L, - 6359114106063703798L, 3414676654757950445L, 5087291284850963038L, 6421090138548270680L, - 8139666055761540861L, 8429069814306277926L, 6511732844609232689L, 4898581444074067179L, - 5209386275687386151L, 5763539562630208905L, 8335018041099817842L, 5532314485466423924L, - 6668014432879854274L, 736502773631228816L, 5334411546303883419L, 2433876626275938215L, - 8535058474086213470L, 7583551416783411467L, 6828046779268970776L, 6066841133426729173L, - 5462437423415176621L, 3008798499370428177L, 8739899877464282594L, 1124728784250774760L, - 6991919901971426075L, 2744457434771574970L, 5593535921577140860L, 2195565947817259976L, - 8949657474523425376L, 3512905516507615961L, 7159725979618740301L, 965650005835137607L, - 5727780783694992240L, 8151217634151930732L, 9164449253911987585L, 3818576177788313364L, - 7331559403129590068L, 3054860942230650691L, 5865247522503672054L, 6133237568526430876L, - 4692198018002937643L, 6751264462192099863L, 7507516828804700229L, 8957348732136404618L, - 6006013463043760183L, 9010553393080078856L, 4804810770435008147L, 1674419492351197600L, - 7687697232696013035L, 4523745595132871322L, 6150157786156810428L, 3618996476106297057L, - 4920126228925448342L, 6584545995626947969L, 7872201966280717348L, 3156575963519296104L, - 6297761573024573878L, 6214609585557347207L, 5038209258419659102L, 8661036483187788089L, - 8061134813471454564L, 6478960743616640295L, 6448907850777163651L, 7027843002264267398L, - 5159126280621730921L, 3777599994440458757L, 8254602048994769474L, 2354811176362823687L, - 6603681639195815579L, 3728523348461214111L, 5282945311356652463L, 4827493086139926451L, - 8452712498170643941L, 5879314530452927160L, 6762169998536515153L, 2858777216991386566L, - 5409735998829212122L, 5976370588335019576L, 8655577598126739396L, 2183495311852210675L, - 6924462078501391516L, 9125493878965589187L, 5539569662801113213L, 5455720695801516188L, - 8863311460481781141L, 6884478705911470739L, 7090649168385424913L, 3662908557358221429L, - 5672519334708339930L, 6619675660628487467L, 9076030935533343889L, 1368109020150804139L, - 7260824748426675111L, 2939161623491598473L, 5808659798741340089L, 506654891422323617L, - 4646927838993072071L, 2249998320508814055L, 7435084542388915313L, 9134020534926967972L, - 5948067633911132251L, 1773193205828708893L, 4758454107128905800L, 8797252194146787761L, - 7613526571406249281L, 4852231473780084609L, 6090821257124999425L, 2037110771653112526L, - 4872657005699999540L, 1629688617322490021L, 7796251209119999264L, 2607501787715984033L, - 6237000967295999411L, 3930675837543742388L, 4989600773836799529L, 1299866262664038749L, - 7983361238138879246L, 5769134835004372321L, 6386688990511103397L, 2770633460632542696L, - 5109351192408882717L, 7750529990618899641L, 8174961907854212348L, 5022150355506418780L, - 6539969526283369878L, 7707069099147045347L, 5231975621026695903L, 631632057204770793L, - 8371160993642713444L, 8389308921011453915L, 6696928794914170755L, 8556121544180118293L, - 5357543035931336604L, 6844897235344094635L, 8572068857490138567L, 5417812354437685931L, - 6857655085992110854L, 644901068808238421L, 5486124068793688683L, 2360595262417545899L, - 8777798510069901893L, 1932278012497118276L, 7022238808055921514L, 5235171224739604944L, - 5617791046444737211L, 6032811387162639117L, 8988465674311579538L, 5963149404718312264L, - 7190772539449263630L, 8459868338516560134L, 5752618031559410904L, 6767894670813248108L, - 9204188850495057447L, 5294608251188331487L - ) - @volatile private[this] var tenPow18Squares: Array[BigInteger] = Array(BigInteger.valueOf(1000000000000000000L)) - - final private def getTenPow18Squares(n: Int): Array[BigInteger] = { - var ss = tenPow18Squares - var i = ss.length - if (n >= i) { - var s = ss(i - 1) - ss = java.util.Arrays.copyOf(ss, n + 1) - while (i <= n) { - s = s.multiply(s) - ss(i) = s - i += 1 - } - tenPow18Squares = ss - } - ss - } - - /** - * Checks if a character does not require JSON escaping or encoding. - * - * @param ch the character to check - * @return `true` if the character is a basic ASCII character (code point less than `0x80`) that does not need JSON escaping - */ - final def isNonEscapedAscii(ch: Char): Boolean = ch < 0x80 && escapedChars(ch) == 0 -} \ No newline at end of file diff --git a/rnd/src/main/scala/co.blocke.scalajack/JsonWriterException.scala b/rnd/src/main/scala/co.blocke.scalajack/JsonWriterException.scala deleted file mode 100644 index 2e12b70a..00000000 --- a/rnd/src/main/scala/co.blocke.scalajack/JsonWriterException.scala +++ /dev/null @@ -1,4 +0,0 @@ -package co.blocke.scalajack - -class JsonWriterException (msg: String, cause: Throwable, withStackTrace: Boolean) - extends RuntimeException(msg, cause, true, withStackTrace) diff --git a/rnd/src/main/scala/co.blocke.scalajack/Run.scala b/rnd/src/main/scala/co.blocke.scalajack/Run.scala deleted file mode 100644 index 39e0e965..00000000 --- a/rnd/src/main/scala/co.blocke.scalajack/Run.scala +++ /dev/null @@ -1,27 +0,0 @@ -package co.blocke.scalajack - -object RunMe extends App: - - val cfg = WriterConfig - .withEscapeUnicode(false) - .withIndentionStep(0) - .withPreferredBufSize(32768) // 2^15 - .withThrowWriterExceptionWithStackTrace(true) - - val writer = JsonWriter(new Array[Byte](256), 0, 0, 0, false, false, null, null, cfg) - - writer.writeArrayStart() - (0 to 300).map(writer.writeVal(_)) - writer.writeArrayEnd() - - println("Result: "+writer.result) - writer.reset - writer.writeArrayStart() - writer.writeVal("Greogry") - writer.writeVal("William") - writer.writeVal("Zoller") - writer.writeArrayEnd() - println("Name: "+writer.result) - - println("Done.") - diff --git a/rnd/src/main/scala/co.blocke.scalajack/WriterConfig.scala b/rnd/src/main/scala/co.blocke.scalajack/WriterConfig.scala deleted file mode 100644 index 7ba0fa94..00000000 --- a/rnd/src/main/scala/co.blocke.scalajack/WriterConfig.scala +++ /dev/null @@ -1,53 +0,0 @@ -package co.blocke.scalajack - -/** - * Configuration for [[com.github.plokhotnyuk.jsoniter_scala.core.JsonWriter]] that contains params for formatting of - * output JSON and for tuning of preferred size for internal byte buffer that is created on the writer instantiation - * and reused in runtime for serialization of messages using [[java.io.OutputStream]] or [[java.nio.DirectByteBuffer]]. - *
- * All configuration params already initialized to default values, but in some cases they should be altered: - *
    - *
  • turn on pretty printing by specifying of indention step that is greater than 0
  • - *
  • turn on escaping of Unicode characters to serialize with only ASCII characters
  • - *
  • increase preferred size of an internal byte buffer to reduce allocation rate of grown and then reduced buffers - * when writing to [[java.io.OutputStream]] or [[java.nio.DirectByteBuffer]] lot of large (>16Kb) - * [[scala.math.BigDecimal]] or [[scala.math.BigInt]] or other non escaped ASCII strings written using - * `JsonWriter.writeNonEscapedAsciiKey` or `JsonWriter.writeNonEscapedAsciiVal`
  • - *
- * @param throwWriterExceptionWithStackTrace a flag that allows to turn on a stack traces for debugging purposes in - * development - * @param indentionStep a size of indention for pretty-printed formatting or 0 for compact output - * @param escapeUnicode a flag to turn on hexadecimal escaping of all non-ASCII chars - * @param preferredBufSize a preferred size (in bytes) of an internal byte buffer when writing to - * [[java.io.OutputStream]] or [[java.nio.DirectByteBuffer]] - */ -class WriterConfig private (val indentionStep: Int, val preferredBufSize: Int, val escapeUnicode: Boolean, - val throwWriterExceptionWithStackTrace: Boolean) extends Serializable { - def withThrowWriterExceptionWithStackTrace(throwWriterExceptionWithStackTrace: Boolean): WriterConfig = - copy(throwWriterExceptionWithStackTrace = throwWriterExceptionWithStackTrace) - - def withIndentionStep(indentionStep: Int): WriterConfig = { - if (indentionStep < 0) throw new IllegalArgumentException("'indentionStep' should be not less than 0") - copy(indentionStep = indentionStep) - } - - def withEscapeUnicode(escapeUnicode: Boolean): WriterConfig = - copy(escapeUnicode = escapeUnicode) - - def withPreferredBufSize(preferredBufSize: Int): WriterConfig = { - if (preferredBufSize <= 0) throw new IllegalArgumentException("'preferredBufSize' should be not less than 1") - copy(preferredBufSize = preferredBufSize) - } - - private[this] def copy(indentionStep: Int = indentionStep, preferredBufSize: Int = preferredBufSize, - throwWriterExceptionWithStackTrace: Boolean = throwWriterExceptionWithStackTrace, - escapeUnicode: Boolean = escapeUnicode): WriterConfig = - new WriterConfig( - indentionStep = indentionStep, - preferredBufSize = preferredBufSize, - escapeUnicode = escapeUnicode, - throwWriterExceptionWithStackTrace = throwWriterExceptionWithStackTrace) -} - -object WriterConfig extends WriterConfig(indentionStep = 0, preferredBufSize = 32768, escapeUnicode = false, - throwWriterExceptionWithStackTrace = false) \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala b/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala index 9bbc5100..b2d97909 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala @@ -970,7 +970,7 @@ object JsonCodecMaker: .asTerm } ) - case TypeHintPolicy.SIMPLE_CLASSNAME => // TODO: Annotation hints + case TypeHintPolicy.SIMPLE_CLASSNAME => CaseDef( Literal(StringConstant(childRef.name)), None, { @@ -1568,12 +1568,9 @@ object JsonCodecMaker: $in.backspace() throw JsonParseError("Failed to read either side of Union type", $in) }.asExprOf[T] - /* - TODO - Intersection: -val syntheticTA = taCache.typeAdapterOf[L] -syntheticTA.write(t.asInstanceOf[L], writer, out) - */ + + case t: LeftRightRef[?] if t.lrkind == LRKind.INTERSECTION => + throw JsonTypeError("Intersection types currently unsupported by ScalaJack") // -------------------- // Enumerations... @@ -1742,6 +1739,16 @@ syntheticTA.write(t.asInstanceOf[L], writer, out) else null }.asExprOf[T] + case t: SetRef[?] => + t.elementRef.refType match + case '[e] => + val rtypeRef = t.elementRef.asInstanceOf[RTypeRef[e]] + '{ + val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in, inTuple) }) + if parsedArray != null then parsedArray.to(${ Expr.summon[Factory[e, T]].get }) // create appropriate flavor of Set[T] here + else null + }.asExprOf[T] + // -------------------- // Java Collections... // -------------------- @@ -2196,14 +2203,6 @@ syntheticTA.write(t.asInstanceOf[L], writer, out) // ================================================================ val codecDef = '{ // FIXME: generate a type class instance using `ClassDef.apply` and `Symbol.newClass` calls after graduating from experimental API: https://www.scala-lang.org/blog/2022/06/21/scala-3.1.3-released.html new JsonCodec[T] { - // def nullValue: A = ${genNullValue[A](rootTpe :: Nil)} // <- needed? - - // TBD... when we're ready to tackle reading! - // def decodeValue(in: JsonReader, default: A): A = ${ - // if (cfg.encodingOnly) '{ ??? } - // else genReadVal(rootTpe :: Nil, 'default, cfg.isStringified, false, 'in) - // } - def encodeValue(in: T, out: JsonOutput): Unit = ${ genWriteVal('in, ref, 'out) } def decodeValue(in: JsonSource): T = ${ genReadVal(ref, 'in) } } diff --git a/src/main/scala/co.blocke.scalajack/json/readers/ClassReader.scalax b/src/main/scala/co.blocke.scalajack/json/readers/ClassReader.scalax deleted file mode 100644 index 9ccbbe3c..00000000 --- a/src/main/scala/co.blocke.scalajack/json/readers/ClassReader.scalax +++ /dev/null @@ -1,58 +0,0 @@ -package co.blocke.scalajack -package json -package readers - -import co.blocke.scala_reflection.reflect.rtypeRefs.* -import co.blocke.scala_reflection.rtypes.* -import co.blocke.scala_reflection.{Clazzes, RTypeRef, TypedName} -import co.blocke.scala_reflection.Liftables.TypedNameToExpr -import scala.quoted.* -import scala.collection.mutable.HashMap -import scala.util.{Failure, Success, Try} - -case class ClassReader(next: ReaderModule, root: ReaderModule) extends ReaderModule: - - def readerFn[T](ref: RTypeRef[T], isMapKey: Boolean = false)(using q: Quotes, tt: Type[T])(using cache: HashMap[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]]): Expr[(JsonConfig, JsonParser) => Either[ParseError, T]] = - import quotes.reflect.* - import Clazzes.* - - ref match - case t: ScalaClassRef[T] => - if isMapKey then throw new JsonError("Class types cannot be map keys.") - t.refType match - case '[s] => - val parseTable = JsonReaderUtil.classParseMap[T](t, root) - val instantiator = JsonReaderUtil.classInstantiator[T](t) - val classFn = '{ (j: JsonConfig, p: JsonParser) => - val rtype = ${ t.expr }.asInstanceOf[ScalaClassRType[T]] - val presetFieldValues = scala.collection.mutable.HashMap.empty[String, Any] - rtype.fields.foreach(f => - if f.fieldType.clazz == OptionClazz then presetFieldValues.put(f.name, None) - else if f.asInstanceOf[ScalaFieldInfo].defaultValueAccessorName.isDefined then - val (companion, accessor) = f.asInstanceOf[ScalaFieldInfo].defaultValueAccessorName.get - - // Have to use Java reflection here to get default value--Scala compiler won't have access to companion - // or accessor if we do a ${} block, and using compiler staging would murder performance. - val defaultValue = { - val c = Class.forName(companion) - val cons = c.getDeclaredConstructor() - cons.setAccessible(true) - val m = c.getMethod(accessor) - m.setAccessible(true) - m.invoke(cons.newInstance()) - } - presetFieldValues.put(f.name, defaultValue) - ) - val classFieldMap = $parseTable(p) // Map[String, JsonConfig => Either[ParseError, ?]] - p.expectClass(j, classFieldMap, presetFieldValues) - .flatMap(fieldValues => - scala.util.Try($instantiator(fieldValues.toMap)) match // instantiate the class here!!! - case Success(v) => Right(v) - case Failure(e) => Left(JsonParseError(p.showError(s"Unable to instantiate class at position [${p.getPos - 1}] with message ${e.getMessage}"))) - ) - } - cache.put(Expr(t.typedName), classFn) - classFn - - case t => - next.readerFn[T](t) diff --git a/src/main/scala/co.blocke.scalajack/json/readers/CollectionReader.scalax b/src/main/scala/co.blocke.scalajack/json/readers/CollectionReader.scalax deleted file mode 100644 index ef5322b3..00000000 --- a/src/main/scala/co.blocke.scalajack/json/readers/CollectionReader.scalax +++ /dev/null @@ -1,130 +0,0 @@ -package co.blocke.scalajack -package json -package readers - -import co.blocke.scala_reflection.reflect.rtypeRefs.* -import co.blocke.scala_reflection.reflect.* -import co.blocke.scala_reflection.{Clazzes, RTypeRef, TypedName} -import co.blocke.scala_reflection.rtypes.* -import co.blocke.scala_reflection.Liftables.TypedNameToExpr -import scala.quoted.* -import scala.collection.Factory -import co.blocke.scala_reflection.RType -import scala.jdk.CollectionConverters.* -import scala.collection.mutable.HashMap -import scala.util.{Failure, Success, Try} - -case class CollectionReader(next: Option[ReaderModule], root: ReaderModule) extends ReaderModule: - - def refRead[T]( - ref: RTypeRef[T], - parserE: Expr[JsonParser], - cfgE: Expr[JsonConfig], - isMapKey: Boolean = false - )(using q: Quotes, tt: Type[T])(using cache: HashMap[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]]): Expr[Either[ParseError, T]] = - import quotes.reflect.* - - ref match - case t: SeqRef[T] => - if isMapKey then '{ Left(new JsonError("Seq types cannot be map keys.")) } - t.elementRef.refType match - case '[e] => - val fn = () => root.refRead[e](t.elementRef.asInstanceOf[RTypeRef[e]], parserE, cfgE) - '{ - $parserE - .expectList[e]($fn) - .map(_.to(${ Expr.summon[Factory[e, T]].get })) // Convert List to whatever the target type should be - } - - /* - case t: MapRef[T] => - if isMapKey then throw new JsonError("Map types cannot be map keys.") - t.refType match - case '[m] => - t.elementRef.refType match - case '[k] => - t.elementRef2.refType match - case '[v] => - val keyFn = root.readerFn[k](t.elementRef.asInstanceOf[RTypeRef[k]], true).asInstanceOf[Expr[(JsonConfig, JsonParser) => Either[ParseError, k]]] - val valFn = root.readerFn[v](t.elementRef2.asInstanceOf[RTypeRef[v]]).asInstanceOf[Expr[(JsonConfig, JsonParser) => Either[ParseError, v]]] - '{ (j: JsonConfig, p: JsonParser) => - p.expectObject[k, v](j, $keyFn, $valFn).map(_.to(${ Expr.summon[Factory[(k, v), T]].get })) // Convert List to whatever the target type should be - } - - case t: ArrayRef[T] => - if isMapKey then throw new JsonError("Arrays cannot be map keys.") - t.refType match - case '[s] => - t.elementRef.refType match - case '[e] => - val subFn = root.readerFn[e](t.elementRef.asInstanceOf[RTypeRef[e]]).asInstanceOf[Expr[(JsonConfig, JsonParser) => Either[ParseError, e]]] - '{ (j: JsonConfig, p: JsonParser) => - p.expectList[e](j, $subFn) - .map(_ match - case v if v == null => null.asInstanceOf[T] - case v => v.to(${ Expr.summon[Factory[e, T]].get }) - ) // Convert List to whatever the target type should be - } - - case t: TupleRef[T] => - if isMapKey then throw new JsonError("Tuple types cannot be map keys.") - t.refType match - case '[s] => - val tupleFns = Expr.ofList( - t.tupleRefs.map(tr => - tr.refType match - case '[e] => - root.readerFn[e](tr.asInstanceOf[RTypeRef[e]]).asInstanceOf[Expr[(JsonConfig, JsonParser) => Either[ParseError, e]]] - ) - ) - val instantiator = JsonReaderUtil.tupleInstantiator[T](t) - '{ (j: JsonConfig, p: JsonParser) => - p.expectTuple(j, $tupleFns) - .flatMap(results => - scala.util.Try($instantiator(results)) match // instantiate the tuple here!!! - case Success(v) => Right(v) - case Failure(e) => Left(JsonParseError(p.showError(s"Unable to instantiate tuple at position [${p.getPos - 1}] with message ${e.getMessage}"))) - ) - } - - // Java - case t: JavaCollectionRef[T] => - if isMapKey then throw new JsonError("Java collections cannot be map keys.") - t.refType match - case '[s] => - t.elementRef.refType match - case '[e] => - val subFn = root.readerFn[e](t.elementRef.asInstanceOf[RTypeRef[e]]).asInstanceOf[Expr[(JsonConfig, JsonParser) => Either[ParseError, e]]] - val className = Expr(t.name) - '{ (j: JsonConfig, p: JsonParser) => - val cname = $className - p.expectList[e](j, $subFn) - .flatMap(result => - scala.util.Try(Class.forName(cname).getDeclaredConstructor(Class.forName("java.util.Collection")).newInstance(result.asJava)) match - case Success(ok) => Right(ok.asInstanceOf[T]) - case Failure(e) => Left(JsonParseError(p.showError(s"Could not instantiate a $cname, with error: " + e))) - ) - } - */ - - case t => - next.get.refRead(ref, parserE, cfgE, isMapKey) - - - - /* - - 1. At compile-time, use reflected RTypes to build a set of pre-baked instructions for the parser... an entire set of commands to parse the given object - 2. Pass this command set to the parser at runtime for linear execution - - - Parson(name: String, age: Int, stuff: List[String]) - - ParseObject - fields: - name: ParseString - age: ParseInt - stuff: ParseList - ParseString - - */ \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json/readers/EnumReader.scalax b/src/main/scala/co.blocke.scalajack/json/readers/EnumReader.scalax deleted file mode 100644 index 7ee73f25..00000000 --- a/src/main/scala/co.blocke.scalajack/json/readers/EnumReader.scalax +++ /dev/null @@ -1,99 +0,0 @@ -package co.blocke.scalajack -package json -package readers - -import co.blocke.scala_reflection.reflect.rtypeRefs.* -import co.blocke.scala_reflection.rtypes.* -import co.blocke.scala_reflection.{Clazzes, RTypeRef, TypedName} -import scala.quoted.* -import scala.collection.mutable.HashMap -import scala.util.{Failure, Success, Try} - -case class EnumReader(next: ReaderModule, root: ReaderModule) extends ReaderModule: - - def readerFn[T](ref: RTypeRef[T], isMapKey: Boolean = false)(using q: Quotes, tt: Type[T])(using cache: HashMap[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]]): Expr[(JsonConfig, JsonParser) => Either[ParseError, T]] = - import quotes.reflect.* - import Clazzes.* - - ref match - case t: ScalaEnumRef[T] => - t.refType match - case '[s] => - val rtypeExpr = t.expr - val wrappedMapKey = Expr(isMapKey) - '{ (j: JsonConfig, p: JsonParser) => - val rtype = $rtypeExpr.asInstanceOf[ScalaEnumRType[T]] - def readNumericEnum = - if $wrappedMapKey then - (for { - _ <- p.expectQuote - enumVal <- p.expectLong(j, p) - _ <- p.expectQuote - } yield enumVal).flatMap { v => - val fromOrdinalMethod = Class.forName(rtype.name).getMethod("fromOrdinal", classOf[Int]) - scala.util.Try(fromOrdinalMethod.invoke(null, v.toInt).asInstanceOf[T]) match - case Success(v2) => Right(v2) - case Failure(e) => Left(JsonParseError(p.showError(s"No enum value in ${rtype.name} for ordinal value '$v'"))) - } - else - p.expectLong(j, p).flatMap { v => - val fromOrdinalMethod = Class.forName(rtype.name).getMethod("fromOrdinal", classOf[Int]) - scala.util.Try(fromOrdinalMethod.invoke(null, v.toInt).asInstanceOf[T]) match - case Success(v2) => Right(v2) - case Failure(e) => Left(JsonParseError(p.showError(s"No enum value in ${rtype.name} for ordinal value '$v'"))) - } - j.enumsAsIds match - case '*' => readNumericEnum - case enumList: List[String] if enumList.contains(rtype.name) => readNumericEnum - case _ => - p.expectString(j, p).flatMap { v => - val valueOfMethod = Class.forName(rtype.name).getMethod("valueOf", classOf[String]) - scala.util.Try(valueOfMethod.invoke(null, v).asInstanceOf[T]) match - case Success(v2) => Right(v2) - case Failure(e) => Left(JsonParseError(p.showError(s"No enum value in ${rtype.name} for value '$v'"))) - } - } - - case t: ScalaEnumerationRef[T] => - t.refType match - case '[s] => - val rtypeExpr = t.expr - val wrappedMapKey = Expr(isMapKey) - '{ (j: JsonConfig, p: JsonParser) => - val rtype = $rtypeExpr.asInstanceOf[ScalaEnumerationRType[T]] - def readNumericEnum = - val eClazz = Class.forName(rtype.name) - val fromOrdinalMethod = eClazz.getMethod("apply", classOf[Int]) - if $wrappedMapKey then - (for { - _ <- p.expectQuote - enumVal <- p.expectLong(j, p) - _ <- p.expectQuote - } yield enumVal).flatMap { v => - scala.util.Try(fromOrdinalMethod.invoke(null, v.toInt).asInstanceOf[T]) match - case Success(v2) => Right(v2) - case Failure(e) => Left(JsonParseError(p.showError(s"No enumeration value in ${rtype.name} for ordinal value '$v'"))) - } - else - p.expectLong(j, p).flatMap { v => - scala.util.Try(fromOrdinalMethod.invoke(null, v.toInt).asInstanceOf[T]) match - case Success(v2) => Right(v2) - case Failure(e) => Left(JsonParseError(p.showError(s"No enumeration value in ${rtype.name} for ordinal value '$v'"))) - } - j.enumsAsIds match - case '*' => readNumericEnum - case enumList: List[String] if enumList.contains(rtype.name) => readNumericEnum - case _ => - p.expectString(j, p).flatMap { v => - val eClazz = Class.forName(rtype.name) - val valueOfMethod = eClazz.getMethod("withName", classOf[String]) - scala.util.Try(valueOfMethod.invoke(null, v).asInstanceOf[T]) match - case Success(v2) => Right(v2) - case Failure(e) => Left(JsonParseError(p.showError(s"No enumeration value in ${rtype.name} for value '$v'"))) - } - } - - // TODO: JavaEnumRef - - case t => - next.readerFn[T](t) diff --git a/src/main/scala/co.blocke.scalajack/json/readers/MiscReader.scalax b/src/main/scala/co.blocke.scalajack/json/readers/MiscReader.scalax deleted file mode 100644 index 7e9b51bc..00000000 --- a/src/main/scala/co.blocke.scalajack/json/readers/MiscReader.scalax +++ /dev/null @@ -1,76 +0,0 @@ -package co.blocke.scalajack -package json -package readers - -import co.blocke.scala_reflection.reflect.rtypeRefs.* -import co.blocke.scala_reflection.rtypes.* -import co.blocke.scala_reflection.{Clazzes, RTypeRef, TypedName} -import scala.quoted.* -import scala.collection.mutable.HashMap -import scala.util.{Failure, Success, Try} - -case class MiscReader(next: ReaderModule, root: ReaderModule) extends ReaderModule: - - def readerFn[T](ref: RTypeRef[T], isMapKey: Boolean = false)(using q: Quotes, tt: Type[T])(using cache: HashMap[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]]): Expr[(JsonConfig, JsonParser) => Either[ParseError, T]] = - import quotes.reflect.* - import Clazzes.* - - ref match - case t: ScalaOptionRef[T] => - if isMapKey then throw new JsonError("Options cannot be map keys.") - t.refType match - case '[s] => - t.optionParamType.refType match - case '[e] => - val subFn = readerFn[e](t.optionParamType.asInstanceOf[RTypeRef[e]]).asInstanceOf[Expr[(JsonConfig, JsonParser) => Either[ParseError, e]]] - val isNullable = Expr(t.optionParamType.isNullable) - '{ (j: JsonConfig, p: JsonParser) => - p.nullCheck match { - case true if j.forbidNullsInInput => Left(JsonParseError(p.showError(s"Forbidden 'null' value received at position [${p.getPos}]"))) - case true if j.noneAsNull => Right(None.asInstanceOf[T]) - case true if ! $isNullable => Left(JsonParseError(p.showError(s"Null value given for non-nullable value type at position [${p.getPos}]"))) - case true => Right(Some(null).asInstanceOf[T]) - case false => $subFn(j, p).map(v => Some(v).asInstanceOf[T]) - } - } - - case t: AliasRef[T] => - t.refType match - case '[s] => - t.unwrappedType.refType match - case '[e] => - val subFn = readerFn[e](t.unwrappedType.asInstanceOf[RTypeRef[e]], isMapKey).asInstanceOf[Expr[(JsonConfig, JsonParser) => Either[ParseError, e]]] - '{ (j: JsonConfig, p: JsonParser) => $subFn(j, p).asInstanceOf[Either[co.blocke.scalajack.json.ParseError, T]] } - - case t: SelfRefRef[T] => - if isMapKey then throw new JsonError("Class or trait types cannot be map keys.") - t.refType match - case '[s] => - val className = Expr(t.typedName.toString) - '{ (j: JsonConfig, p: JsonParser) => - val cname = $className - p.cache.get(cname.asInstanceOf[TypedName]) match - case Some(fn) => fn(j, p).asInstanceOf[Either[co.blocke.scalajack.json.ParseError, T]] - case None => Left(JsonParseError(p.showError(s"Expected self-ref class $cname but none found in cache at position [${p.getPos}]"))) - } - - case t: TryRef[T] => - if isMapKey then throw new JsonError("Try values cannot be map keys.") - t.refType match - case '[s] => - t.tryRef.refType match - case '[e] => - val subFn = readerFn[e](t.tryRef.asInstanceOf[RTypeRef[e]]).asInstanceOf[Expr[(JsonConfig, JsonParser) => Either[ParseError, e]]] - val isNullable = Expr(t.tryRef.isNullable) - '{ (j: JsonConfig, p: JsonParser) => - p.nullCheck match { - case true if j.forbidNullsInInput => Left(JsonParseError(p.showError(s"Forbidden 'null' value received at position [${p.getPos}]"))) - case true if j.tryFailureHandling == TryOption.AS_NULL => Right(Failure(JsonParseError(p.showError("null value received indicating Try failure"))).asInstanceOf[T]) - case true if ! $isNullable => Left(JsonParseError(p.showError(s"Null value given for non-nullable value type at position [${p.getPos}]"))) - case true => Right(Success(null).asInstanceOf[T]) - case false => $subFn(j, p).map(v => Success(v).asInstanceOf[T]) - } - } - - case t => - next.readerFn[T](t) diff --git a/src/main/scala/co.blocke.scalajack/json/readers/PrimitiveReader.scalax b/src/main/scala/co.blocke.scalajack/json/readers/PrimitiveReader.scalax deleted file mode 100644 index 40f733ad..00000000 --- a/src/main/scala/co.blocke.scalajack/json/readers/PrimitiveReader.scalax +++ /dev/null @@ -1,220 +0,0 @@ -package co.blocke.scalajack -package json -package readers - -import co.blocke.scala_reflection.reflect.rtypeRefs.* -import co.blocke.scala_reflection.reflect.* -import co.blocke.scala_reflection.{Clazzes, RTypeRef, TypedName} -import co.blocke.scala_reflection.rtypes.* -import co.blocke.scala_reflection.Liftables.TypedNameToExpr -import scala.quoted.* -import scala.collection.Factory -import co.blocke.scala_reflection.RType -import scala.jdk.CollectionConverters.* -import scala.collection.mutable.HashMap -import scala.util.{Failure, Success, Try} - -case class IntReader() extends JsonReader[Long, Int]: - def apply(in: Long) = in.toInt - -case class StringReader() extends JsonReader[String, String]: - def apply(in: String) = in - -// Magic line to convert a List[T] to some U <: Seq[?], eg zconvert[Int,Set[?]] -// can use this if I have element type T, and target collection U, which the SeqRef has! -// This line should be executed at runtime. -// -// inline def zconvert[T,U](in:List[T])(using Factory[T, U]) = in.to( summon[Factory[T,U]] ) -// -// case class SeqReader[B, T](toObj: B) extends JsonReader[List[T], Seq[T]]: -// def apply(in: List[T]) = in.to(toObj) -// def apply[s](in: List[T]) = in.to(${ Expr.summon[Factory[T, s]].get }) - -/* -case class PrimitiveReader(next: Option[ReaderModule], root: ReaderModule) extends ReaderModule: - - def refRead[T]( - ref: RTypeRef[T], - parserE: Expr[JsonParser], - cfgE: Expr[JsonConfig], - isMapKey: Boolean = false - )(using q: Quotes, tt: Type[T])(using cache: HashMap[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]]): Expr[Either[ParseError, T]] = - import quotes.reflect.* - import Clazzes.* - - ref match - // - // Scala Primitives - // - case t: PrimitiveRef[T] if t.name == BIG_DECIMAL_CLASS => - val isMapKeyE = Expr(isMapKey) - '{ $parserE.expectBigDouble($isMapKeyE).map(_.asInstanceOf[T]) } - - case t: PrimitiveRef[T] if t.name == BIG_INT_CLASS => - val isMapKeyE = Expr(isMapKey) - '{ $parserE.expectBigLong($isMapKeyE).map(_.asInstanceOf[T]) } - - case t: PrimitiveRef[T] if t.name == BOOLEAN_CLASS => - val isMapKeyE = Expr(isMapKey) - '{ $parserE.expectBoolean($isMapKeyE).map(_.asInstanceOf[T]) } - - case t: PrimitiveRef[T] if t.name == BYTE_CLASS => - val isMapKeyE = Expr(isMapKey) - '{ $parserE.expectLong($isMapKeyE).map(_.toByte.asInstanceOf[T]) } - - case t: PrimitiveRef[T] if t.name == CHAR_CLASS => - '{ - $parserE - .expectString() - .flatMap(s => - if s == null then Left(JsonParseError($parserE.showError(s"Char typed values cannot be null"))) - else - s.toArray.headOption match - case Some(c) => Right(c.asInstanceOf[T]) - case None => Left(JsonParseError($parserE.showError(s"Cannot convert value '$s' into a Char"))) - ) - } - - case t: PrimitiveRef[T] if t.name == DOUBLE_CLASS => - val isMapKeyE = Expr(isMapKey) - '{ $parserE.expectDouble($isMapKeyE).map(_.asInstanceOf[T]) } - - case t: PrimitiveRef[T] if t.name == FLOAT_CLASS => - val isMapKeyE = Expr(isMapKey) - '{ $parserE.expectDouble($isMapKeyE).map(_.toFloat.asInstanceOf[T]) } - - case t: PrimitiveRef[T] if t.name == INT_CLASS => - val isMapKeyE = Expr(isMapKey) - '{ $parserE.expectLong($isMapKeyE).map(_.toInt.asInstanceOf[T]) } - - case t: PrimitiveRef[T] if t.name == LONG_CLASS => - val isMapKeyE = Expr(isMapKey) - '{ $parserE.expectLong($isMapKeyE).map(_.asInstanceOf[T]) } - - case t: PrimitiveRef[T] if t.name == SHORT_CLASS => - val isMapKeyE = Expr(isMapKey) - '{ $parserE.expectLong($isMapKeyE).map(_.toShort.asInstanceOf[T]) } - - case t: PrimitiveRef[T] if t.name == STRING_CLASS => - '{ $parserE.expectString().map(_.asInstanceOf[T]) } - - case t => - next.get.refRead(ref, parserE, cfgE, isMapKey) - */ - -/* - def readerFn[T](ref: RTypeRef[T], isMapKey: Boolean = false)(using q: Quotes, tt: Type[T])(using cache: HashMap[Expr[TypedName], Expr[(JsonConfig, JsonParser) => Either[ParseError, ?]]]): Expr[(JsonConfig, JsonParser) => Either[ParseError, T]] = - import Clazzes.* - import quotes.reflect.* - - ref match - - // - // Scala Primitives - // - - // - // Java Primitives - // - case t: PrimitiveRef[T] if t.name == JBOOLEAN_CLASS => - if isMapKey then - '{ (j: JsonConfig, p: JsonParser) => - (for { - _ <- p.expectQuote - v <- p.expectBoolean(j, p).map(b => java.lang.Boolean(b).asInstanceOf[T]) - _ <- p.expectQuote - } yield v) - } - else '{ (j: JsonConfig, p: JsonParser) => p.expectBoolean(j, p).map(_.asInstanceOf[T]) } - - case t: PrimitiveRef[T] if t.name == JBYTE_CLASS => - if isMapKey then - '{ (j: JsonConfig, p: JsonParser) => - (for { - _ <- p.expectQuote - v <- p.expectLong(j, p).map(b => java.lang.Byte(b.asInstanceOf[Byte]).asInstanceOf[T]) - _ <- p.expectQuote - } yield v) - } - else '{ (j: JsonConfig, p: JsonParser) => p.expectLong(j, p).map(_.toByte.asInstanceOf[T]) } - - case t: PrimitiveRef[T] if t.name == JCHARACTER_CLASS => - '{ (j: JsonConfig, p: JsonParser) => - p.expectString(j, p) - .flatMap(s => - s.toArray.headOption match - case Some(c) => Right(java.lang.Character(c).asInstanceOf[T]) - case None => Left(JsonParseError(p.showError(s"Cannot convert value '$s' into a Char."))) - ) - } - - case t: PrimitiveRef[T] if t.name == JDOUBLE_CLASS => - if isMapKey then - '{ (j: JsonConfig, p: JsonParser) => - (for { - _ <- p.expectQuote - v <- p.expectDouble(j, p).map(b => java.lang.Double(b).asInstanceOf[T]) - _ <- p.expectQuote - } yield v) - } - else '{ (j: JsonConfig, p: JsonParser) => p.expectDouble(j, p).map(_.asInstanceOf[T]) } - - case t: PrimitiveRef[T] if t.name == JFLOAT_CLASS => - if isMapKey then - '{ (j: JsonConfig, p: JsonParser) => - (for { - _ <- p.expectQuote - v <- p.expectDouble(j, p).map(b => java.lang.Float(b).asInstanceOf[T]) - _ <- p.expectQuote - } yield v) - } - else '{ (j: JsonConfig, p: JsonParser) => p.expectDouble(j, p).map(_.toFloat.asInstanceOf[T]) } - - case t: PrimitiveRef[T] if t.name == JINTEGER_CLASS => - if isMapKey then - '{ (j: JsonConfig, p: JsonParser) => - (for { - _ <- p.expectQuote - v <- p.expectLong(j, p).map(b => java.lang.Integer(b.toInt).asInstanceOf[T]) - _ <- p.expectQuote - } yield v) - } - else '{ (j: JsonConfig, p: JsonParser) => p.expectLong(j, p).map(_.toInt.asInstanceOf[T]) } - - case t: PrimitiveRef[T] if t.name == JLONG_CLASS => - if isMapKey then - '{ (j: JsonConfig, p: JsonParser) => - (for { - _ <- p.expectQuote - v <- p.expectLong(j, p).map(b => java.lang.Long(b).asInstanceOf[T]) - _ <- p.expectQuote - } yield v) - } - else '{ (j: JsonConfig, p: JsonParser) => p.expectLong(j, p).map(_.asInstanceOf[T]) } - - case t: PrimitiveRef[T] if t.name == JSHORT_CLASS => - if isMapKey then - '{ (j: JsonConfig, p: JsonParser) => - (for { - _ <- p.expectQuote - v <- p.expectLong(j, p).map(b => java.lang.Short(b.toShort).asInstanceOf[T]) - _ <- p.expectQuote - } yield v) - } - else '{ (j: JsonConfig, p: JsonParser) => p.expectLong(j, p).map(_.toShort.asInstanceOf[T]) } - - case t: PrimitiveRef[T] if t.name == UUID_CLASS => - '{ (j: JsonConfig, p: JsonParser) => - p.expectString(j, p) - .flatMap(u => - if u == null then Right(null.asInstanceOf[T]) - else - scala.util.Try(java.util.UUID.fromString(u)) match - case Success(uuid) => Right(uuid.asInstanceOf[T]) - case Failure(_) => Left(JsonParseError(p.showError(s"Unable to marshal UUID from value '$u' at position [${p.getPos}]"))) - ) - } - - case t => - next.readerFn[T](t) - */ diff --git a/src/main/scala/co.blocke.scalajack/json/writing/WriteCodec.scalax b/src/main/scala/co.blocke.scalajack/json/writing/WriteCodec.scalax deleted file mode 100644 index 3c92bbe7..00000000 --- a/src/main/scala/co.blocke.scalajack/json/writing/WriteCodec.scalax +++ /dev/null @@ -1,766 +0,0 @@ -package co.blocke.scalajack -package json -package writing - -import JsonCodecMaker.MethodKey -import co.blocke.scala_reflection.RTypeRef -import co.blocke.scala_reflection.reflect.rtypeRefs.* -import co.blocke.scala_reflection.reflect.ReflectOnType - -import scala.quoted.* - -case class WriteCodec[T](ref: RTypeRef[T], cfg: JsonConfig)(using q: Quotes)(using tt: Type[T]): - import q.reflect.* - - private val writeMethodSyms = new scala.collection.mutable.HashMap[MethodKey, Symbol] - val writeMethodDefs = new scala.collection.mutable.ArrayBuffer[DefDef] - - // Fantastic magic here--lifted from Jasoniter. Props! This thing will create a DefDef, and a Symbol to it. - // The Symbol will let you call the generated function later from other macro-generated code. The goal is to use - // generated functions to create cleaner/faster macro code than what straight quotes/splices would create unaided. - def makeWriteFn[U: Type](methodKey: MethodKey, arg: Expr[U], out: Expr[JsonOutput])(f: (Expr[U], Expr[JsonOutput]) => Expr[Unit]): Expr[Unit] = - // Get a symbol, if one already created for this key... else make one. - Apply( - Ref( - writeMethodSyms.getOrElse( - methodKey, { - val sym = Symbol.newMethod( - Symbol.spliceOwner, - "w" + writeMethodSyms.size, // 'w' is for Writer! - MethodType(List("in", "out"))(_ => List(TypeRepr.of[U], TypeRepr.of[JsonOutput]), _ => TypeRepr.of[Unit]) - ) - writeMethodSyms.update(methodKey, sym) - writeMethodDefs += DefDef( - sym, - params => { - val List(List(in, out)) = params - Some(f(in.asExprOf[U], out.asExprOf[JsonOutput]).asTerm.changeOwner(sym)) - } - ) - sym - } - ) - ), - List(arg.asTerm, out.asTerm) - ).asExprOf[Unit] - - def maybeWrite[T](label: String, aE: Expr[T], ref: RTypeRef[T], out: Expr[JsonOutput], cfg: JsonConfig): Expr[Unit] = - val labelE = Expr(label) - _maybeWrite[T]( - '{ $out.label($labelE) }, - aE, - ref, - out, - cfg - ) - - def maybeWriteMap[K, V](keyE: Expr[K], valueE: Expr[V], keyRef: RTypeRef[K], valueRef: RTypeRef[V], out: Expr[JsonOutput], cfg: JsonConfig): Expr[Unit] = - keyRef.refType match - case '[k] => - _maybeWrite[V]( - '{ - $out.maybeComma() - ${ genWriteVal(keyE.asExprOf[k], keyRef.asInstanceOf[RTypeRef[k]], out, false, true) } - $out.colon() - }, - valueE, - valueRef, - out, - cfg - ) - - // Tests whether we should write something or not--mainly in the case of Option, or wrapped Option - // Affected types: Option, java.util.Optional, Left/Right, Try/Failure - // Returns Expr[Unit] containing either the original phrase (if ok to write) or the phrase - // prepended with the type-appropriate runtime check. This may seem like drama, but the idea - // is to avoid slowing runtime down with extra "if" checks unless they're absolutely needed. - def _maybeWrite[T](prefix: Expr[Unit], aE: Expr[T], ref: RTypeRef[T], out: Expr[JsonOutput], cfg: JsonConfig): Expr[Unit] = - ref match - case t: ScalaOptionRef[?] if !cfg.noneAsNull => - t.optionParamType.refType match - case '[e] => - val tin = aE.asExprOf[Option[e]] - '{ - $tin match - case None => () - case Some(v) => ${ _maybeWrite[e](prefix, '{ v }.asExprOf[e], t.optionParamType.asInstanceOf[RTypeRef[e]], out, cfg) } - } - case t: JavaOptionalRef[?] if !cfg.noneAsNull => - t.optionParamType.refType match - case '[e] => - val tin = aE.asExprOf[java.util.Optional[e]] - '{ - if ! $tin.isEmpty then ${ _maybeWrite[e](prefix, '{ $tin.get }.asExprOf[e], t.optionParamType.asInstanceOf[RTypeRef[e]], out, cfg) } - } - case t: TryRef[?] => - t.tryRef.refType match - case '[e] => - val tin = aE.asExprOf[scala.util.Try[e]] - '{ - $tin match - case scala.util.Failure(v) => - ${ - cfg.tryFailureHandling match - case TryPolicy.AS_NULL => - '{ - $prefix - $out.burpNull() - } - case TryPolicy.ERR_MSG_STRING => - '{ - $prefix - $out.value("Try Failure with msg: " + v.getMessage()) - } - case TryPolicy.THROW_EXCEPTION => '{ throw v } - } - case _ => ${ _maybeWrite[e](prefix, '{ $tin.get }.asExprOf[e], t.tryRef.asInstanceOf[RTypeRef[e]], out, cfg) } - } - case t: LeftRightRef[?] if t.lrkind == LRKind.EITHER => - t.refType match - case '[u] => - val tin = aE.asExprOf[u] - t.rightRef.refType match - case '[rt] => - cfg.eitherLeftHandling match - case EitherLeftPolicy.AS_NULL => - '{ - if $tin == null then $out.burpNull() - $tin match - case Left(_) => - $prefix - $out.burpNull() - case Right(v) => ${ _maybeWrite[rt](prefix, '{ v.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out, cfg) } - } - case EitherLeftPolicy.ERR_MSG_STRING => - '{ - if $tin == null then $out.burpNull() - $tin match - case Left(err) => - $prefix - $out.value("Left Error: " + err.toString) - case Right(v) => ${ _maybeWrite[rt](prefix, '{ v.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out, cfg) } - } - case EitherLeftPolicy.THROW_EXCEPTION => - '{ - if $tin == null then $out.burpNull() - $tin match - case Left(err) => throw new JsonEitherLeftError("Left Error: " + err.toString) - case Right(v) => ${ _maybeWrite[rt](prefix, '{ v.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out, cfg) } - } - case EitherLeftPolicy.AS_VALUE => - t.leftRef.refType match - case '[lt] => - '{ - if $tin == null then $out.burpNull() - $tin match - case Left(v) => - ${ _maybeWrite[lt](prefix, '{ v.asInstanceOf[lt] }, t.leftRef.asInstanceOf[RTypeRef[lt]], out, cfg) } - case Right(v) => - ${ _maybeWrite[rt](prefix, '{ v.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out, cfg) } - } - case t: LeftRightRef[?] if t.lrkind != LRKind.EITHER => - t.refType match - case '[e] => - t.rightRef.refType match - case '[rt] => - t.leftRef.refType match - case '[lt] => - val tin = aE.asExprOf[e] - '{ - if $tin == null then $out.burpNull() - else - $out.mark() - scala.util.Try { - ${ _maybeWrite[rt](prefix, '{ $tin.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out, cfg) } - } match - case scala.util.Success(_) => () - case scala.util.Failure(f) => - $out.revert() - ${ _maybeWrite[lt](prefix, '{ $tin.asInstanceOf[lt] }, t.leftRef.asInstanceOf[RTypeRef[lt]], out, cfg) } - } - case t: AnyRef => - AnyWriter.isOkToWrite(prefix, aE, out, cfg) - case _ => - ref.refType match - case '[u] => - '{ - $prefix - ${ genWriteVal[u](aE.asExprOf[u], ref.asInstanceOf[RTypeRef[u]], out) } - } - - - def genEncFnBody[T](r: RTypeRef[?], aE: Expr[T], out: Expr[JsonOutput], emitDiscriminator: Boolean = false, inTuple: Boolean = false, isMapKey: Boolean = false)(using Quotes): Expr[Unit] = - r.refType match - case '[b] => - r match - case t: ArrayRef[?] => - makeWriteFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => - t.elementRef.refType match - case '[e] => - val tin = in.asInstanceOf[Expr[Array[e]]] - '{ - if $tin == null then $out.burpNull() - else - $out.startArray() - $tin.foreach { i => - ${ genWriteVal('{ i }, t.elementRef.asInstanceOf[RTypeRef[e]], out) } - } - $out.endArray() - } - } - - case t: SeqRef[?] => - makeWriteFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => - t.elementRef.refType match - case '[e] => - val tin = if t.isMutable then in.asExprOf[scala.collection.mutable.Seq[e]] else in.asExprOf[Seq[e]] - '{ - if $tin == null then $out.burpNull() - else - $out.startArray() - $tin.foreach { i => - ${ genWriteVal('{ i }, t.elementRef.asInstanceOf[RTypeRef[e]], out) } - } - $out.endArray() - } - } - - case t: SetRef[?] => - makeWriteFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => - t.elementRef.refType match - case '[e] => - val tin = if t.isMutable then in.asExprOf[scala.collection.mutable.Set[e]] else in.asExprOf[Set[e]] - '{ - if $tin == null then $out.burpNull() - else - $out.startArray() - $tin.foreach { i => - ${ genWriteVal('{ i }.asExprOf[e], t.elementRef.asInstanceOf[RTypeRef[e]], out) } - } - $out.endArray() - } - } - - // case t: ScalaClassRef[?] if t.isSealed => // basically just like sealed trait... - case t: Sealable if t.isSealed => // basically just like sealed trait... - if t.childrenAreObject then - val tin = aE.asExprOf[b] - // case object -> just write the simple name of the object - '{ - if $tin == null then $out.burpNull() - else $out.value($tin.getClass.getName.split('.').last.stripSuffix("$")) - } - else - val cases = t.sealedChildren.map { child => - child.refType match - case '[c] => - val subtype = TypeRepr.of[c] - val sym = Symbol.newBind(Symbol.spliceOwner, "t", Flags.EmptyFlags, subtype) - CaseDef(Bind(sym, Typed(Wildcard(), Inferred(subtype))), None, genEncFnBody[c](child, Ref(sym).asExprOf[c], out, true).asTerm) - } :+ CaseDef(Literal(NullConstant()), None, '{ $out.burpNull() }.asTerm) - val matchExpr = Match(aE.asTerm, cases).asExprOf[Unit] - matchExpr - - // We don't use makeWriteFn here because a value class is basically just a "box" around a simple type - case t: ScalaClassRef[?] if t.isValueClass => - val theField = t.fields.head.fieldRef - theField.refType match - case '[e] => - val fieldValue = Select.unique(aE.asTerm, t.fields.head.name).asExprOf[e] - genWriteVal(fieldValue, theField.asInstanceOf[RTypeRef[e]], out, isMapKey = isMapKey) - - case t: ScalaClassRef[?] => - makeWriteFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => - val body = { - val eachField = t.fields.map { f => - f.fieldRef.refType match - case '[z] => - val fieldValue = Select.unique(in.asTerm, f.name).asExprOf[z] - val fieldName = changeFieldName(f) - maybeWrite[z](fieldName, fieldValue, f.fieldRef.asInstanceOf[RTypeRef[z]], out, cfg) - } - if emitDiscriminator then - val cname = cfg.typeHintPolicy match - case TypeHintPolicy.SIMPLE_CLASSNAME => Expr(lastPart(t.name)) - case TypeHintPolicy.SCRAMBLE_CLASSNAME => '{ scramble(${ Expr(lastPart(t.name).hashCode) }) } - case TypeHintPolicy.USE_ANNOTATION => - Expr(t.annotations.get("co.blocke.scalajack.TypeHint").flatMap(_.get("hintValue")).getOrElse(lastPart(t.name))) - val withDisc = '{ - $out.label(${ Expr(cfg.typeHintLabel) }) - $out.value($cname) - } +: eachField - Expr.block(withDisc.init, withDisc.last) - else if eachField.length == 1 then eachField.head - else Expr.block(eachField.init, eachField.last) - } - - if !t.isCaseClass && cfg._writeNonConstructorFields then - val eachField = t.nonConstructorFields.map { f => - f.fieldRef.refType match - case '[e] => - val fieldValue = Select.unique(in.asTerm, f.getterLabel).asExprOf[e] - val fieldName = changeFieldName(f) - maybeWrite[e](fieldName, fieldValue, f.fieldRef.asInstanceOf[RTypeRef[e]], out, cfg) - } - val subBody = eachField.length match - case 0 => '{} - case 1 => eachField.head - case _ => Expr.block(eachField.init, eachField.last) - '{ - if $in == null then $out.burpNull() - else - $out.startObject() - $body - $subBody - $out.endObject() - } - else - '{ - if $in == null then $out.burpNull() - else - $out.startObject() - $body - $out.endObject() - } - } - - case t: MapRef[?] => - makeWriteFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => - t.elementRef.refType match - case '[k] => - t.elementRef2.refType match - case '[v] => - val tin = if t.isMutable then in.asExprOf[scala.collection.mutable.Map[k, v]] else in.asExprOf[Map[k, v]] - '{ - if $tin == null then $out.burpNull() - else - $out.startObject() - $tin.foreach { case (key, value) => - ${ - (t.elementRef, t.elementRef2) match - case (aliasK: AliasRef[?], aliasV: AliasRef[?]) => - aliasK.unwrappedType.refType match - case '[ak] => - aliasV.unwrappedType.refType match - case '[av] => - testValidMapKey(aliasK.unwrappedType) - maybeWriteMap[ak, av]( - '{ key.asInstanceOf[ak] }, - '{ value.asInstanceOf[av] }, - aliasK.unwrappedType.asInstanceOf[RTypeRef[ak]], - aliasV.unwrappedType.asInstanceOf[RTypeRef[av]], - out, - cfg - ) - case (_, aliasV: AliasRef[?]) => - aliasV.unwrappedType.refType match - case '[av] => - testValidMapKey(t.elementRef) - maybeWriteMap[k, av]( - '{ key }.asExprOf[k], - '{ value.asInstanceOf[av] }, - t.elementRef.asInstanceOf[RTypeRef[k]], - aliasV.unwrappedType.asInstanceOf[RTypeRef[av]], - out, - cfg - ) - case (aliasK: AliasRef[?], _) => - aliasK.unwrappedType.refType match - case '[ak] => - testValidMapKey(aliasK.unwrappedType) - maybeWriteMap[ak, v]( - '{ key.asInstanceOf[ak] }, - '{ value }.asExprOf[v], - aliasK.unwrappedType.asInstanceOf[RTypeRef[ak]], - t.elementRef2.asInstanceOf[RTypeRef[v]], - out, - cfg - ) - case (_, _) => - testValidMapKey(t.elementRef) - maybeWriteMap[k, v]('{ key }, '{ value }.asExprOf[v], t.elementRef.asInstanceOf[RTypeRef[k]], t.elementRef2.asInstanceOf[RTypeRef[v]], out, cfg) - } - } - $out.endObject() - } - } - - case t: JavaCollectionRef[?] => - makeWriteFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => - t.elementRef.refType match - case '[e] => - val tin = '{ $in.asInstanceOf[java.util.Collection[_]] } - '{ - if $tin == null then $out.burpNull() - else - $out.startArray() - $tin.toArray.foreach { elem => - ${ genWriteVal('{ elem.asInstanceOf[e] }, t.elementRef.asInstanceOf[RTypeRef[e]], out) } - } - $out.endArray() - } - } - - case t: JavaMapRef[?] => - makeWriteFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => - t.elementRef.refType match - case '[k] => - t.elementRef2.refType match - case '[v] => - val tin = in.asExprOf[java.util.Map[k, v]] - '{ - if $tin == null then $out.burpNull() - else - $out.startObject() - $tin.asScala.foreach { case (key, value) => - ${ - maybeWriteMap[k, v]('{ key }, '{ value }.asExprOf[v], t.elementRef.asInstanceOf[RTypeRef[k]], t.elementRef2.asInstanceOf[RTypeRef[v]], out, cfg) - } - } - $out.endObject() - } - } - - case t: JavaClassRef[?] => - makeWriteFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => - t.refType match - case '[p] => - val rtype = t.expr.asExprOf[JavaClassRType[p]] - val tin = in.asExprOf[b] - var fieldRefs = t.fields.asInstanceOf[List[NonConstructorFieldInfoRef]] - val sref = ReflectOnType[String](q)(TypeRepr.of[String])(using scala.collection.mutable.Map.empty[TypedName, Boolean]) - '{ - if $tin == null then $out.burpNull() - else - $out.startObject() - val rt = $rtype - rt.fields.foreach { f => - val field = f.asInstanceOf[NonConstructorFieldInfo] - val m = $tin.getClass.getMethod(field.getterLabel) - m.setAccessible(true) - val fieldValue = m.invoke($tin) - val fieldName = field.annotations.get("co.blocke.scalajack.Change").flatMap(_.get("name")).getOrElse(f.name) - ${ - val ref = fieldRefs.head - fieldRefs = fieldRefs.tail - ref.fieldRef.refType match - case '[e] => - maybeWriteMap[String, e]('{ fieldName }, '{ fieldValue.asInstanceOf[e] }, sref, ref.fieldRef.asInstanceOf[RTypeRef[e]], out, cfg) - } - } - $out.endObject() - } - } - - case t: TraitRef[?] => throw JsonUnsupportedType("Non-sealed traits are not supported") - - // No makeWriteFn here--Option is just a wrapper to the real thingy - case t: ScalaOptionRef[?] => - t.optionParamType.refType match - case '[e] => - val tin = aE.asExprOf[b] - '{ - $tin match - case null => $out.burpNull() - case None => - ${ - if cfg.noneAsNull || inTuple then '{ $out.burpNull() } - else '{ () } - } - case Some(v) => - val vv = v.asInstanceOf[e] - ${ genWriteVal[e]('{ vv }, t.optionParamType.asInstanceOf[RTypeRef[e]], out) } - } - case t: JavaOptionalRef[?] => - t.optionParamType.refType match - case '[e] => - val tin = aE.asExprOf[b] - '{ - $tin.asInstanceOf[java.util.Optional[e]] match - case null => $out.burpNull() - case o if o.isEmpty => - ${ - if cfg.noneAsNull || inTuple then '{ $out.burpNull() } - else '{ () } - } - case o => - val vv = o.get().asInstanceOf[e] - ${ genWriteVal[e]('{ vv }, t.optionParamType.asInstanceOf[RTypeRef[e]], out) } - } - - // No makeWriteFn here... SelfRef is referring to something we've already seen before. There absolutely should already be a geneated - // and cached function for this thing that we can call. - case t: SelfRefRef[?] => - t.refType match - case '[e] => - val ref = ReflectOnType[e](q)(TypeRepr.of[e])(using scala.collection.mutable.Map.empty[TypedName, Boolean]) - val key = MethodKey(ref, false) - val sym = writeMethodSyms(key) - val tin = aE.asExprOf[b] - '{ - if $tin == null then $out.burpNull() - else ${ Ref(sym).appliedTo(tin.asTerm, out.asTerm).asExprOf[Unit] } - } - - // No makeWriteFn here. All LeftRight types (Either, Union, Intersection) are just type wrappers - case t: LeftRightRef[?] => - val tin = aE.asExprOf[b] - t.leftRef.refType match - case '[lt] => - t.rightRef.refType match - case '[rt] => - // This is a close parallel with maybeWrite handling of Either. If the Either is a field in a class or - // Map, the maybeWrite logic applies--because we need to not write both the Either value AND the field label. - // If the Either is part of a tuple, Seq, etc., then this logic applies. - if t.lrkind == LRKind.EITHER then - cfg.eitherLeftHandling match - case EitherLeftPolicy.AS_VALUE => - '{ - if $tin == null then $out.burpNull() - else - $tin match - case Left(v) => - ${ genWriteVal[lt]('{ v.asInstanceOf[lt] }, t.leftRef.asInstanceOf[RTypeRef[lt]], out, inTuple = inTuple) } - case Right(v) => - ${ genWriteVal[rt]('{ v.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out, inTuple = inTuple) } - } - case EitherLeftPolicy.AS_NULL => - '{ - if $tin == null then $out.burpNull() - else - $tin match - case Left(v) => $out.burpNull() - case Right(v) => - ${ genWriteVal[rt]('{ v.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out, inTuple = inTuple) } - } - case EitherLeftPolicy.ERR_MSG_STRING => - '{ - if $tin == null then $out.burpNull() - else - $tin match - case Left(v) => $out.value("Left Error: " + v.toString) - case Right(v) => - ${ genWriteVal[rt]('{ v.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out, inTuple = inTuple) } - } - case EitherLeftPolicy.THROW_EXCEPTION => - '{ - if $tin == null then $out.burpNull() - else - $tin match - case Left(v) => throw new JsonEitherLeftError("Left Error: " + v.toString) - case Right(v) => - ${ genWriteVal[rt]('{ v.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out, inTuple = inTuple) } - } - else - '{ - $out.mark() - scala.util.Try { - ${ genWriteVal[rt]('{ $tin.asInstanceOf[rt] }, t.rightRef.asInstanceOf[RTypeRef[rt]], out, inTuple = inTuple) } - } match - case scala.util.Success(_) => () // do nothing further--write to out already happened - case scala.util.Failure(_) => - $out.revert() - ${ genWriteVal[lt]('{ $tin.asInstanceOf[lt] }, t.leftRef.asInstanceOf[RTypeRef[lt]], out, inTuple = inTuple) } - } - - // No makeWriteFn here. Try is just a wrapper - case t: TryRef[?] => - t.tryRef.refType match - case '[e] => - val tin = aE.asExprOf[scala.util.Try[e]] - '{ - if $tin == null then $out.burpNull() - else - $tin match - case scala.util.Success(v) => - ${ genWriteVal[e]('{ v }.asInstanceOf[Expr[e]], t.tryRef.asInstanceOf[RTypeRef[e]], out, inTuple = inTuple) } - case scala.util.Failure(v) => - ${ - cfg.tryFailureHandling match - case _ if inTuple => '{ $out.burpNull() } - case TryPolicy.AS_NULL => '{ $out.burpNull() } - case TryPolicy.ERR_MSG_STRING => '{ $out.value("Try Failure with msg: " + v.getMessage()) } - case TryPolicy.THROW_EXCEPTION => '{ throw v } - } - } - - case t: TupleRef[?] => - makeWriteFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => - '{ - if $in == null then $out.burpNull() - else - $out.startArray() - ${ - // Note: Don't use maybeWrite here... Tuples are fixed-length. We need to write - // something for every position, so write null for None or other "bad" values - val elementsE = t.tupleRefs.zipWithIndex.map { case (ref, i) => - ref.refType match - case '[e] => - val fieldValue = Select.unique(in.asTerm, "_" + (i + 1)).asExprOf[e] - genWriteVal[e](fieldValue, ref.asInstanceOf[RTypeRef[e]], out, inTuple = true) - } - if elementsE.size == 1 then elementsE.head - else Expr.block(elementsE.init, elementsE.last) - } - $out.endArray() - } - } - - case t => throw new JsonUnsupportedType("Type represented by " + t.name + " is unsupported for JSON writes") - - // --------------------------------------------------------------------------------------------- - - def genWriteVal[T: Type]( - aE: Expr[T], - ref: RTypeRef[T], - out: Expr[JsonOutput], - inTuple: Boolean = false, - isMapKey: Boolean = false // only primitive or primitive-equiv types can be Map keys - )(using Quotes): Expr[Unit] = - val methodKey = MethodKey(ref, false) - writeMethodSyms - .get(methodKey) - .map { sym => // hit cache first... then match on Ref type - Apply(Ref(sym), List(aE.asTerm, out.asTerm)).asExprOf[Unit] - } - .getOrElse( - ref match - // First cover all primitive and simple types... - case t: BigDecimalRef => - if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[scala.math.BigDecimal] }) } - else '{ $out.value(${ aE.asExprOf[scala.math.BigDecimal] }) } - case t: BigIntRef => - if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[scala.math.BigInt] }) } - else '{ $out.value(${ aE.asExprOf[scala.math.BigInt] }) } - case t: BooleanRef => - if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[Boolean] }) } - else '{ $out.value(${ aE.asExprOf[Boolean] }) } - case t: ByteRef => - if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[Byte] }) } - else '{ $out.value(${ aE.asExprOf[Byte] }) } - case t: CharRef => - '{ $out.value(${ aE.asExprOf[Char] }) } - case t: DoubleRef => - if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[Double] }) } - else '{ $out.value(${ aE.asExprOf[Double] }) } - case t: FloatRef => - if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[Float] }) } - else '{ $out.value(${ aE.asExprOf[Float] }) } - case t: IntRef => - if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[Int] }) } - else '{ $out.value(${ aE.asExprOf[Int] }) } - case t: LongRef => - if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[Long] }) } - else '{ $out.value(${ aE.asExprOf[Long] }) } - case t: ShortRef => - if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[Short] }) } - else '{ $out.value(${ aE.asExprOf[Short] }) } - case t: StringRef => '{ $out.valueEscaped(${ aE.asExprOf[String] }) } - - case t: JBigDecimalRef => - if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[java.math.BigDecimal] }) } - else '{ $out.value(${ aE.asExprOf[java.math.BigDecimal] }) } - case t: JBigIntegerRef => - if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[java.math.BigInteger] }) } - else '{ $out.value(${ aE.asExprOf[java.math.BigInteger] }) } - case t: JBooleanRef => - if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[java.lang.Boolean] }) } - else '{ $out.value(${ aE.asExprOf[java.lang.Boolean] }) } - case t: JByteRef => - if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[java.lang.Byte] }) } - else '{ $out.value(${ aE.asExprOf[java.lang.Byte] }) } - case t: JCharacterRef => - '{ $out.value(${ aE.asExprOf[java.lang.Character] }) } - case t: JDoubleRef => - if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[java.lang.Double] }) } - else '{ $out.value(${ aE.asExprOf[java.lang.Double] }) } - case t: JFloatRef => - if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[java.lang.Float] }) } - else '{ $out.value(${ aE.asExprOf[java.lang.Float] }) } - case t: JIntegerRef => - if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[java.lang.Integer] }) } - else '{ $out.value(${ aE.asExprOf[java.lang.Integer] }) } - case t: JLongRef => - if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[java.lang.Long] }) } - else '{ $out.value(${ aE.asExprOf[java.lang.Long] }) } - case t: JShortRef => - if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[java.lang.Short] }) } - else '{ $out.value(${ aE.asExprOf[java.lang.Short] }) } - case t: JNumberRef => - if isMapKey then '{ $out.valueStringified(${ aE.asExprOf[java.lang.Number] }) } - else '{ $out.value(${ aE.asExprOf[java.lang.Number] }) } - - case t: DurationRef => '{ $out.value(${ aE.asExprOf[java.time.Duration] }) } - case t: InstantRef => '{ $out.value(${ aE.asExprOf[java.time.Instant] }) } - case t: LocalDateRef => '{ $out.value(${ aE.asExprOf[java.time.LocalDate] }) } - case t: LocalDateTimeRef => '{ $out.value(${ aE.asExprOf[java.time.LocalDateTime] }) } - case t: LocalTimeRef => '{ $out.value(${ aE.asExprOf[java.time.LocalTime] }) } - case t: MonthDayRef => '{ $out.value(${ aE.asExprOf[java.time.MonthDay] }) } - case t: OffsetDateTimeRef => '{ $out.value(${ aE.asExprOf[java.time.OffsetDateTime] }) } - case t: OffsetTimeRef => '{ $out.value(${ aE.asExprOf[java.time.OffsetTime] }) } - case t: PeriodRef => '{ $out.value(${ aE.asExprOf[java.time.Period] }) } - case t: YearRef => '{ $out.value(${ aE.asExprOf[java.time.Year] }) } - case t: YearMonthRef => '{ $out.value(${ aE.asExprOf[java.time.YearMonth] }) } - case t: ZonedDateTimeRef => '{ $out.value(${ aE.asExprOf[java.time.ZonedDateTime] }) } - case t: ZoneIdRef => '{ $out.value(${ aE.asExprOf[java.time.ZoneId] }) } - case t: ZoneOffsetRef => '{ $out.value(${ aE.asExprOf[java.time.ZoneOffset] }) } - - case t: URLRef => '{ $out.value(${ aE.asExprOf[java.net.URL] }) } - case t: URIRef => '{ $out.value(${ aE.asExprOf[java.net.URI] }) } - case t: UUIDRef => '{ $out.value(${ aE.asExprOf[java.util.UUID] }) } - case t: ObjectRef[?] => '{ $out.value(${ Expr(t.name) }) } - - case t: AliasRef[?] => - // Special check for RawJson pseudo-type - if lastPart(t.definedType) == "RawJson" then '{ $out.valueRaw(${ aE.asExprOf[RawJson] }) } - else - t.unwrappedType.refType match - case '[e] => - genWriteVal[e](aE.asInstanceOf[Expr[e]], t.unwrappedType.asInstanceOf[RTypeRef[e]], out, inTuple = inTuple) - - // These one's here becaue Enums and their various flavors can be Map keys - // (EnumRef handles: Scala 3 enum, Scala 2 Enumeration, Java Enumeration) - case t: EnumRef[?] => - val enumAsId = cfg.enumsAsIds match - case None => false - case Some(Nil) => true - case Some(list) if list.contains(t.name) => true - case _ => false - val rtype = t.expr - if enumAsId then - if isMapKey then '{ $out.value($rtype.asInstanceOf[EnumRType[_]].ordinal($aE.toString).get.toString) } // stringified id - else '{ $out.value($rtype.asInstanceOf[EnumRType[_]].ordinal($aE.toString).get) } // int value of id - else '{ $out.value($aE.toString) } - - // NeoType is a bit of a puzzle-box. To get the correct underlying base type, I had to dig into - // the argument of method validate$retainedBody. It happened to have the correctly-typed parameter. - // With the correct type, we can correct write out the value. - case t: NeoTypeRef[?] => // in Quotes context - Symbol.requiredModule(t.typedName.toString).methodMember("validate$retainedBody").head.paramSymss.head.head.tree match - case ValDef(_, tt, _) => - tt.tpe.asType match - case '[u] => - val baseTypeRef = ReflectOnType.apply(q)(tt.tpe)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) - genWriteVal[u]('{ $aE.asInstanceOf[u] }, baseTypeRef.asInstanceOf[RTypeRef[u]], out) - - /* This is how you call make(), which includes validate() - val myMake = module.methodMember("make").head - val tm = Ref(module) - val z = Apply( - Select.unique(tm, "make"), - List( - Expr(List(1, 2, 3)).asTerm - ) - ).asExprOf[Either[String, _]] - '{ - println("Hello...") - println($z) - } - */ - - case t: AnyRef => '{ AnyWriter.writeAny($aE, $out, ${ Expr(cfg) }) } - - // Everything else... - // case _ if isStringified => throw new JsonIllegalKeyType("Non-primitive/non-simple types cannot be map keys") - case _ => genEncFnBody(ref, aE, out, inTuple = inTuple, isMapKey = isMapKey) - ) diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala index 88174d3f..e552a65f 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -2,108 +2,35 @@ package co.blocke.scalajack package json package run -import co.blocke.scala_reflection.* -import scala.jdk.CollectionConverters.* -import scala.reflect.ClassTag -import json.* -import scala.collection.immutable.Queue -import co.blocke.scalajack.json.reading.SafeNumbers.double -import co.blocke.scalajack.json.schema.* - -class Shape[T](polygon: T) -class Parallelogram() -class Rectangle() extends Parallelogram +case class Well(repo: scala.collection.mutable.Map[String, Int] = scala.collection.mutable.Map.empty[String, Int]) object RunMe extends App: - val suite: Shape[Parallelogram] = new Shape[Parallelogram](new Parallelogram()) - - // UPDATE: I have no idea what these two cases actually test! They seem to do different things... - - // val s = "\"This is a test\"" - // val now = System.nanoTime() - // (1 to 1000000).map(_ => exp.readFromString(s)) - // val later = System.nanoTime() - // println("JsonReader: " + (later - now)) - - // println("==============================") - - // val s2 = "This is a test\"" - // val now2 = System.nanoTime() - // (1 to 1000000).map(_ => parseString(reading.JsonSource(s2))) - // val later2 = System.nanoTime() - // println("SJ : " + (later2 - now2)) - - // def parseString(in: reading.JsonSource): CharSequence = - // // charWithWS(in, '"') - // val sb = new reading.FastStringBuilder(64)x - // while true do - // val c = in.readEscapedString() - // if c == END_OF_STRING then return sb.buffer // mutable thing escapes, but cannot be changed - // sb.append(c.toChar) - // throw JsonParseError("Invalid string value detected", in) - import ScalaJack.* import co.blocke.scalajack.json.run.Record - println("\n") - - val a: java.util.Set[Int] = new java.util.TreeSet[Int]() - val b: java.util.Set[Int] = new java.util.TreeSet[Int]() - val tt = new java.util.TreeSet[java.util.Set[Int]](java.util.Arrays.asList(a, b).asInstanceOf[java.util.Collection[java.util.Set[Int]]]) - println(tt) -//case class JJ(a: java.util.Stack[Int]) - // given blah: ScalaJack[JJ] = sjCodecOf[JJ] - // val js = """{"a":[1,2,3]}""" - // println(blah.fromJson(js)) - - // println(RType.of[Person].pretty) - - // implicit val blah: ScalaJack[Record] = sj[Record] // (JsonConfig.withSuppressedEscapedStrings()) - // println(blah.toJson(record)) - -// val f = new FastStringBuilder() -// val s = """Gregory "William" -// Zoller""" + "\u20A0 wow" -// f.appendEscaped(s, 0, s.length) -// println(f.result) - - // implicit val blah: ScalaJack[schema.Schema] = codecOf[schema.Schema](JsonConfig.withSuppressTypeHints()) - // val oride = Map("co.blocke.scalajack.json.run.Header$Who$$Type" -> schema.EnumSchema(List("staff", "customer", "program"))) - // println(ScalaJack[schema.Schema].toJson(schema.JsonSchema.of[DormancyEvent](oride))) - - // NOT YET! - // implicit val blah: ScalaJack[schema.Schema] = codecOf[schema.Schema](JsonConfig.withSuppressTypeHints()) - // val s = schema.JsonSchema.of[DormancyEvent] - // println(ScalaJack[schema.Schema].toJson(s)) - - // val jssrc = json.reading.JsonSource(""""Red"""") - // println("E: " + jssrc.expectEnum()) - // implicit val blah: ScalaJack[Pizza] = codecOf[Pizza] - // val c: Pizza = ScalaJack[Pizza].fromJson("\"READY\"") - // println("Pizza: " + c) + // given sjPerson: ScalaJack[Person] = sjCodecOf[Person] + // println(sjPerson.toJson(Person())) - // case class LRUnionHolder2[T, U](a: Seq[Boolean | Int], b: (T | U, T | U)) - // implicit val blah: ScalaJack[LRUnionHolder2[scala.util.Try[Option[Int]], String]] = sjCodecOf[LRUnionHolder2[scala.util.Try[Option[Int]], String]] - // val b: LRUnionHolder2[scala.util.Try[Option[Int]], String] = LRUnionHolder2(List(true, 3, false, -1), ("y", scala.util.Success(Some(5)))) - // val js = ScalaJack[LRUnionHolder2[scala.util.Try[Option[Int]], String]].toJson(b) - // println(js) - // println(ScalaJack[LRUnionHolder2[scala.util.Try[Option[Int]], String]].fromJson(js)) + println(sjCodecOf[Person].toJson(Person())) - println("done.") + given Well() - /* + summon[Well].repo.put("a", 1) + summon[Well].repo.put("b", 2) + println(summon[Well]) - Option[Left(5)] -> None - Either[Err,Option[String]] + /* + + given ScalaJack() - Left-Policy Class Field Option-Wrapped In Collection In Tuple - ---------------- -------------- --------------- --------------- ------------ - NO_WRITE Null None () Null <-- KILL NO_WRITE!! A symantic mess! - AS_VALUE Value Value Value Value - AS_NULL Null Null Null Null - ERR_MSG_STRING Err string Err String Err String Err String - THROW_EXCEPTION Throw Exception Throw Exception Throw Exception Throw Excpeiton + sj[Foo].toJson(myFoo) - */ + Macro: + 1. Summons SJWell (error if not found) + 2. Looks up codec for Foo. If found, emits a call to it. + 3. If not found, create codec and populate in SJWell + 4. Subsequent calls to sj[Foo] should *NOT* re-gen the macro expansion + + */ \ No newline at end of file diff --git a/src/test/scala/co.blocke.scalajack/json/classes/TraitSpec.scala b/src/test/scala/co.blocke.scalajack/json/classes/TraitSpec.scala index e7f83daf..15942806 100644 --- a/src/test/scala/co.blocke.scalajack/json/classes/TraitSpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/classes/TraitSpec.scala @@ -44,7 +44,7 @@ class TraitSpec() extends AnyFunSpec with JsonMatchers: val js = sj.toJson(inst) val diff = parseJValue(js).diff(parseJValue("""{"a":"Start","b":{"_hint":"86999-847-46A","species":"Beta","freshwater":false},"c":{"_hint":"13652-857-33B","temp":101.1},"d":{"_hint":"51470-503-54B","numStreets":25}}""")) val diffMap = diff.changed.values.asInstanceOf[Map[String, Map[String, ?]]] - assert(diffMap("b").contains("_hint") && diffMap("c").contains("_hint") && diffMap("d").contains("_hint") == true) // ie only the scrambled _hint values are different + assert(diffMap("b").contains("_hint") && diffMap("c").contains("_hint") && diffMap("d").contains("_hint") == true) // ie only the scrambled _hint values are different val re = sj.fromJson(js) re.a shouldEqual (inst.a) re.b shouldEqual (inst.b) diff --git a/src/test/scala/co.blocke.scalajack/json/collections/SeqSetArraySpec.scalax b/src/test/scala/co.blocke.scalajack/json/collections/SeqSetArraySpec.scala similarity index 58% rename from src/test/scala/co.blocke.scalajack/json/collections/SeqSetArraySpec.scalax rename to src/test/scala/co.blocke.scalajack/json/collections/SeqSetArraySpec.scala index 7cf615a4..f42d218d 100644 --- a/src/test/scala/co.blocke.scalajack/json/collections/SeqSetArraySpec.scalax +++ b/src/test/scala/co.blocke.scalajack/json/collections/SeqSetArraySpec.scala @@ -17,155 +17,215 @@ class SeqSetArraySpec() extends AnyFunSpec with JsonMatchers: describe(colorString("+++ Positive Tests +++")) { it("Seq is null must work") { val inst = SeqHolder[Int](null) - val js = sjCodecOf[SeqHolder[Int]].toJson(inst) + val sj = sjCodecOf[SeqHolder[Int]] + val js = sj.toJson(inst) js should matchJson("""{"a":null}""") + sj.fromJson(js) shouldEqual(inst) } it("Seq of numeric must work") { val inst = SeqHolder[Int](List(1, 2, 3)) - val js = sjCodecOf[SeqHolder[Int]].toJson(inst) + val sj = sjCodecOf[SeqHolder[Int]] + val js = sj.toJson(inst) js should matchJson("""{"a":[1,2,3]}""") + sj.fromJson(js) shouldEqual(inst) } it("Seq of string must work") { val inst = SeqHolder[String](List("a", "b", "c")) - val js = sjCodecOf[SeqHolder[String]].toJson(inst) + val sj = sjCodecOf[SeqHolder[String]] + val js = sj.toJson(inst) js should matchJson("""{"a":["a","b","c"]}""") + sj.fromJson(js) shouldEqual(inst) } it("Seq of boolean must work") { val inst = SeqHolder[Boolean](List(true, false, true)) - val js = sjCodecOf[SeqHolder[Boolean]].toJson(inst) + val sj = sjCodecOf[SeqHolder[Boolean]] + val js = sj.toJson(inst) js should matchJson("""{"a":[true,false,true]}""") + sj.fromJson(js) shouldEqual(inst) } it("Seq of seq (nested) must work") { val inst = SeqHolder[List[Int]](List(List(1, 2), List(3, 4))) - val js = sjCodecOf[SeqHolder[List[Int]]].toJson(inst) + val sj = sjCodecOf[SeqHolder[List[Int]]] + val js = sj.toJson(inst) js should matchJson("""{"a":[[1,2],[3,4]]}""") + sj.fromJson(js) shouldEqual(inst) } it("Seq of either must work") { val inst = SeqHolder[Either[Int, Boolean]](List(Right(true), Left(15), Right(false))) - val js = sjCodecOf[SeqHolder[Either[Int, Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)).toJson(inst) + val sj = sjCodecOf[SeqHolder[Either[Int, Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)) + val js = sj.toJson(inst) js should matchJson("""{"a":[true,15,false]}""") + sj.fromJson(js) shouldEqual(inst) } it("Seq of union must work") { val inst = SeqHolder[Int | Boolean](List(true, 15, false)) - val js = sjCodecOf[SeqHolder[Int | Boolean]].toJson(inst) + val sj = sjCodecOf[SeqHolder[Int | Boolean]] + val js = sj.toJson(inst) js should matchJson("""{"a":[true,15,false]}""") + sj.fromJson(js) shouldEqual(inst) } it("Seq of option must work") { val inst = SeqHolder[Option[Int]](List(Some(1), None, Some(3))) - val js = sjCodecOf[SeqHolder[Option[Int]]].toJson(inst) + val sj = sjCodecOf[SeqHolder[Option[Int]]] + val js = sj.toJson(inst) js should matchJson("""{"a":[1,3]}""") + sj.fromJson(js) shouldEqual(SeqHolder[Option[Int]](List(Some(1), Some(3)))) } it("Seq of map must work") { val inst = SeqHolder[Map[String, Int]](List(Map("a" -> 1, "b" -> 2), Map("c" -> 3, "d" -> 4))) - val js = sjCodecOf[SeqHolder[Map[String, Int]]].toJson(inst) + val sj = sjCodecOf[SeqHolder[Map[String, Int]]] + val js = sj.toJson(inst) js should matchJson("""{"a":[{"a":1,"b":2},{"c":3,"d":4}]}""") + sj.fromJson(js) shouldEqual(inst) } it("Seq of class must work") { val inst = SeqHolder[Person](List(Person("Bob", 35), Person("Sally", 54))) - val js = sjCodecOf[SeqHolder[Person]].toJson(inst) + val sj = sjCodecOf[SeqHolder[Person]] + val js = sj.toJson(inst) js should matchJson("""{"a":[{"name":"Bob","age",35},{"name":"Sally","age",54}]}""") + sj.fromJson(js) shouldEqual(inst) } it("Set is null must work") { val inst = SetHolder[Int](null) - val js = sjCodecOf[SetHolder[Int]].toJson(inst) + val sj = sjCodecOf[SetHolder[Int]] + val js = sj.toJson(inst) js should matchJson("""{"a":null}""") + sj.fromJson(js) shouldEqual(inst) } it("Set of numeric must work") { val inst = SetHolder[Int](HashSet(1, 2, 3)) - val js = sjCodecOf[SetHolder[Int]].toJson(inst) + val sj = sjCodecOf[SetHolder[Int]] + val js = sj.toJson(inst) js should matchJson("""{"a":[1,2,3]}""") + sj.fromJson(js) shouldEqual(inst) } it("Set of string must work") { val inst = SetHolder[String](HashSet("a", "b", "c")) - val js = sjCodecOf[SetHolder[String]].toJson(inst) + val sj = sjCodecOf[SetHolder[String]] + val js = sj.toJson(inst) js should matchJson("""{"a":["a","b","c"]}""") + sj.fromJson(js) shouldEqual(inst) } it("Set of boolean must work") { val inst = SetHolder[Boolean](HashSet(true, false, true)) - val js = sjCodecOf[SetHolder[Boolean]].toJson(inst) + val sj = sjCodecOf[SetHolder[Boolean]] + val js = sj.toJson(inst) js should matchJson("""{"a":[false,true]}""") + sj.fromJson(js) shouldEqual(inst) } it("Set of Set (nested) must work") { val inst = SetHolder[HashSet[Int]](HashSet(HashSet(1, 2), HashSet(3, 4))) - val js = sjCodecOf[SetHolder[HashSet[Int]]].toJson(inst) + val sj = sjCodecOf[SetHolder[HashSet[Int]]] + val js = sj.toJson(inst) js should matchJson("""{"a":[[3,4],[1,2]]}""") + sj.fromJson(js) shouldEqual(inst) } it("Set of either must work") { val inst = SetHolder[Either[Int, Boolean]](HashSet(Right(true), Left(15), Right(false))) - val js = sjCodecOf[SetHolder[Either[Int, Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)).toJson(inst) + val sj = sjCodecOf[SetHolder[Either[Int, Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)) + val js = sj.toJson(inst) js should matchJson("""{"a":[15,true,false]}""") + sj.fromJson(js) shouldEqual(inst) } it("Set of union must work") { val inst = SetHolder[Int | Boolean](HashSet(true, 15, false)) - val js = sjCodecOf[SetHolder[Int | Boolean]].toJson(inst) + val sj = sjCodecOf[SetHolder[Int | Boolean]] + val js = sj.toJson(inst) js should matchJson("""{"a":[false,true,15]}""") + sj.fromJson(js) shouldEqual(inst) } it("Set of option must work") { val inst = SetHolder[Option[Int]](HashSet(Some(1), None, Some(3))) - val js = sjCodecOf[SetHolder[Option[Int]]].toJson(inst) + val sj = sjCodecOf[SetHolder[Option[Int]]] + val js = sj.toJson(inst) js should matchJson("""{"a":[3,1]}""") + sj.fromJson(js) shouldEqual(SetHolder[Option[Int]](HashSet(Some(1), Some(3)))) } it("Set of map must work") { val inst = SetHolder[Map[String, Int]](HashSet(Map("a" -> 1, "b" -> 2), Map("c" -> 3, "d" -> 4))) - val js = sjCodecOf[SetHolder[Map[String, Int]]].toJson(inst) + val sj = sjCodecOf[SetHolder[Map[String, Int]]] + val js = sj.toJson(inst) js should matchJson("""{"a":[{"c":3,"d":4},{"a":1,"b":2}]}""") + sj.fromJson(js) shouldEqual(inst) } it("Set of class must work") { val inst = SetHolder[Person](HashSet(Person("Bob", 35), Person("Sally", 54))) - val js = sjCodecOf[SetHolder[Person]].toJson(inst) + val sj = sjCodecOf[SetHolder[Person]] + val js = sj.toJson(inst) js should matchJson("""{"a":[{"name":"Bob","age",35},{"name":"Sally","age",54}]}""") + sj.fromJson(js) shouldEqual(inst) } it("Array is null must work") { val inst = ArrayHolder[Int](null) - val js = sjCodecOf[ArrayHolder[Int]].toJson(inst) + val sj = sjCodecOf[ArrayHolder[Int]] + val js = sj.toJson(inst) js should matchJson("""{"a":null}""") + sj.fromJson(js) shouldEqual(inst) } it("Array of numeric must work") { val inst = ArrayHolder[Int](Array(1, 2, 3)) - val js = sjCodecOf[ArrayHolder[Int]].toJson(inst) + val sj = sjCodecOf[ArrayHolder[Int]] + val js = sj.toJson(inst) js should matchJson("""{"a":[1,2,3]}""") + sj.fromJson(js).a.toList shouldEqual(inst.a.toList) } it("Array of string must work") { val inst = ArrayHolder[String](Array("a", "b", "c")) - val js = sjCodecOf[ArrayHolder[String]].toJson(inst) + val sj = sjCodecOf[ArrayHolder[String]] + val js = sj.toJson(inst) js should matchJson("""{"a":["a","b","c"]}""") + sj.fromJson(js).a.toList shouldEqual(inst.a.toList) } it("Array of boolean must work") { val inst = ArrayHolder[Boolean](Array(true, false, true)) - val js = sjCodecOf[ArrayHolder[Boolean]].toJson(inst) + val sj = sjCodecOf[ArrayHolder[Boolean]] + val js = sj.toJson(inst) js should matchJson("""{"a":[true,false,true]}""") + sj.fromJson(js).a.toList shouldEqual(inst.a.toList) } it("Array of Array (nested) must work") { val inst = ArrayHolder[Array[Int]](Array(Array(1, 2), Array(3, 4))) - val js = sjCodecOf[ArrayHolder[Array[Int]]].toJson(inst) + val sj = sjCodecOf[ArrayHolder[Array[Int]]] + val js = sj.toJson(inst) js should matchJson("""{"a":[[1,2],[3,4]]}""") + sj.fromJson(js).a.map(_.toList).toList shouldEqual(inst.a.map(_.toList).toList) } it("Array of either must work") { val inst = ArrayHolder[Either[Int, Boolean]](Array(Right(true), Left(15), Right(false))) - val js = sjCodecOf[ArrayHolder[Either[Int, Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)).toJson(inst) + val sj = sjCodecOf[ArrayHolder[Either[Int, Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)) + val js = sj.toJson(inst) js should matchJson("""{"a":[true,15,false]}""") + sj.fromJson(js).a.toList shouldEqual(inst.a.toList) } it("Array of union must work") { val inst = ArrayHolder[Int | Boolean](Array(true, 15, false)) - val js = sjCodecOf[ArrayHolder[Int | Boolean]].toJson(inst) + val sj = sjCodecOf[ArrayHolder[Int | Boolean]] + val js = sj.toJson(inst) js should matchJson("""{"a":[true,15,false]}""") + sj.fromJson(js).a.toList shouldEqual(inst.a.toList) } it("Array of option must work") { val inst = ArrayHolder[Option[Int]](Array(Some(1), None, Some(3))) - val js = sjCodecOf[ArrayHolder[Option[Int]]].toJson(inst) + val sj = sjCodecOf[ArrayHolder[Option[Int]]] + val js = sj.toJson(inst) js should matchJson("""{"a":[1,3]}""") + sj.fromJson(js).a.toList shouldEqual(ArrayHolder[Option[Int]](Array(Some(1), Some(3))).a.toList) } it("Array of map must work") { val inst = ArrayHolder[Map[String, Int]](Array(Map("a" -> 1, "b" -> 2), Map("c" -> 3, "d" -> 4))) - val js = sjCodecOf[ArrayHolder[Map[String, Int]]].toJson(inst) + val sj = sjCodecOf[ArrayHolder[Map[String, Int]]] + val js = sj.toJson(inst) js should matchJson("""{"a":[{"a":1,"b":2},{"c":3,"d":4}]}""") + sj.fromJson(js).a.toList shouldEqual(inst.a.toList) } it("Array of class must work") { val inst = ArrayHolder[Person](Array(Person("Bob", 35), Person("Sally", 54))) - val js = sjCodecOf[ArrayHolder[Person]].toJson(inst) + val sj = sjCodecOf[ArrayHolder[Person]] + val js = sj.toJson(inst) js should matchJson("""{"a":[{"name":"Bob","age",35},{"name":"Sally","age",54}]}""") + sj.fromJson(js).a.toList shouldEqual(inst.a.toList) } } } diff --git a/src/test/scala/co.blocke.scalajack/json/misc/EnumSpec.scala b/src/test/scala/co.blocke.scalajack/json/misc/EnumSpec.scala index ae3669b6..6cb843a8 100644 --- a/src/test/scala/co.blocke.scalajack/json/misc/EnumSpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/misc/EnumSpec.scala @@ -14,96 +14,100 @@ import TestUtil.* class EnumSpec() extends AnyFunSpec with JsonMatchers: describe(colorString("-------------------------------\n: Enum Tests :\n-------------------------------", Console.YELLOW)) { - it("Enum as Map key and value must work") { - val inst = MapHolder[Color, Color](Map(Color.Red -> Color.Blue, Color.Green -> Color.Red)) - val sj = sjCodecOf[MapHolder[Color, Color]] - val js = sj.toJson(inst) - js should matchJson("""{"a":{"Red":"Blue","Green":"Red"}}""") - sj.fromJson(js) shouldEqual(inst) + describe(colorString("+++ Positive Tests +++")) { + it("Enum as Map key and value must work") { + val inst = MapHolder[Color, Color](Map(Color.Red -> Color.Blue, Color.Green -> Color.Red)) + val sj = sjCodecOf[MapHolder[Color, Color]] + val js = sj.toJson(inst) + js should matchJson("""{"a":{"Red":"Blue","Green":"Red"}}""") + sj.fromJson(js) shouldEqual (inst) + } + it("Enum as Map key and value must work (using id)") { + val inst = MapHolder[Color, Color](Map(Color.Red -> Color.Blue, Color.Green -> Color.Red)) + val sj = sjCodecOf[MapHolder[Color, Color]](JsonConfig.withEnumsAsIds(Nil)) + val js = sj.toJson(inst) + js should matchJson("""{"a":{"0":1,"2":0}}""") + sj.fromJson(js) shouldEqual (inst) + } + it("Enumeration (Scale 2) as Map key and value must work") { + import Permissions.* + val inst = MapHolder[Permissions, Permissions](Map(Permissions.READ -> Permissions.WRITE, Permissions.EXEC -> Permissions.NONE)) + val sj = sjCodecOf[MapHolder[Permissions, Permissions]] + val js = sj.toJson(inst) + js should matchJson("""{"a":{"READ":"WRITE","EXEC":"NONE"}}""") + sj.fromJson(js) shouldEqual (inst) + } + it("Enumeration (Scale 2) as Map key and value must work (using id)") { + import Permissions.* + val inst = MapHolder[Permissions, Permissions](Map(Permissions.READ -> Permissions.WRITE, Permissions.EXEC -> Permissions.NONE)) + val sj = sjCodecOf[MapHolder[Permissions, Permissions]](JsonConfig.withEnumsAsIds(Nil)) + val js = sj.toJson(inst) + js should matchJson("""{"a":{"0":1,"2":3}}""") + sj.fromJson(js) shouldEqual (inst) + } + it("Enumeration (Scala 2) ordinal mutation") { + import SizeWithType.* + val inst = SampleEnum(Size.Small, Size.Medium, Size.Large, null, Size.Medium, Little) + val sj = sjCodecOf[SampleEnum] + val js = sj.toJson(inst) + js shouldEqual ("""{"e1":"Small","e2":"Medium","e3":"Large","e4":null,"e5":"Medium","e6":"Little"}""") + // mutate e5 into an ordinal... + val js2 = js.replaceAll(""""e5":"Medium"""", """"e5":1""") + sj.fromJson(js2) shouldEqual (inst) + } + it("Java Enumeration as Map key and value must work") { + val inst = MapHolder[CarEnum, CarEnum](Map(CarEnum.VW -> CarEnum.PORSCHE, CarEnum.PORSCHE -> CarEnum.TOYOTA)) + val sj = sjCodecOf[MapHolder[CarEnum, CarEnum]] + val js = sj.toJson(inst) + js should matchJson("""{"a":{"VW":"PORSCHE","PORSCHE":"TOYOTA"}}""") + sj.fromJson(js) shouldEqual (inst) + } + it("Java Enumeration as Map key and value must work (using id)") { + val inst = MapHolder[CarEnum, CarEnum](Map(CarEnum.VW -> CarEnum.PORSCHE, CarEnum.PORSCHE -> CarEnum.TOYOTA)) + val sj = sjCodecOf[MapHolder[CarEnum, CarEnum]](JsonConfig.withEnumsAsIds(Nil)) + val js = sj.toJson(inst) + val targetJs = RType.of[CarEnum] match + case t: co.blocke.scala_reflection.rtypes.JavaEnumRType[?] => + val valMap = t.values.zipWithIndex.toMap + s"""{"a":{"${valMap("VW")}":${valMap("PORSCHE")},"${valMap("PORSCHE")}":${valMap("TOYOTA")}}}""" + js shouldEqual (targetJs) + sj.fromJson(js) shouldEqual (inst) + } + it("Enum/Enumeration mix of enum as value must work") { + import Permissions.* + val inst = MapHolder[Color, Permissions](Map(Color.Red -> Permissions.WRITE, Color.Blue -> Permissions.NONE)) + val sj = sjCodecOf[MapHolder[Color, Permissions]](JsonConfig.withEnumsAsIds(List("co.blocke.scalajack.json.misc.Color"))) + val js = sj.toJson(inst) + js should matchJson("""{"a":{"0":"WRITE","1":"NONE"}}""") + sj.fromJson(js) shouldEqual (inst) + } + it("Sealed trait enumeration w/case objects must work") { + val inst = FlavorHolder(Chocolate, null, Map(Bourbon -> "a"), Map("a" -> Vanilla)) + val sj = sjCodecOf[FlavorHolder] + val js = sj.toJson(inst) + js should matchJson("""{"f":"Chocolate","f2":null,"f3":{"Bourbon":"a"},"f4":{"a":"Vanilla"}}""") + sj.fromJson(js) shouldEqual (inst) + } + it("Sealed trait enumeration w/case class must work") { + val inst = VehicleHolder(Car(4, "Red"), null, Map("a" -> Truck(18))) + val sj = sjCodecOf[VehicleHolder] + val js = sj.toJson(inst) + js should matchJson("""{"f":{"_hint":"Car","numberOfWheels":4,"color":"Red"},"f2":null,"f4":{"a":{"_hint":"Truck","numberOfWheels":18}}}""") + sj.fromJson(js) shouldEqual (inst) + } } - it("Enum as Map key and value must work (using id)") { - val inst = MapHolder[Color, Color](Map(Color.Red -> Color.Blue, Color.Green -> Color.Red)) - val sj = sjCodecOf[MapHolder[Color, Color]](JsonConfig.withEnumsAsIds(Nil)) - val js = sj.toJson(inst) - js should matchJson("""{"a":{"0":1,"2":0}}""") - sj.fromJson(js) shouldEqual(inst) + describe(colorString("--- Negative Tests ---")) { + it("Enum must break - bad value") { + val sj = sjCodecOf[MapHolder[Color, Color]] + val js = """{"a":{"Red":"Bogus","Green":"Red"}}""" + val ex = intercept[java.lang.IllegalArgumentException](sj.fromJson(js)) + ex.getMessage() shouldEqual ("enum co.blocke.scalajack.json.misc.Color has no case with name: Bogus") + } + it("Enum must break(using id) - bad value") { + val sj = sjCodecOf[MapHolder[Color, Color]](JsonConfig.withEnumsAsIds(Nil)) + val js = """{"a":{"0":1,"9":0}}""" + val ex = intercept[java.util.NoSuchElementException](sj.fromJson(js)) + ex.getMessage() shouldEqual ("enum co.blocke.scalajack.json.misc.Color has no case with ordinal: 9") + } } - it("Enumeration (Scale 2) as Map key and value must work") { - import Permissions.* - val inst = MapHolder[Permissions, Permissions](Map(Permissions.READ -> Permissions.WRITE, Permissions.EXEC -> Permissions.NONE)) - val sj = sjCodecOf[MapHolder[Permissions, Permissions]] - val js = sj.toJson(inst) - js should matchJson("""{"a":{"READ":"WRITE","EXEC":"NONE"}}""") - sj.fromJson(js) shouldEqual(inst) - } - it("Enumeration (Scale 2) as Map key and value must work (using id)") { - import Permissions.* - val inst = MapHolder[Permissions, Permissions](Map(Permissions.READ -> Permissions.WRITE, Permissions.EXEC -> Permissions.NONE)) - val sj = sjCodecOf[MapHolder[Permissions, Permissions]](JsonConfig.withEnumsAsIds(Nil)) - val js = sj.toJson(inst) - js should matchJson("""{"a":{"0":1,"2":3}}""") - sj.fromJson(js) shouldEqual(inst) - } - it("Enumeration (Scala 2) ordinal mutation") { - import SizeWithType._ - val inst = SampleEnum(Size.Small, Size.Medium, Size.Large, null, Size.Medium, Little) - val sj = sjCodecOf[SampleEnum] - val js = sj.toJson(inst) - js shouldEqual("""{"e1":"Small","e2":"Medium","e3":"Large","e4":null,"e5":"Medium","e6":"Little"}""") - // mutate e5 into an ordinal... - val js2 = js.replaceAll(""""e5":"Medium"""", """"e5":1""") - sj.fromJson(js2) shouldEqual(inst) - } - it("Java Enumeration as Map key and value must work") { - val inst = MapHolder[CarEnum, CarEnum](Map(CarEnum.VW -> CarEnum.PORSCHE, CarEnum.PORSCHE -> CarEnum.TOYOTA)) - val sj = sjCodecOf[MapHolder[CarEnum, CarEnum]] - val js = sj.toJson(inst) - js should matchJson("""{"a":{"VW":"PORSCHE","PORSCHE":"TOYOTA"}}""") - sj.fromJson(js) shouldEqual(inst) - } - it("Java Enumeration as Map key and value must work (using id)") { - val inst = MapHolder[CarEnum, CarEnum](Map(CarEnum.VW -> CarEnum.PORSCHE, CarEnum.PORSCHE -> CarEnum.TOYOTA)) - val sj = sjCodecOf[MapHolder[CarEnum, CarEnum]](JsonConfig.withEnumsAsIds(Nil)) - val js = sj.toJson(inst) - val targetJs = RType.of[CarEnum] match - case t: co.blocke.scala_reflection.rtypes.JavaEnumRType[?] => - val valMap = t.values.zipWithIndex.toMap - s"""{"a":{"${valMap("VW")}":${valMap("PORSCHE")},"${valMap("PORSCHE")}":${valMap("TOYOTA")}}}""" - js shouldEqual(targetJs) - sj.fromJson(js) shouldEqual(inst) - } - it("Enum/Enumeration mix of enum as value must work") { - import Permissions.* - val inst = MapHolder[Color, Permissions](Map(Color.Red -> Permissions.WRITE, Color.Blue -> Permissions.NONE)) - val sj = sjCodecOf[MapHolder[Color, Permissions]](JsonConfig.withEnumsAsIds(List("co.blocke.scalajack.json.misc.Color"))) - val js = sj.toJson(inst) - js should matchJson("""{"a":{"0":"WRITE","1":"NONE"}}""") - sj.fromJson(js) shouldEqual(inst) - } - it("Sealed trait enumeration w/case objects must work") { - val inst = FlavorHolder( Chocolate, null, Map(Bourbon->"a"), Map("a"->Vanilla)) - val sj = sjCodecOf[FlavorHolder] - val js = sj.toJson(inst) - js should matchJson("""{"f":"Chocolate","f2":null,"f3":{"Bourbon":"a"},"f4":{"a":"Vanilla"}}""") - sj.fromJson(js) shouldEqual(inst) - } - it("Sealed trait enumeration w/case class must work") { - val inst = VehicleHolder( Car(4, "Red"), null, Map("a"->Truck(18))) - val sj = sjCodecOf[VehicleHolder] - val js = sj.toJson(inst) - js should matchJson("""{"f":{"_hint":"Car","numberOfWheels":4,"color":"Red"},"f2":null,"f4":{"a":{"_hint":"Truck","numberOfWheels":18}}}""") - sj.fromJson(js) shouldEqual(inst) - } - it("Enum must break - bad value") { - val sj = sjCodecOf[MapHolder[Color, Color]] - val js = """{"a":{"Red":"Bogus","Green":"Red"}}""" - val ex = intercept[java.lang.IllegalArgumentException](sj.fromJson(js)) - ex.getMessage() shouldEqual("enum co.blocke.scalajack.json.misc.Color has no case with name: Bogus") - } - it("Enum must break(using id) - bad value") { - val sj = sjCodecOf[MapHolder[Color, Color]](JsonConfig.withEnumsAsIds(Nil)) - val js = """{"a":{"0":1,"9":0}}""" - val ex = intercept[java.util.NoSuchElementException](sj.fromJson(js)) - ex.getMessage() shouldEqual("enum co.blocke.scalajack.json.misc.Color has no case with ordinal: 9") - } - } \ No newline at end of file + } diff --git a/src/test/scala/co.blocke.scalajack/json/misc/LRSpec.scala b/src/test/scala/co.blocke.scalajack/json/misc/LRSpec.scala index cfb25498..f70a9c78 100644 --- a/src/test/scala/co.blocke.scalajack/json/misc/LRSpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/misc/LRSpec.scala @@ -131,3 +131,13 @@ class LRSpec() extends AnyFunSpec with JsonMatchers: sj.fromJson(js) shouldEqual (LRUnionHolder(List(null, "x"), ("y", null))) // Left's come back as null } } + + // describe(colorString("----------------------------------\n: Intersection Tests :\n----------------------------------", Console.YELLOW)) { + // it("LR (intersection) must work") { + // val inst = LRUnionHolder[Option[Int], String](List(Some(5), "x"), ("y", Some(10))) + // val sj = sjCodecOf[LRUnionHolder[Option[Int], String]] + // val js = sj.toJson(inst) + // js should matchJson("""{"a":[5,"x"],"b":["y",10]}""") + // sj.fromJson(js) shouldEqual inst + // } + // } diff --git a/src/test/scala/co.blocke.scalajack/json/misc/MiscSpec.scala b/src/test/scala/co.blocke.scalajack/json/misc/MiscSpec.scala index 1a007249..12d57f09 100644 --- a/src/test/scala/co.blocke.scalajack/json/misc/MiscSpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/misc/MiscSpec.scala @@ -25,7 +25,7 @@ on another level.""") js1 should equal("""{"a":"This is a \"strange\" test\non another level."}""") js2 should equal("""{"a":"This is a "strange" test on another level."}""") - sj1.fromJson(js1) shouldEqual(inst) + sj1.fromJson(js1) shouldEqual (inst) val msg = """Expected ',' or '}' but found 's' at position [17] |{"a":"This is a "strange" test~on another level."} @@ -38,7 +38,7 @@ on another level."}""") val sj = sjCodecOf[Validated] val js = sj.toJson(inst) js should equal("""{"name":"Mike","xspot":["x","y","z"],"nada":["","",""]}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("NeoType validation must work (test failure)") { val sj = sjCodecOf[Validated] @@ -65,7 +65,7 @@ on another level."}""") val sj = sjCodecOf[AnyHolder] val js = sj.toJson(inst) js should equal("""{"maybe":[1,2,3],"itried":{"a":-5},"itried2":99,"ifailed":null,"anymap":{"a":1,"b":2},"whichOneR":3,"whichOneL":"nope","bunch":["a",null,"b"]}""") - sj.fromJson(js) shouldEqual(AnyHolder(List(1, 2, 3),null,Map("a" -> -5),99,null,Map("a" -> 1, "b" -> 2),3,"nope",List("a", null, "b"))) + sj.fromJson(js) shouldEqual (AnyHolder(List(1, 2, 3), null, Map("a" -> -5), 99, null, Map("a" -> 1, "b" -> 2), 3, "nope", List("a", null, "b"))) } it("Any type must work (none as null)") { val inst = AnyHolder( @@ -87,6 +87,6 @@ on another level."}""") ) val js = sj.toJson(inst) js should equal("""{"maybe":[1,2,3],"maybeNot":null,"itried":{"a":-5},"itried2":99,"ifailed":"Try Failure with msg: oops","anymap":{"a":1,"b":2},"whichOneR":3,"whichOneL":"Left Error: nope","bunch":["a",null,"b"]}""") - sj.fromJson(js) shouldEqual(AnyHolder(List(1, 2, 3),null,Map("a" -> -5),99,"Try Failure with msg: oops",Map("a" -> 1, "b" -> 2),3,"Left Error: nope",List("a", null, "b"))) + sj.fromJson(js) shouldEqual (AnyHolder(List(1, 2, 3), null, Map("a" -> -5), 99, "Try Failure with msg: oops", Map("a" -> 1, "b" -> 2), 3, "Left Error: nope", List("a", null, "b"))) } } diff --git a/src/test/scala/co.blocke.scalajack/json/misc/Model.scala b/src/test/scala/co.blocke.scalajack/json/misc/Model.scala index e1c88ad4..043756ab 100644 --- a/src/test/scala/co.blocke.scalajack/json/misc/Model.scala +++ b/src/test/scala/co.blocke.scalajack/json/misc/Model.scala @@ -114,3 +114,24 @@ case class Truck(numberOfWheels: Int) extends Vehicle case class Car(numberOfWheels: Int, color: String) extends Vehicle case class Plane(numberOfEngines: Int) extends Vehicle case class VehicleHolder(f: Vehicle, f2: Vehicle, f4: Map[String, Vehicle]) + +/* + +TODO: FUTURE + +To implement Intersection types we must be able to support non-sealed traits. +To do that, we need a full runtime mirror of everything else in ScalaJack that +happens at compile-time (everything, basically). Not sure if/when that will +happen, so for now... no Intersection type support. + +trait Scissors: + val isSharp: Boolean + def cut: Unit +trait Needle: + val bobbins: Int + def sew: Unit +case class DressFixer(isSharp: Boolean, bobbins: Int) extends Scissors, Needle: + override def cut = println("Cut") + override def sew = println("Sew") +case class LRInterHolder[T, U](a: Seq[T | U], b: (T | U, T | U)) + */ diff --git a/src_old/main/java/co/blocke/scalajack/Change.java b/src_old/main/java/co/blocke/scalajack/Change.java deleted file mode 100644 index 5222fc66..00000000 --- a/src_old/main/java/co/blocke/scalajack/Change.java +++ /dev/null @@ -1,9 +0,0 @@ -package co.blocke.scalajack; - -import java.lang.annotation.*; - -@Target({ElementType.PARAMETER, ElementType.METHOD}) -@Retention(RetentionPolicy.RUNTIME) -public @interface Change { - String name(); -} diff --git a/src_old/main/java/co/blocke/scalajack/Collection.java b/src_old/main/java/co/blocke/scalajack/Collection.java deleted file mode 100644 index 65cdcdef..00000000 --- a/src_old/main/java/co/blocke/scalajack/Collection.java +++ /dev/null @@ -1,9 +0,0 @@ -package co.blocke.scalajack; - -import java.lang.annotation.*; - -@Target({ElementType.TYPE}) -@Retention(RetentionPolicy.RUNTIME) -public @interface Collection { - String name(); -} diff --git a/src_old/main/java/co/blocke/scalajack/DBKey.java b/src_old/main/java/co/blocke/scalajack/DBKey.java deleted file mode 100644 index 72377f60..00000000 --- a/src_old/main/java/co/blocke/scalajack/DBKey.java +++ /dev/null @@ -1,12 +0,0 @@ -package co.blocke.scalajack; - -import java.lang.annotation.*; - -@Inherited -@Target({ElementType.PARAMETER, ElementType.METHOD}) -@Retention(RetentionPolicy.RUNTIME) -public @interface DBKey { - // Some indexing schemes create unordered composite keys (eg Mongo) while others have primary/secondary distinctions (eg Dynamo). - // If the later, use the index parameter to "order" fields within a compound key. - int index() default 0; -} diff --git a/src_old/main/java/co/blocke/scalajack/Ignore.java b/src_old/main/java/co/blocke/scalajack/Ignore.java deleted file mode 100644 index ec479305..00000000 --- a/src_old/main/java/co/blocke/scalajack/Ignore.java +++ /dev/null @@ -1,13 +0,0 @@ -package co.blocke.scalajack; - -import java.lang.annotation.*; - -/** Annotation for Java getters or setters to tell reflector to ignore the decorated property for - * the purposes of reflection. - */ - -@Inherited -@Target({ElementType.METHOD, ElementType.FIELD}) -@Retention(RetentionPolicy.RUNTIME) -public @interface Ignore { -} \ No newline at end of file diff --git a/src_old/main/java/co/blocke/scalajack/JavaStuff.java b/src_old/main/java/co/blocke/scalajack/JavaStuff.java deleted file mode 100644 index d0111855..00000000 --- a/src_old/main/java/co/blocke/scalajack/JavaStuff.java +++ /dev/null @@ -1,11 +0,0 @@ -package co.blocke.scalajack; - -import java.math.*; - -public class JavaStuff { - - private BigInteger[][] multi; - - public BigInteger[][] getMulti() { return multi; } - public void setMulti(BigInteger[][] n) { multi = n; } -} \ No newline at end of file diff --git a/src_old/main/java/co/blocke/scalajack/Optional.java b/src_old/main/java/co/blocke/scalajack/Optional.java deleted file mode 100644 index e67af8c6..00000000 --- a/src_old/main/java/co/blocke/scalajack/Optional.java +++ /dev/null @@ -1,10 +0,0 @@ -package co.blocke.scalajack; - -import java.lang.annotation.*; - -@Inherited -@Target({ElementType.FIELD, ElementType.METHOD}) -@Retention(RetentionPolicy.RUNTIME) -public @interface Optional { -} - diff --git a/src_old/main/scala/co.blocke.scalajack/Converters.scala b/src_old/main/scala/co.blocke.scalajack/Converters.scala deleted file mode 100644 index 52a9cf4c..00000000 --- a/src_old/main/scala/co.blocke.scalajack/Converters.scala +++ /dev/null @@ -1,70 +0,0 @@ -package co.blocke.scalajack - -import co.blocke.scalajack.json.JsonFlavor -import model._ -import yaml.YamlFlavor -import delimited.DelimitedFlavor -import json4s.Json4sFlavor -import org.json4s.JValue -import json._ -import yaml._ -import delimited._ - -object Converters: - - //------ - // Note: Json, Json4s, and Yaml do NOT have a 'toDelimited' option because delimited format is *ordered*, while - // other 3 are intrinsically un-ordered, so there's no guarantee the delimited fields will be rendered in the - // 'correct' order. - //------ - - // JSON mappers - extension (j: JSON) - inline def mapJsonTo[T, S](toFlavor: JackFlavor[S])(fn: T => T)(implicit sjJ: JackFlavor[JSON]): S = toFlavor.render[T](fn(sjJ.read[T](j))) - - extension (j: JSON) - inline def jsonToYaml[T](implicit sjY: JackFlavor[YAML], sjJ: JackFlavor[JSON]): YAML = sjY.render( sjJ.read[T](j) ) - inline def jsonToJson4s[T](implicit sjV: JackFlavor[JValue], sjJ: JackFlavor[JSON]): JValue = sjV.render( sjJ.read[T](j) ) - inline def fromJson[T](implicit sjJ: JackFlavor[JSON]): T = sjJ.read[T](j) - inline def mapJson[T](fn: T => T)(implicit sjJ: JackFlavor[JSON]): JSON = sjJ.render[T](fn(sjJ.read[T](j))) - - - // YAML mappers - extension (y: YAML) - inline def mapYamlTo[T, S](toFlavor: JackFlavor[S])(fn: T => T)(implicit sjY: JackFlavor[YAML]): S = toFlavor.render[T](fn(sjY.read[T](y))) - - extension (y: YAML) - inline def yamlToJson[T](implicit sjJ: JackFlavor[JSON], sjY: JackFlavor[YAML]): JSON = sjJ.render( sjY.read[T](y) ) - inline def yamlToJson4s[T](implicit sjV: JackFlavor[JValue], sjY: JackFlavor[YAML]): JValue = sjV.render( sjY.read[T](y) ) - inline def fromYaml[T](implicit sjY: JackFlavor[YAML]): T = sjY.read[T](y) - inline def mapYaml[T](fn: T => T)(implicit sjY: JackFlavor[YAML]): YAML = sjY.render[T](fn(sjY.read[T](y))) - - - // DELIMITED mappers - extension (d: DELIMITED) - inline def mapDelimitedTo[T, S](toFlavor: JackFlavor[S])(fn: T => T)(implicit sjD: JackFlavor[DELIMITED]): S = toFlavor.render[T](fn(sjD.read[T](d))) - - extension (d: DELIMITED) - inline def delimitedToJson[T](implicit sjJ: JackFlavor[JSON], sjD: JackFlavor[DELIMITED]): JSON = sjJ.render( sjD.read[T](d) ) - inline def delimitedToJson4s[T](implicit sjV: JackFlavor[JValue], sjD: JackFlavor[DELIMITED]): JValue = sjV.render( sjD.read[T](d) ) - inline def delimitedToYaml[T](implicit sjY: JackFlavor[YAML], sjD: JackFlavor[DELIMITED]): YAML = sjY.render( sjD.read[T](d) ) - inline def fromDelimited[T](implicit sjD: JackFlavor[DELIMITED]): T = sjD.read[T](d) - inline def mapDelimited[T](fn: T => T)(implicit sjD: JackFlavor[DELIMITED]): DELIMITED = sjD.render[T](fn(sjD.read[T](d))) - - - // Json4s mappers - extension (j: JValue) - inline def mapJson4sTo[T, S](toFlavor: JackFlavor[S])(fn: T => T)(implicit sjV: JackFlavor[JValue]): S = toFlavor.render[T](fn(sjV.read[T](j))) - - extension (j: JValue) - inline def json4sToYaml[T](implicit sjY: JackFlavor[YAML], sjV: JackFlavor[JValue]): YAML = sjY.render( sjV.read[T](j) ) - inline def json4sToJson[T](implicit sjJ: JackFlavor[JSON], sjV: JackFlavor[JValue]): JSON = sjJ.render( sjV.read[T](j) ) - inline def fromJson4s[T](implicit sjV: JackFlavor[JValue]): T = sjV.read[T](j) - inline def mapJson4s[T](fn: T => T)(implicit sjV: JackFlavor[JValue]): JValue = sjV.render[T](fn(sjV.read[T](j))) - - - extension[T] (a: T) - inline def toJson(implicit sjJ: JackFlavor[JSON]): JSON = sjJ.render(a) - inline def toYaml(implicit sjY: JackFlavor[YAML]): YAML = sjY.render(a) - inline def toDelimited(implicit sjD: JackFlavor[DELIMITED]): DELIMITED = sjD.render(a) - inline def toJson4s(implicit sjV: JackFlavor[JValue]): JValue = sjV.render(a) diff --git a/src_old/main/scala/co.blocke.scalajack/Main.scalax b/src_old/main/scala/co.blocke.scalajack/Main.scalax deleted file mode 100644 index 33319130..00000000 --- a/src_old/main/scala/co.blocke.scalajack/Main.scalax +++ /dev/null @@ -1,37 +0,0 @@ -package co.blocke.scalajack - -import co.blocke.scala_reflection._ -import co.blocke.scala_reflection.info._ -import org.apache.commons.codec.binary.Base64 - -import scala.collection.mutable -import scala.jdk.CollectionConverters._ -import json._ -import yaml._ -import delimited._ -import model.JackFlavor -import co.blocke.scalajack.Converters._ - - -opaque type Foom = String - -object Main { - - def main(args: Array[String]): Unit = - - val f = "Foom".asInstanceOf[Foom] - if f == null then - println("Null!") - - - def constructors(clazz: Class[_]): String = - s"=== Constructors: ${clazz.getName} ===\n " + clazz.getConstructors.toList.mkString("\n ") - def methods(clazz: Class[_]): String = - s"=== Methods: ${clazz.getName} ===\n " + clazz.getMethods.toList.mkString("\n ") - def fields(clazz: Class[_]): String = - s"=== Fields: ${clazz.getName} ===\n " + clazz.getFields.toList.mkString("\n ") - def stack(clazz: Class[_]): String = - s"=== Superclass: ${clazz.getName} ===\n " + clazz.getSuperclass() + "\n" + - s"=== Interfaces: ${clazz.getName} ===\n " + clazz.getInterfaces.toList.mkString("\n ") - -} diff --git a/src_old/main/scala/co.blocke.scalajack/Performance.scalax b/src_old/main/scala/co.blocke.scalajack/Performance.scalax deleted file mode 100644 index dc894687..00000000 --- a/src_old/main/scala/co.blocke.scalajack/Performance.scalax +++ /dev/null @@ -1,106 +0,0 @@ -package co.blocke.scalajack - -import co.blocke.scala_reflection._ -import co.blocke.scala_reflection.info._ -import org.apache.commons.codec.binary.Base64 - -case class Person( - id: Int, - first_name: String, - last_name: String, - email: String, - gender: String, - ip_address: String) - - -/* -object Main { - - val people = - """[{"id":1,"first_name":"Kenneth","last_name":"Watson","email":"kwatson0@goo.ne.jp","gender":"Male","ip_address":"50.27.55.219"}, - {"id":2,"first_name":"Jason","last_name":"Peters","email":"jpeters1@tinypic.com","gender":"Male","ip_address":"152.156.120.235"}, - {"id":3,"first_name":"Beverly","last_name":"Stevens","email":"bstevens2@ustream.tv","gender":"Female","ip_address":"169.212.150.35"}, - {"id":4,"first_name":"Theresa","last_name":"Dixon","email":"tdixon3@hp.com","gender":"Female","ip_address":"137.214.192.32"}, - {"id":5,"first_name":"Michael","last_name":"Carr","email":"mcarr4@discovery.com","gender":"Male","ip_address":"244.152.168.54"}, - {"id":6,"first_name":"Carolyn","last_name":"Cruz","email":"ccruz5@nps.gov","gender":"Female","ip_address":"228.112.58.94"}, - {"id":7,"first_name":"Louis","last_name":"Alexander","email":"lalexander6@mapy.cz","gender":"Male","ip_address":"118.195.8.173"}, - {"id":8,"first_name":"Laura","last_name":"Campbell","email":"lcampbell7@google.ca","gender":"Female","ip_address":"125.91.1.1"}, - {"id":9,"first_name":"Judy","last_name":"Burke","email":"jburke8@furl.net","gender":"Female","ip_address":"153.45.26.242"}, - {"id":10,"first_name":"Earl","last_name":"Stevens","email":"estevens9@discovery.com","gender":"Male","ip_address":"172.161.173.238"}, - {"id":11,"first_name":"Rose","last_name":"Cooper","email":"rcoopera@lulu.com","gender":"Female","ip_address":"99.128.103.204"}, - {"id":12,"first_name":"Ashley","last_name":"Hawkins","email":"ahawkinsb@artisteer.com","gender":"Female","ip_address":"128.225.193.155"}, - {"id":13,"first_name":"Howard","last_name":"Harvey","email":"hharveyc@naver.com","gender":"Male","ip_address":"64.177.55.210"}, - {"id":14,"first_name":"Edward","last_name":"Ramos","email":"eramosd@is.gd","gender":"Male","ip_address":"208.65.154.100"}, - {"id":15,"first_name":"Jonathan","last_name":"Gonzalez","email":"jgonzaleze@walmart.com","gender":"Male","ip_address":"166.223.153.41"}, - {"id":16,"first_name":"Chris","last_name":"Reynolds","email":"creynoldsf@mail.ru","gender":"Male","ip_address":"183.239.230.178"}, - {"id":17,"first_name":"Helen","last_name":"Morales","email":"hmoralesg@vkontakte.ru","gender":"Female","ip_address":"19.89.226.60"}, - {"id":18,"first_name":"Tina","last_name":"Baker","email":"tbakerh@hubpages.com","gender":"Female","ip_address":"41.15.68.62"}, - {"id":19,"first_name":"Patricia","last_name":"Martin","email":"pmartini@booking.com","gender":"Female","ip_address":"98.67.244.69"}, - {"id":20,"first_name":"Rebecca","last_name":"Kelley","email":"rkelleyj@apple.com","gender":"Female","ip_address":"182.160.172.136"}, - {"id":21,"first_name":"Bonnie","last_name":"Carr","email":"bcarrk@jigsy.com","gender":"Female","ip_address":"73.181.196.21"}, - {"id":22,"first_name":"Harold","last_name":"Carter","email":"hcarterl@quantcast.com","gender":"Male","ip_address":"227.72.164.120"}, - {"id":23,"first_name":"Martha","last_name":"Barnes","email":"mbarnesm@skyrock.com","gender":"Female","ip_address":"46.162.4.230"}, - {"id":24,"first_name":"Martha","last_name":"Henderson","email":"mhendersonn@quantcast.com","gender":"Female","ip_address":"226.177.120.99"}, - {"id":25,"first_name":"Ashley","last_name":"Henderson","email":"ahendersono@buzzfeed.com","gender":"Female","ip_address":"159.212.195.202"}, - {"id":26,"first_name":"Sean","last_name":"Day","email":"sdayp@nationalgeographic.com","gender":"Male","ip_address":"32.29.74.112"}, - {"id":27,"first_name":"Mary","last_name":"Arnold","email":"marnoldq@sina.com.cn","gender":"Female","ip_address":"217.221.110.62"}, - {"id":28,"first_name":"Philip","last_name":"Pierce","email":"ppiercer@youtube.com","gender":"Male","ip_address":"170.222.96.245"}, - {"id":29,"first_name":"Johnny","last_name":"Gordon","email":"jgordons@themeforest.net","gender":"Male","ip_address":"229.207.75.169"}, - {"id":30,"first_name":"Julie","last_name":"Ruiz","email":"jruizt@jimdo.com","gender":"Female","ip_address":"209.193.34.42"}, - {"id":31,"first_name":"Benjamin","last_name":"Alvarez","email":"balvarezu@newsvine.com","gender":"Male","ip_address":"69.42.98.157"}, - {"id":32,"first_name":"Steve","last_name":"Marshall","email":"smarshallv@bizjournals.com","gender":"Male","ip_address":"135.55.106.6"}, - {"id":33,"first_name":"Aaron","last_name":"Diaz","email":"adiazw@friendfeed.com","gender":"Male","ip_address":"250.102.146.94"}, - {"id":34,"first_name":"Bonnie","last_name":"Fields","email":"bfieldsx@opera.com","gender":"Female","ip_address":"164.40.128.148"}, - {"id":35,"first_name":"Beverly","last_name":"Cunningham","email":"bcunninghamy@umn.edu","gender":"Female","ip_address":"4.128.182.77"}, - {"id":36,"first_name":"Juan","last_name":"Porter","email":"jporterz@nasa.gov","gender":"Male","ip_address":"171.157.112.131"}, - {"id":37,"first_name":"Donna","last_name":"Butler","email":"dbutler10@cdbaby.com","gender":"Female","ip_address":"126.95.247.209"}, - {"id":38,"first_name":"Richard","last_name":"Rivera","email":"rrivera11@irs.gov","gender":"Male","ip_address":"219.104.120.129"}, - {"id":39,"first_name":"Juan","last_name":"Hall","email":"jhall12@ftc.gov","gender":"Male","ip_address":"157.211.238.243"}, - {"id":40,"first_name":"Heather","last_name":"Lee","email":"hlee13@dailymail.co.uk","gender":"Female","ip_address":"10.153.241.206"}, - {"id":41,"first_name":"Rose","last_name":"Kennedy","email":"rkennedy14@bravesites.com","gender":"Female","ip_address":"200.54.196.76"}, - {"id":42,"first_name":"Russell","last_name":"Warren","email":"rwarren15@livejournal.com","gender":"Male","ip_address":"169.251.130.191"}, - {"id":43,"first_name":"Dennis","last_name":"Howell","email":"dhowell16@biglobe.ne.jp","gender":"Male","ip_address":"222.19.174.168"}, - {"id":44,"first_name":"Kimberly","last_name":"Wilson","email":"kwilson17@networksolutions.com","gender":"Female","ip_address":"13.139.193.159"}, - {"id":45,"first_name":"Sharon","last_name":"Jacobs","email":"sjacobs18@stanford.edu","gender":"Female","ip_address":"130.22.68.55"}, - {"id":46,"first_name":"Donald","last_name":"Nguyen","email":"dnguyen19@posterous.com","gender":"Male","ip_address":"0.115.100.139"}, - {"id":47,"first_name":"Brenda","last_name":"Stone","email":"bstone1a@senate.gov","gender":"Female","ip_address":"165.196.166.161"}, - {"id":48,"first_name":"Kelly","last_name":"Pierce","email":"kpierce1b@xrea.com","gender":"Female","ip_address":"73.180.74.227"}, - {"id":49,"first_name":"Sandra","last_name":"Murray","email":"smurray1c@princeton.edu","gender":"Female","ip_address":"211.149.35.132"}, - {"id":50,"first_name":"Alice","last_name":"Davis","email":"adavis1d@ow.ly","gender":"Female","ip_address":"4.124.35.181"}]""".stripMargin.asInstanceOf[co.blocke.scalajack.json.JSON] - - - val dj = ScalaJack() - - def main(args: Array[String]): Unit = { - - println(RType.of[SampleJNumber]) - - /* - - for(i <- 0 to 100) - dj.read[List[Person]](people) - - val n = 100000 - for (j <- 0 to 30) - println(s"Run $n iterations in ${run(n)} ms") - */ - - } - - def run(n:Int): Long = - val now = System.currentTimeMillis - for(i <- 0 to n) - dj.read[List[Person]](people) - val later = System.currentTimeMillis - later - now - - - -} -*/ - -// From reflection: -// ScalaCaseClassInfo(xxx.Thing,class xxx.Thing,List(),List(ScalaFieldInfo(0,t,ArrayInfo([[I,class [[I,ArrayInfo([I,class [I,Scala_Int)),Map(),public int[][] co.blocke.scala_reflection.Thing.t(),None)),List(),Map(),false) - -// From scalajack: -// ScalaCaseClassInfo(xxx.Thing,class xxx.Thing,List(),List(ScalaFieldInfo(0,t,ArrayInfo([[I,class scala.Array,ArrayInfo([I,class scala.Array,Scala_Int)),Map(),public int[][] co.blocke.scalajack.Thing.t(),None)),List(),Map(),false) diff --git a/src_old/main/scala/co.blocke.scalajack/SJCapture.scala b/src_old/main/scala/co.blocke.scalajack/SJCapture.scala deleted file mode 100644 index 963c241b..00000000 --- a/src_old/main/scala/co.blocke.scalajack/SJCapture.scala +++ /dev/null @@ -1,9 +0,0 @@ -package co.blocke.scalajack - -trait SJCapture { - var captured: java.util.HashMap[String,_] = new java.util.HashMap[String, Any]() - // var captured: java.util.HashMap[String,String] = new java.util.HashMap[String, String]() -} - -// Java classes should inherit this! -class SJCaptureJava extends SJCapture diff --git a/src_old/main/scala/co.blocke.scalajack/ScalaJack.scala b/src_old/main/scala/co.blocke.scalajack/ScalaJack.scala deleted file mode 100644 index ebcf3464..00000000 --- a/src_old/main/scala/co.blocke.scalajack/ScalaJack.scala +++ /dev/null @@ -1,20 +0,0 @@ -package co.blocke.scalajack - -import model._ -import json._ - -object ScalaJack: - def apply() = JsonFlavor() - def apply[S](kind: JackFlavor[S]): JackFlavor[S] = kind - - -class ScalaJackError(msg: String) extends Exception(msg) -class ScalaJackValueError(val value: Any, cause: Throwable) extends Exception(cause.getMessage) - -type HintBijective = util.BijectiveFunction[String, String] -val CHANGE_ANNO = "co.blocke.scalajack.Change" -val OPTIONAL_ANNO = "co.blocke.scalajack.Optional" -val IGNORE = "co.blocke.scalajack.Ignore" -val DB_KEY = "co.blocke.scalajack.DBKey" -val DB_COLLECTION = "co.blocke.scalajack.Collection" -val SJ_CAPTURE = "co.blocke.scalajack.SJCapture" \ No newline at end of file diff --git a/src_old/main/scala/co.blocke.scalajack/delimited/DelimitedEitherTypeAdapterFactory.scala b/src_old/main/scala/co.blocke.scalajack/delimited/DelimitedEitherTypeAdapterFactory.scala deleted file mode 100644 index 58312096..00000000 --- a/src_old/main/scala/co.blocke.scalajack/delimited/DelimitedEitherTypeAdapterFactory.scala +++ /dev/null @@ -1,46 +0,0 @@ -package co.blocke.scalajack -package delimited - -import model._ -import typeadapter.{EitherTypeAdapter, MaybeStringWrapTypeAdapter, StringWrapTypeAdapter} -import co.blocke.scala_reflection._ -import co.blocke.scala_reflection.info.EitherInfo -import co.blocke.scala_reflection.impl.Clazzes._ - -import scala.collection.mutable.Builder -import scala.util.{ Failure, Success, Try } - -/** - * The only reason this machinery exists for delimited either is that writes need to be string-wrapped, where they don't in the general/JSON case. - */ -object DelimitedEitherTypeAdapterFactory extends TypeAdapterFactory: - - def matches(concrete: RType): Boolean = - concrete match { - case _: EitherInfo => true - case _ => false - } - - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[_] = - val eitherInfo = concrete.asInstanceOf[EitherInfo] - val leftInfo = eitherInfo.leftType - val rightInfo = eitherInfo.rightType - - if( leftInfo.infoClass <:< rightInfo.infoClass || rightInfo.infoClass <:< leftInfo.infoClass) - throw new IllegalArgumentException( - s"Types ${leftInfo.name} and ${rightInfo.name} are not mutually exclusive" - ) - - val leftTypeAdapter = taCache.typeAdapterOf(leftInfo) match { - case ta if ta.isStringish => ta - case ta => MaybeStringWrapTypeAdapter(taCache.jackFlavor, ta) - } - val rightTypeAdapter = taCache.typeAdapterOf(rightInfo) match { - case ta if ta.isStringish => ta - case ta => MaybeStringWrapTypeAdapter(taCache.jackFlavor, ta) - } - - EitherTypeAdapter( - concrete, - leftTypeAdapter, - rightTypeAdapter) \ No newline at end of file diff --git a/src_old/main/scala/co.blocke.scalajack/delimited/DelimitedFlavor.scala b/src_old/main/scala/co.blocke.scalajack/delimited/DelimitedFlavor.scala deleted file mode 100644 index 9939d88f..00000000 --- a/src_old/main/scala/co.blocke.scalajack/delimited/DelimitedFlavor.scala +++ /dev/null @@ -1,72 +0,0 @@ -package co.blocke.scalajack -package delimited - -import model._ -import co.blocke.scala_reflection.RType -import typeadapter.{MaybeStringWrapTypeAdapter, StringWrapTypeAdapter} - -opaque type DELIMITED = String -val DELIM_PREFIX: Char = 2 - -case class DelimitedFlavor( - delimiter: Char = ',', - override val defaultHint: String = "_hint", - override val permissivesOk: Boolean = false, - override val customAdapters: List[TypeAdapterFactory] = List.empty[TypeAdapterFactory], - override val hintMap: Map[String, String] = Map.empty[String, String], - override val hintValueModifiers: Map[String, HintValueModifier] = Map.empty[String, HintValueModifier], - override val typeValueModifier: HintValueModifier = DefaultHintModifier, - override val parseOrElseMap: Map[Class[_], RType] = Map.empty[Class[_], RType], - override val enumsAsInt: Boolean = false -) extends JackFlavor[DELIMITED] { - - def _read[T](input: DELIMITED, typeAdapter: TypeAdapter[T]): T = - val parser = DelimitedParser(delimiter, s"$DELIM_PREFIX$input".asInstanceOf[DELIMITED], this) - typeAdapter.read(parser) - - def _render[T](t: T, typeAdapter: TypeAdapter[T]): DELIMITED = - val sb = StringBuilder[DELIMITED]() - typeAdapter.write(t, writer, sb) - sb.result().asInstanceOf[DELIMITED] - - def parse(input: DELIMITED): Parser = DelimitedParser(delimiter, input, this) - - private val writer = DelimitedWriter(delimiter) - - def allowPermissivePrimitives(): JackFlavor[DELIMITED] = - throw new ScalaJackError("Not available for delimited encoding") - def enumsAsInts(): JackFlavor[DELIMITED] = this.copy(enumsAsInt = true) - def parseOrElse(poe: (RType, RType)*): JackFlavor[DELIMITED] = - this.copy(parseOrElseMap = this.parseOrElseMap ++ poe.map{(p,oe) => p.infoClass->oe}) - def withAdapters(ta: TypeAdapterFactory*): JackFlavor[DELIMITED] = - throw new ScalaJackError("Not available for delimited encoding") - def withDefaultHint(hint: String): JackFlavor[DELIMITED] = - throw new ScalaJackError("Not available for delimited encoding") - def withHints(h: (RType, String)*): JackFlavor[DELIMITED] = - throw new ScalaJackError("Not available for delimited encoding") - def withHintModifiers(hm: (RType, HintValueModifier)*): JackFlavor[DELIMITED] = - throw new ScalaJackError("Not available for delimited encoding") - def withTypeValueModifier(tm: HintValueModifier): JackFlavor[DELIMITED] = - throw new ScalaJackError("Not available for delimited encoding") - - def stringWrapTypeAdapterFactory[T]( - wrappedTypeAdapter: TypeAdapter[T], - emptyStringOk: Boolean = true - ): TypeAdapter[T] = - StringWrapTypeAdapter(wrappedTypeAdapter) - // wrappedTypeAdapter // No-Op for delimited - - override def bakeCache(): TypeAdapterCache = { - val dads = super.bakeCache() - dads.copy( - factories = DelimitedEitherTypeAdapterFactory :: - DelimitedOptionTypeAdapterFactory :: dads.factories - ) - } - - def maybeStringWrapTypeAdapterFactory[T]( - wrappedTypeAdapter: TypeAdapter[T], - emptyStringOk: Boolean = true - ): TypeAdapter[T] = - MaybeStringWrapTypeAdapter(this, wrappedTypeAdapter, emptyStringOk) -} \ No newline at end of file diff --git a/src_old/main/scala/co.blocke.scalajack/delimited/DelimitedOptionTypeAdapterFactory.scala b/src_old/main/scala/co.blocke.scalajack/delimited/DelimitedOptionTypeAdapterFactory.scala deleted file mode 100644 index 87d3d30a..00000000 --- a/src_old/main/scala/co.blocke.scalajack/delimited/DelimitedOptionTypeAdapterFactory.scala +++ /dev/null @@ -1,31 +0,0 @@ -package co.blocke.scalajack -package delimited - -import typeadapter.{OptionTypeAdapter, JavaOptionalTypeAdapter} -import co.blocke.scala_reflection.RType -import co.blocke.scala_reflection.info.{OptionInfo, ScalaOptionInfo, JavaOptionalInfo, TypeSymbolInfo} - -import model._ - -/** - * Options are handled a little differently for Delimited. They should result in an empty field. - * Empty fields area always read in as None, so no null fields are possible for Delimited options. - */ -object DelimitedOptionTypeAdapterFactory extends TypeAdapterFactory: - - def matches(concrete: RType): Boolean = - concrete match { - case _: OptionInfo => true - case _ => false - } - - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[_] = - val optiBase = concrete.asInstanceOf[OptionInfo] - val wrapped = optiBase.optionParamType match { - case c: TypeSymbolInfo => throw new ScalaJackError(s"Unexpected non-concrete type in option: ${c.name}") - case c => taCache.typeAdapterOf(c) - } - concrete match { - case opti: ScalaOptionInfo => OptionTypeAdapter(concrete, wrapped, true) // Note nullAsNone = true here! - case jopti: JavaOptionalInfo => JavaOptionalTypeAdapter(concrete, wrapped, true) - } diff --git a/src_old/main/scala/co.blocke.scalajack/delimited/DelimitedParser.scala b/src_old/main/scala/co.blocke.scalajack/delimited/DelimitedParser.scala deleted file mode 100644 index 3b423360..00000000 --- a/src_old/main/scala/co.blocke.scalajack/delimited/DelimitedParser.scala +++ /dev/null @@ -1,221 +0,0 @@ -package co.blocke.scalajack -package delimited - -import model._ -import scala.collection.mutable -import typeadapter.classes.ClassTypeAdapterBase -import co.blocke.scala_reflection.info.TypeMemberInfo - -case class DelimitedParser( - delimChar: Char, - input: DELIMITED, - jackFlavor: JackFlavor[DELIMITED]) - extends Parser { - type WIRE = DELIMITED - - private var pos: Int = 0 - private val delimPrefixString = DELIM_PREFIX.toString - - val (tokens, indexes) = { - val dChars: Array[Char] = input.asInstanceOf[String].toCharArray - var i = 0 - val maxChars: Int = dChars.length - val tokenList = scala.collection.mutable.ListBuffer.empty[String] - val indexList = scala.collection.mutable.ListBuffer.empty[Int] - while (i < maxChars) { - var inQuotes = false - var done = false - val acc = new java.lang.StringBuilder() - indexList += i - while (i < maxChars && !done) { - dChars(i) match { - case DELIM_PREFIX => - acc.append(delimPrefixString) - done = true - case this.delimChar if !inQuotes => - done = true - case '"' if !inQuotes && i + 1 < maxChars && dChars(i + 1) != '"' => - inQuotes = true - i += 1 - case '"' if inQuotes && (i + 1 == maxChars || dChars(i + 1) != '"') => - inQuotes = false - i += 1 - done = true - case '"' if i + 1 < maxChars && dChars(i + 1) == '"' => - acc.append(dChars(i)) - i += 2 - case _ => // do nothing - acc.append(dChars(i)) - i += 1 - } - } - tokenList += acc.toString - if (i < maxChars) i += 1 // skip delimiter - } - if (i > 0 && dChars(i - 1) == delimChar) { - tokenList += "" - indexList += i - } - (tokenList.toList, indexList.toList) - } - - val max: Int = tokens.size - - @inline def isNumberChar(char: Char): Boolean = - ('0' <= char && char <= '9') || char == '.' || char == 'e' || char == 'E' || char == '-' || char == '+' - - def expectString(nullOK: Boolean = true): String = - if (pos < max) { - val ret = tokens(pos) - pos += 1 - ret - } else throw new ScalaJackError(showError("Attempt to read beyond input")) - - def expectList[K, TO]( - KtypeAdapter: TypeAdapter[K], - builder: mutable.Builder[K, TO]): TO = - expectString() match { - case "" => null.asInstanceOf[TO] - case listStr => - val subParser = DelimitedParser(delimChar, listStr.asInstanceOf[DELIMITED], jackFlavor) - while (subParser.pos < subParser.max) builder += KtypeAdapter.read( - subParser - ) - builder.result() - } - - def expectNumber(nullsOK: Boolean = false): String = - expectString() match { - // $COVERAGE-OFF$Never called--nulls caught in CollectionTypeAdapter before coming here. Left here as a safety - case "" if nullsOK => null - case "" => - backspace() - throw new ScalaJackError(showError("Expected a Number here")) - // $COVERAGE-ON$ - case candidate => - candidate.toCharArray.find(c => !isNumberChar(c)) match { - case None => candidate - case Some(_) => - backspace() - throw new ScalaJackError(showError("Expected a Number here")) - } - } - - def expectBoolean(): Boolean = - expectString() match { - case "true" => true - case "false" => false - case _ => - backspace() - throw new ScalaJackError(showError("Expected a Boolean here")) - } - - def expectTuple( - tupleFieldTypeAdapters: List[TypeAdapter[_]] - ): List[Object] = - expectString() match { - // $COVERAGE-OFF$Can't test--can't validly tell whether "" is escaped '"' or empty value in this context. Blame CSV - case "" => null.asInstanceOf[List[Object]] - // $COVERAGE-ON$ - case listStr => - val subParser = DelimitedParser(delimChar, listStr.asInstanceOf[DELIMITED], jackFlavor) - tupleFieldTypeAdapters.map { _ match { - case ccta: Classish => ccta.read( DelimitedParser(delimChar, subParser.expectString().asInstanceOf[DELIMITED], jackFlavor)) - case ta => ta.read(subParser) - }}.asInstanceOf[List[Object]] - } - - def expectMap[K, V, TO]( - keyTypeAdapter: TypeAdapter[K], - valueTypeAdapter: TypeAdapter[V], - builder: mutable.Builder[(K, V), TO]): TO = - throw new ScalaJackError(showError("No Map support for delimited data.")) // No map support in delimited format - - def expectObject( - classBase: ClassTypeAdapterBase[_], - hintLabel: String - ): (mutable.BitSet, List[Object], java.util.HashMap[String, _]) = { - if (!classBase.isCaseClass) - throw new ScalaJackError( - showError( - "Only case classes with non-empty constructors are supported for delimited data." - ) - ) - val fieldBits = mutable.BitSet() - - val args = classBase.argsTemplate.clone() - classBase.orderedFieldNames.foreach { name => - val oneField = classBase.fieldMembersByName(name) - val valueRead = oneField.valueTypeAdapter match { - case ccta: typeadapter.classes.CaseClassTypeAdapter[_] => - expectString() match { - case "" => null - case listStr => - val subParser = DelimitedParser(delimChar, listStr.asInstanceOf[DELIMITED], jackFlavor) - ccta.read(subParser) - } - case ta => ta.read(this) - } - valueRead match { - case None => - case null if oneField.info.defaultValue.isDefined => - case _ => - args(oneField.info.index) = valueRead.asInstanceOf[Object] - fieldBits += oneField.info.index - } - } - (fieldBits, args.toList, new java.util.HashMap[String, String]()) - } - - def showError(msg: String): String = { - val inputStr = input.asInstanceOf[String].drop(1) // Account for prefix char on input - val (clip, dashes) = indexes(pos) - 1 match { - case ep if ep <= 50 && max < 80 => (inputStr, ep) - case ep if ep <= 50 => (inputStr.substring(0, 77) + "...", ep) - case ep if ep > 50 && ep + 30 >= max => - ("..." + inputStr.substring(indexes(pos) - 50), 52) - case ep => ("..." + inputStr.substring(ep - 49, ep + 27) + "...", 52) - } - msg + "\n" + clip.replaceAll("[\n\t]", "~") + "\n" + ("-" * dashes) + "^" - } - - def peekForNull: Boolean = { - val isNull = pos == max || tokens(pos) == "" || (tokens(pos) == delimPrefixString && pos == max - 1) - if (pos < max && isNull || pos < max && tokens(pos) == delimPrefixString) - pos += 1 - isNull - } - - // $COVERAGE-OFF$No meaning for delimited input - def resolveTypeMembers( - typeMembersByName: Map[String, TypeMemberInfo], - converterFn: HintBijective - ): Map[String, TypeMemberInfo] = - throw new ScalaJackError( - showError("DelimitedFlavor does not support classes with type members") - ) - - def scanForHint(hint: String, converterFn: HintBijective): Class[_] = - throw new ScalaJackError( - showError("DelimitedFlavor does not support traits") - ) - - def nextIsObject: Boolean = false - def nextIsArray: Boolean = false - def nextIsBoolean: Boolean = false - def sourceAsString: String = input.asInstanceOf[String] - // $COVERAGE-ON$ - - def backspace(): Unit = pos -= 1 - def mark(): Int = pos - def revertToMark(mark: Int): Unit = pos = mark - def nextIsString: Boolean = true - def nextIsNumber: Boolean = { - val save = pos - val isValidNumber = expectString().toCharArray.forall(c => isNumberChar(c)) - pos = save - isValidNumber - } - def subParser(input: DELIMITED): Parser = - DelimitedParser(delimChar, input, jackFlavor) -} \ No newline at end of file diff --git a/src_old/main/scala/co.blocke.scalajack/delimited/DelimitedWriter.scala b/src_old/main/scala/co.blocke.scalajack/delimited/DelimitedWriter.scala deleted file mode 100644 index fe3513ae..00000000 --- a/src_old/main/scala/co.blocke.scalajack/delimited/DelimitedWriter.scala +++ /dev/null @@ -1,109 +0,0 @@ -package co.blocke.scalajack -package delimited - -import model._ -import co.blocke.scalajack.model - -import scala.collection.Map -import scala.collection.mutable - -case class DelimitedWriter(delimiter: Char) extends Writer[DELIMITED] { - - def writeArray[Elem](t: Iterable[Elem], elemTypeAdapter: TypeAdapter[Elem], out: mutable.Builder[DELIMITED, DELIMITED]): Unit = - if (t != null) { - val sb = model.StringBuilder[DELIMITED]() - val iter = t.iterator - while (iter.hasNext) { - elemTypeAdapter.write(iter.next, this, sb) - if (iter.hasNext) - sb += delimiter.toString.asInstanceOf[DELIMITED] - } - writeString(sb.result().asInstanceOf[String], out) - } - - def writeBigInt(t: BigInt, out: mutable.Builder[DELIMITED, DELIMITED]): Unit = - out += t.toString.asInstanceOf[DELIMITED] - def writeBoolean(t: Boolean, out: mutable.Builder[DELIMITED, DELIMITED]): Unit = - out += t.toString.asInstanceOf[DELIMITED] - def writeDecimal(t: BigDecimal, out: mutable.Builder[DELIMITED, DELIMITED]): Unit = - out += t.toString.asInstanceOf[DELIMITED] - def writeDouble(t: Double, out: mutable.Builder[DELIMITED, DELIMITED]): Unit = - out += t.toString.asInstanceOf[DELIMITED] - def writeInt(t: Int, out: mutable.Builder[DELIMITED, DELIMITED]): Unit = - out += t.toString.asInstanceOf[DELIMITED] - def writeLong(t: Long, out: mutable.Builder[DELIMITED, DELIMITED]): Unit = - out += t.toString.asInstanceOf[DELIMITED] - - def writeMap[Key, Value, To](t: Map[Key, Value], keyTypeAdapter: TypeAdapter[Key], valueTypeAdapter: TypeAdapter[Value], out: mutable.Builder[DELIMITED, DELIMITED]): Unit = - throw new ScalaJackError( - "Map-typed data is not supported for delimited output" - ) - - def writeNull(out: mutable.Builder[DELIMITED, DELIMITED]): Unit = {} // write empty field - - def writeObject[T]( - t: T, - orderedFieldNames: List[String], - fieldMembersByName: Map[String, ClassFieldMember[_,_]], - out: mutable.Builder[DELIMITED, DELIMITED], - extras: List[(String, ExtraFieldValue[_])] = List.empty[(String, ExtraFieldValue[_])] - ): Unit = - if (t != null) { - var first = true - orderedFieldNames.foreach { name => - if (first) - first = false - else - out += delimiter.toString.asInstanceOf[DELIMITED] - val f = fieldMembersByName(name) - f.valueTypeAdapter match { - case ta if ta.isInstanceOf[Classish] => - val sb = model.StringBuilder[DELIMITED]() - ta.castAndWrite(f.info.valueOf(t), this, sb) - writeString(sb.result().asInstanceOf[String], out) - case ta => - ta.castAndWrite(f.info.valueOf(t), this, out) - } - } - } - - def writeString(t: String, out: mutable.Builder[DELIMITED, DELIMITED]): Unit = - if (t != null) { - val t0 = t.replaceAll("\"", "\"\"") - val toWrite = - if (t0 != t || t0.contains(delimiter)) - "\"" + t0 + "\"" - else - t - out += toWrite.asInstanceOf[DELIMITED] - } - - // $COVERAGE-OFF$Never called for delimited output - def writeRaw(t: DELIMITED, out: mutable.Builder[DELIMITED, DELIMITED]): Unit = - writeString(t.asInstanceOf[String], out) - // $COVERAGE-ON$ - - def writeTuple[T]( - t: T, - writeFn: (Product) => List[(TypeAdapter[_], Any)], - out: mutable.Builder[DELIMITED, DELIMITED] - ): Unit = { - var first = true - val sb = model.StringBuilder[DELIMITED]() - writeFn(t.asInstanceOf[Product]).foreach { (fieldTA, fieldValue) => - if (first) - first = false - else - sb += delimiter.toString.asInstanceOf[DELIMITED] - fieldTA match { - case cta: Classish => - val sb2 = model.StringBuilder[DELIMITED]() - fieldTA.castAndWrite(fieldValue, this, sb2) - writeString(sb2.result().asInstanceOf[String], sb) - case ta => - fieldTA.castAndWrite(fieldValue, this, sb) - } - } - writeString(sb.result().asInstanceOf[String], out) - } -} diff --git a/src_old/main/scala/co.blocke.scalajack/json/JsonFlavor.scala b/src_old/main/scala/co.blocke.scalajack/json/JsonFlavor.scala deleted file mode 100644 index c98d3d43..00000000 --- a/src_old/main/scala/co.blocke.scalajack/json/JsonFlavor.scala +++ /dev/null @@ -1,63 +0,0 @@ -package co.blocke.scalajack -package json - -import model._ -import co.blocke.scala_reflection.RType -import typeadapter.{MaybeStringWrapTypeAdapter, StringWrapTypeAdapter} - -opaque type JSON = String - -case class JsonFlavor( - override val defaultHint: String = "_hint", - override val permissivesOk: Boolean = false, - override val customAdapters: List[TypeAdapterFactory] = List.empty[TypeAdapterFactory], - override val hintMap: Map[String, String] = Map.empty[String, String], - override val hintValueModifiers: Map[String, HintValueModifier] = Map.empty[String, HintValueModifier], - override val typeValueModifier: HintValueModifier = DefaultHintModifier, - override val parseOrElseMap: Map[Class[_], RType] = Map.empty[Class[_], RType], - override val enumsAsInt: Boolean = false -) extends JackFlavor[JSON] { - - def _read[T](input: JSON, typeAdapter: TypeAdapter[T]): T = - val parser = JsonParser(input, this) - typeAdapter.read(parser).asInstanceOf[T] - - def _render[T](t: T, typeAdapter: TypeAdapter[T]): JSON = - val sb = StringBuilder[JSON]() - typeAdapter.write(t, writer, sb) - sb.result() - - def parse(input: JSON): Parser = JsonParser(input, this) - - private val writer = JsonWriter() - - override val stringifyMapKeys: Boolean = true - - def allowPermissivePrimitives(): JackFlavor[JSON] = - this.copy(permissivesOk = true) - def enumsAsInts(): JackFlavor[JSON] = this.copy(enumsAsInt = true) - def parseOrElse(poe: (RType, RType)*): JackFlavor[JSON] = - this.copy(parseOrElseMap = this.parseOrElseMap ++ poe.map{(p,oe) => p.infoClass->oe}) - def withAdapters(ta: TypeAdapterFactory*): JackFlavor[JSON] = - this.copy(customAdapters = this.customAdapters ++ ta.toList) - def withDefaultHint(hint: String): JackFlavor[JSON] = - this.copy(defaultHint = hint) - def withHints(h: (RType, String)*): JackFlavor[JSON] = - this.copy(hintMap = this.hintMap ++ h.map{(rt,hint) => rt.name->hint}) - def withHintModifiers(hm: (RType, HintValueModifier)*): JackFlavor[JSON] = - this.copy(hintValueModifiers = this.hintValueModifiers ++ hm.map{(rt,hintM) => rt.name->hintM}) - def withTypeValueModifier(tm: HintValueModifier): JackFlavor[JSON] = - this.copy(typeValueModifier = tm) - - def stringWrapTypeAdapterFactory[T]( - wrappedTypeAdapter: TypeAdapter[T], - emptyStringOk: Boolean = true - ): TypeAdapter[T] = - StringWrapTypeAdapter(wrappedTypeAdapter, emptyStringOk) - - def maybeStringWrapTypeAdapterFactory[T]( - wrappedTypeAdapter: TypeAdapter[T], - emptyStringOk: Boolean = true - ): TypeAdapter[T] = - MaybeStringWrapTypeAdapter(this, wrappedTypeAdapter, emptyStringOk) -} diff --git a/src_old/main/scala/co.blocke.scalajack/json/JsonParser.scala b/src_old/main/scala/co.blocke.scalajack/json/JsonParser.scala deleted file mode 100644 index 6ade936e..00000000 --- a/src_old/main/scala/co.blocke.scalajack/json/JsonParser.scala +++ /dev/null @@ -1,428 +0,0 @@ -package co.blocke.scalajack -package json - -import model._ -import typeadapter.classes.ClassTypeAdapterBase -import co.blocke.scala_reflection._ -import co.blocke.scala_reflection.info.TypeMemberInfo - -import scala.collection.mutable -import scala.jdk.CollectionConverters._ - -case class JsonParser(jsRaw: JSON, jackFlavor: JackFlavor[JSON]) extends Parser { - - type WIRE = JSON - - private val js = jsRaw.asInstanceOf[String] - - private val jsChars: Array[Char] = js.toCharArray - private var i = 0 - private val max: Int = jsChars.length - - @inline def whitespace(): Unit = - while (i < max && jsChars(i).isWhitespace) i += 1 - @inline def nullCheck(): Boolean = - jsChars(i) == 'n' && i + 4 <= max && js.substring(i, i + 4) == "null" - - def backspace(): Unit = i -= 1 - - def expectString(nullOK: Boolean = true): String = { - if (nullOK && nullCheck()) { - i += 4 - null - } else if (jsChars(i) == '"') { - i += 1 - val mark = i - var captured: Option[String] = None - while (i < max && jsChars(i) != '"') { - if (jsChars(i) == '\\') { // Oops! Special char found. Reset and try again while capturing/translating special chars - i = mark - captured = Some(_expectString()) - } else - i += 1 - } - i += 1 - captured.getOrElse(js.substring(mark, i - 1)) - } else - throw new ScalaJackError(showError("Expected a String here")) - } - - // Slower "capture" version for when we discover embedded special chars that need translating, i.e. we - // can't do a simple substring within quotes. - private def _expectString(): String = { - val builder = new java.lang.StringBuilder() - while (i < max && jsChars(i) != '"') { - if (jsChars(i) == '\\') { - jsChars(i + 1) match { - case '"' => - builder.append('\"') - i += 2 - - case '\\' => - builder.append('\\') - i += 2 - - case 'b' => - builder.append('\b') - i += 2 - - case 'f' => - builder.append('\f') - i += 2 - - case 'n' => - builder.append('\n') - i += 2 - - case 'r' => - builder.append('\r') - i += 2 - - case 't' => - builder.append('\t') - i += 2 - - case 'u' => - val hexEncoded = js.substring(i + 2, i + 6) - val unicodeChar = Integer.parseInt(hexEncoded, 16).toChar - builder.append(unicodeChar.toString) - i += 6 - - case c => - builder.append(c) - i += 2 - } - } else { - builder.append(jsChars(i)) - i += 1 - } - } - builder.toString - } - - def expectBoolean(): Boolean = - if (i + 4 <= max && js.substring(i, i + 4) == "true") { - i += 4 - true - } else if (i + 5 <= max && js.substring(i, i + 5) == "false") { - i += 5 - false - } else - throw new ScalaJackError(showError("Expected a Boolean here")) - - @inline def isNumberChar(char: Char): Boolean = - ('0' <= char && char <= '9') || char == '.' || char == 'e' || char == 'E' || char == '-' || char == '+' - - def expectNumber(nullOK: Boolean = false): String = { - val mark = i - nullCheck() match { - case true if nullOK => - i += 4 - null - case true => - throw new ScalaJackError(showError("Expected a Number here")) - case false => - while (i < max && isNumberChar(jsChars(i))) i += 1 - if (mark == i) - throw new ScalaJackError(showError("Expected a Number here")) - else if (i == max || "\t\n ,}]".contains(jsChars(i))) - js.substring(mark, i) - else - throw new ScalaJackError(showError("Expected a Number here")) - } - } - - def expectList[E, TO](elemTypeAdapter: TypeAdapter[E], builder: mutable.Builder[E, TO]): TO = { - if (jsChars(i) != '[') - throw new ScalaJackError(showError("Expected start of list here")) - i += 1 - var first = true - while (i < max && jsChars(i) != ']') { - whitespace() - if (!first) { - if (jsChars(i) != ',') - throw new ScalaJackError(showError("Expected comma here")) - else - i += 1 // skip ',' - whitespace() - } else - first = false - builder += elemTypeAdapter.read(this) // Parse next item! - whitespace() - } - if (i == max || jsChars(i) != ']') - throw new ScalaJackError(showError("Expected end of list here")) - i += 1 - builder.result() - } - - def expectTuple( - tupleFieldTypeAdapters: List[TypeAdapter[_]] - ): List[Object] = { - if (i == max || jsChars(i) != '[') - throw new ScalaJackError(showError("Expected start of tuple here")) - i += 1 - var first = true - val result = tupleFieldTypeAdapters.map { fieldTypeAdapter => - whitespace() - if (!first) { - if (i == max || jsChars(i) != ',') - throw new ScalaJackError(showError("Expected comma here")) - else - i += 1 // skip ',' - whitespace() - } else - first = false - fieldTypeAdapter.read(this) - } - if (i == max || jsChars(i) != ']') - throw new ScalaJackError(showError("Expected end of tuple here")) - i += 1 - result.asInstanceOf[List[Object]] - } - - def expectMap[K, V, TO](keyTypeAdapter: TypeAdapter[K], valueTypeAdapter: TypeAdapter[V], builder: mutable.Builder[(K, V), TO]): TO = { - whitespace() - if (jsChars(i) != '{') - throw new ScalaJackError(showError("Expected start of object here")) - i += 1 - var first = true - while (i < max && jsChars(i) != '}') { - whitespace() - if (!first) { - if (i == max || jsChars(i) != ',') - throw new ScalaJackError(showError("Expected comma here")) - else - i += 1 // skip ',' - whitespace() - } else - first = false - val key = keyTypeAdapter.read(this) - if (key == null) - throw new ScalaJackError(showError("Map keys cannot be null")) - whitespace() - if (i == max || jsChars(i) != ':') - throw new ScalaJackError(showError("Expected colon here")) - i += 1 - whitespace() - val value = valueTypeAdapter.read(this) - whitespace() - builder += ((key, value)) - } - if (i == max || jsChars(i) != '}') - throw new ScalaJackError(showError("Expected end of object here")) - i += 1 - builder.result() - } - - def expectObject( - classBase: ClassTypeAdapterBase[_], - hintLabel: String - ): (mutable.BitSet, List[Object], java.util.HashMap[String, String]) = { - whitespace() - val args = classBase.argsTemplate.clone() - val fieldBits = mutable.BitSet() - val captured = - if (classBase.isSJCapture) - new java.util.HashMap[String, String]() - else - null - if (i == max || jsChars(i) != '{') - throw new ScalaJackError(showError("Expected start of object here")) - i += 1 - var first = true - while (i < max && jsChars(i) != '}') { - whitespace() - if (!first) { - if (i == max || jsChars(i) != ',') - throw new ScalaJackError(showError("Expected comma here")) - else - i += 1 // skip ',' - whitespace() - } else - first = false - val key = expectString(false) - whitespace() - if (i == max || jsChars(i) != ':') - throw new ScalaJackError(showError("Expected colon here")) - i += 1 - classBase.fieldMembersByName.get(key) match { - case Some(field) => - whitespace() - fieldBits += field.info.index - args(field.info.index) = field.valueTypeAdapter.read(this).asInstanceOf[Object] - case None => // found some input field not present in class - val mark = i - skipOverElement() - if (classBase.isSJCapture && key != hintLabel) - captured.put(key, js.substring(mark, i)) - } - whitespace() - } - if (i == max || jsChars(i) != '}') - throw new ScalaJackError(showError("Expected end of object here")) - i += 1 - (fieldBits, args.toList, captured) - } - - private def skipString(): Unit = { - i += 1 - while (i < max && jsChars(i) != '"') { - if (jsChars(i) == '\\') i += 1 - i += 1 - } - i += 1 - } - - private def skipOverElement(): Unit = { - whitespace() - jsChars(i) match { - case '[' => - var level = 0 - i += 1 - while (i < max && level >= 0) { - jsChars(i) match { - case '[' => - level += 1 - i += 1 - case '"' => skipString() - case ']' => - level -= 1 - i += 1 - case _ => - i += 1 - } - } - case '{' => - var level = 0 - i += 1 - while (i < max && level >= 0) jsChars(i) match { - case '{' => - level += 1 - i += 1 - case '"' => skipString() - case '}' => - level -= 1 - i += 1 - case _ => - i += 1 - } - case '"' => skipString() - case _ => // "naked" value: null, number, boolean - while (i < max && jsChars(i) != ',' && jsChars(i) != '}' && jsChars(i) != ']') i += 1 - } - } - - def peekForNull: Boolean = - if (nullCheck()) { - i += 4 - true - } else false - - // NOTE: Expectation here is we're sitting on beginning of object, '{'. This is called from TraitTypeAdapter - def scanForHint(hint: String, converterFn: HintBijective): Class[_] = { - val mark = i - whitespace() - if (i == max || jsChars(i) != '{') - throw new ScalaJackError(showError("Expected start of object here")) - i += 1 // skip over { - var done = false - while (!done) { - whitespace() - val key = expectString() - whitespace() - if (i == max || jsChars(i) != ':') - throw new ScalaJackError(showError("Expected ':' here")) - i += 1 // skip ':' - if (key == hint) { - whitespace() - done = true - } else { - skipOverElement() - whitespace() - jsChars(i) match { - case ',' => i += 1 // skip ',' - case '}' => - throw new ScalaJackError(showError(s"Type hint '$hint' not found")) - case _ => - throw new ScalaJackError(showError("Unexpected character found")) - } - } - } - val rawHintString = expectString() - val hintType = try { - converterFn.apply(rawHintString) - } catch { - case t: Throwable => - i -= 1 - throw new ScalaJackError( - showError(s"Couldn't marshal class for $rawHintString") - ) - } - i = mark // we found hint, but go back to parse object - Class.forName(hintType) - } - - def resolveTypeMembers( - typeMembersByName: Map[String, TypeMemberInfo], - converterFn: HintBijective - ): Map[String, TypeMemberInfo] = { - val mark = i - whitespace() - if (i == max || jsChars(i) != '{') - throw new ScalaJackError(showError("Expected start of object here")) - val collected = new java.util.HashMap[String, TypeMemberInfo]() - i += 1 // skip over { - var done = false - while (!done) { - whitespace() - val key = expectString() - whitespace() - if (i == max || jsChars(i) != ':') - throw new ScalaJackError(showError("Expected ':' here")) - i += 1 // skip ':' - if (typeMembersByName.contains(key)) { - whitespace() - collected.put( - key, - TypeMemberInfo(key, typeMembersByName(key).typeSymbol, RType.of(Class.forName(converterFn.apply(expectString())))) - ) - } else - skipOverElement() - whitespace() - jsChars(i) match { - case ',' => i += 1 // skip ',' - case '}' => done = true - case _ => - throw new ScalaJackError(showError("Unexpected character found")) - } - } - i = mark // go back to parse object - collected.asScala.toMap - } - - def showError(msg: String): String = { - val (clip, dashes) = i match { - case ep if ep <= 50 && max < 80 => (js, ep) - case ep if ep <= 50 => (js.substring(0, 77) + "...", ep) - case ep if ep > 50 && ep + 30 >= max => - ("..." + js.substring(i - 49), 52) - case ep => ("..." + js.substring(ep - 49, ep + 27) + "...", 52) - } - msg + "\n" + clip.replaceAll("[\n\t]", "~") + "\n" + ("-" * dashes) + "^" - } - - def mark(): Int = i - def revertToMark(mark: Int): Unit = i = mark - - def nextIsString: Boolean = nullCheck() || jsChars(i) == '"' - def nextIsNumber: Boolean = isNumberChar(jsChars(i)) - def nextIsObject: Boolean = nullCheck() || jsChars(i) == '{' - def nextIsArray: Boolean = nullCheck() || jsChars(i) == '[' - def nextIsBoolean: Boolean = - (i + 4 <= max && js.substring(i, i + 4) == "true") || (i + 5 <= max && js - .substring(i, i + 5) == "false") - def sourceAsString: String = js - - def subParser(input: JSON): Parser = JsonParser(input, jackFlavor) -} diff --git a/src_old/main/scala/co.blocke.scalajack/json/JsonWriter.scala b/src_old/main/scala/co.blocke.scalajack/json/JsonWriter.scala deleted file mode 100644 index 6629d719..00000000 --- a/src_old/main/scala/co.blocke.scalajack/json/JsonWriter.scala +++ /dev/null @@ -1,188 +0,0 @@ -package co.blocke.scalajack -package json - -import model._ -import scala.collection.mutable -import scala.collection.Map - -case class JsonWriter() extends Writer[JSON] { - - @inline def addString(s: String, out: mutable.Builder[JSON, JSON]): Unit = - out += s.asInstanceOf[JSON] - - def writeArray[Elem](t: Iterable[Elem], elemTypeAdapter: TypeAdapter[Elem], out: mutable.Builder[JSON, JSON]): Unit = t match { - case null => addString("null", out) - case a => - out += "[".asInstanceOf[JSON] - val iter = a.iterator - while (iter.hasNext) { - elemTypeAdapter.write(iter.next, this, out) - if (iter.hasNext) - out += ",".asInstanceOf[JSON] - } - out += "]".asInstanceOf[JSON] - } - - def writeBigInt(t: BigInt, out: mutable.Builder[JSON, JSON]): Unit = - addString(t.toString, out) - - def writeBoolean(t: Boolean, out: mutable.Builder[JSON, JSON]): Unit = - addString(t.toString, out) - - def writeDecimal(t: BigDecimal, out: mutable.Builder[JSON, JSON]): Unit = - t match { - case null => addString("null", out) - case s => addString(s.toString, out) - } - - def writeDouble(t: Double, out: mutable.Builder[JSON, JSON]): Unit = - addString(t.toString, out) - - def writeInt(t: Int, out: mutable.Builder[JSON, JSON]): Unit = - addString(t.toString, out) - - def writeLong(t: Long, out: mutable.Builder[JSON, JSON]): Unit = - addString(t.toString, out) - - def writeMap[Key, Value, To](t: Map[Key, Value], keyTypeAdapter: TypeAdapter[Key], valueTypeAdapter: TypeAdapter[Value], out: mutable.Builder[JSON, JSON]): Unit = - t match { - case null => addString("null", out) - case daMap => - out += "{".asInstanceOf[JSON] - var first = true - daMap.foreach { - case (key, value) => - if (first) - first = false - else - out += ",".asInstanceOf[JSON] - if (key == null) - throw new ScalaJackError("Map keys cannot be null.") - keyTypeAdapter.write(key, this, out) - out += ":".asInstanceOf[JSON] - valueTypeAdapter.write(value, this, out) - } - out += "}".asInstanceOf[JSON] - } - - def writeString(t: String, out: mutable.Builder[JSON, JSON]): Unit = - t match { - case null => addString("null", out) - case _: String => - out += "\"".asInstanceOf[JSON] - var i = 0 - val length = t.length - val chars = t.toCharArray - - while (i < length) { - chars(i) match { - case '"' => addString("""\"""", out) - case '\\' => addString("""\\""", out) - case '\b' => addString("""\b""", out) - case '\f' => addString("""\f""", out) - case '\n' => addString("""\n""", out) - case '\r' => addString("""\r""", out) - case '\t' => addString("""\t""", out) - case ch if ch < 32 || ch >= 128 => - addString("""\""" + "u" + "%04x".format(ch.toInt), out) - case c => out += c.toString.asInstanceOf[JSON] - } - - i += 1 - } - out += "\"".asInstanceOf[JSON] - } - - def writeRaw(t: JSON, out: mutable.Builder[JSON, JSON]): Unit = - addString(t.asInstanceOf[String], out) - - def writeNull(out: mutable.Builder[JSON, JSON]): Unit = - addString("null", out) - - @inline private def writeFields( - isFirst: Boolean, - fields: List[(String, Any, TypeAdapter[Any])], - out: mutable.Builder[JSON, JSON] - ): Boolean = { - var first = isFirst - for ((label, value, valueTypeAdapter) <- fields) - value match { - case None => // do nothing (skip this field) - case o: java.util.Optional[_] if !o.isPresent => // do nothing (skip this field) - case _ => - if (first) - first = false - else - out += ",".asInstanceOf[JSON] - writeString(label, out) - out += ":".asInstanceOf[JSON] - valueTypeAdapter.write(value, this, out) - } - first - } - - def writeObject[T]( - t: T, - orderedFieldNames: List[String], - fieldMembersByName: Map[String, ClassFieldMember[_,_]], - out: mutable.Builder[JSON, JSON], - extras: List[(String, ExtraFieldValue[_])] - ): Unit = { - if (t == null) { - addString("null", out) - } else { - out += "{".asInstanceOf[JSON] - val wasFirst = writeFields( - isFirst = true, - extras.map{ - e => - ( - e._1, - e._2.value, - e._2.valueTypeAdapter.asInstanceOf[TypeAdapter[Any]] - ) - }, - out - ) - val wasFirst2 = writeFields( - wasFirst, - orderedFieldNames - .map { fieldName => // Strictly-speaking JSON has no order, but it's clean to write out in constructor order. - val oneField = fieldMembersByName(fieldName) - (fieldName, oneField.info.valueOf(t), oneField.valueTypeAdapter.asInstanceOf[TypeAdapter[Any]]) - }, - out - ) - t match { - case sjc: SJCapture => - import scala.jdk.CollectionConverters._ - var first = wasFirst2 - sjc.captured.asScala.foreach { - case (field, fvalue) => - if (first) - first = false - else - out += ",".asInstanceOf[JSON] - writeString(field, out) - out += ":".asInstanceOf[JSON] - out += fvalue.asInstanceOf[JSON] // all json captured things are String - } - case _ => - } - out += "}".asInstanceOf[JSON] - } - } - - def writeTuple[T](t: T, writeFn: (Product) => List[(TypeAdapter[_], Any)], out: mutable.Builder[JSON, JSON]): Unit = { - out += "[".asInstanceOf[JSON] - var first = true - writeFn(t.asInstanceOf[Product]).foreach { (fieldTA, fieldValue) => - if (first) - first = false - else - out += ",".asInstanceOf[JSON] - fieldTA.castAndWrite( fieldValue, this, out ) - } - out += "]".asInstanceOf[JSON] - } -} \ No newline at end of file diff --git a/src_old/main/scala/co.blocke.scalajack/json4s/JValueBuilder.scala b/src_old/main/scala/co.blocke.scalajack/json4s/JValueBuilder.scala deleted file mode 100644 index fcc701a1..00000000 --- a/src_old/main/scala/co.blocke.scalajack/json4s/JValueBuilder.scala +++ /dev/null @@ -1,22 +0,0 @@ -package co.blocke.scalajack -package json4s - -import org.json4s.JValue - -import scala.collection.mutable - -case class JValueBuilder() extends mutable.Builder[JValue, JValue] { - private var internalValue: Option[JValue] = None - - def addOne(elem: JValue): this.type = { - internalValue = Some(elem) - this - } - - def clear(): Unit = internalValue = None - - def result(): JValue = - internalValue.getOrElse( - throw new ScalaJackError("No value set for internal json4s builder") - ) -} diff --git a/src_old/main/scala/co.blocke.scalajack/json4s/Json4sFlavor.scala b/src_old/main/scala/co.blocke.scalajack/json4s/Json4sFlavor.scala deleted file mode 100644 index f56f47d3..00000000 --- a/src_old/main/scala/co.blocke.scalajack/json4s/Json4sFlavor.scala +++ /dev/null @@ -1,68 +0,0 @@ -package co.blocke.scalajack -package json4s - -import model._ -import typeadapter.MaybeStringWrapTypeAdapter -import org.json4s._ - -import scala.collection.mutable -import co.blocke.scala_reflection.RType - - -case class Json4sFlavor( - override val defaultHint: String = "_hint", - override val permissivesOk: Boolean = false, - override val customAdapters: List[TypeAdapterFactory] = List.empty[TypeAdapterFactory], - override val hintMap: Map[String, String] = Map.empty[String, String], - override val hintValueModifiers: Map[String, HintValueModifier] = Map.empty[String, HintValueModifier], - override val typeValueModifier: HintValueModifier = DefaultHintModifier, - override val parseOrElseMap: Map[Class[_], RType] = Map.empty[Class[_], RType], - override val enumsAsInt: Boolean = false -) extends JackFlavor[JValue] { - - def _read[T](input: JValue, typeAdapter: TypeAdapter[T]): T = - val parser = Json4sParser(input, this) - typeAdapter.read(parser).asInstanceOf[T] - - def _render[T](t: T, typeAdapter: TypeAdapter[T]): JValue = - val sb = json4s.JValueBuilder() - typeAdapter.write(t, writer, sb) - sb.result() - - def parse(input: JValue): Parser = Json4sParser(input, this) - - private val writer = Json4sWriter() - - override val stringifyMapKeys: Boolean = true - - def allowPermissivePrimitives(): JackFlavor[JValue] = - throw new ScalaJackError("Permissive primitives not supported for Json4s") - def enumsAsInts(): JackFlavor[JValue] = this.copy(enumsAsInt = true) - def parseOrElse(poe: (RType, RType)*): JackFlavor[JValue] = - this.copy(parseOrElseMap = this.parseOrElseMap ++ poe.map{(p,oe) => p.infoClass->oe}) - def withAdapters(ta: TypeAdapterFactory*): JackFlavor[JValue] = - this.copy(customAdapters = this.customAdapters ++ ta.toList) - def withDefaultHint(hint: String): JackFlavor[JValue] = - this.copy(defaultHint = hint) - def withHints(h: (RType, String)*): JackFlavor[JValue] = - this.copy(hintMap = this.hintMap ++ h.map{(rt,hint) => rt.name->hint}) - def withHintModifiers(hm: (RType, HintValueModifier)*): JackFlavor[JValue] = - this.copy(hintValueModifiers = this.hintValueModifiers ++ hm.map{(rt,hintM) => rt.name->hintM}) - def withTypeValueModifier(tm: HintValueModifier): JackFlavor[JValue] = - this.copy(typeValueModifier = tm) - - def stringWrapTypeAdapterFactory[T]( - wrappedTypeAdapter: TypeAdapter[T], - emptyStringOk: Boolean = true - ): TypeAdapter[T] = - StringWrapTypeAdapter(wrappedTypeAdapter) - - override def getBuilder: mutable.Builder[JValue, JValue] = - json4s.JValueBuilder() - - def maybeStringWrapTypeAdapterFactory[T]( - wrappedTypeAdapter: TypeAdapter[T], - emptyStringOk: Boolean = true - ): TypeAdapter[T] = - MaybeStringWrapTypeAdapter(this, wrappedTypeAdapter, emptyStringOk) -} diff --git a/src_old/main/scala/co.blocke.scalajack/json4s/Json4sParser.scala b/src_old/main/scala/co.blocke.scalajack/json4s/Json4sParser.scala deleted file mode 100644 index 80a87574..00000000 --- a/src_old/main/scala/co.blocke.scalajack/json4s/Json4sParser.scala +++ /dev/null @@ -1,201 +0,0 @@ -package co.blocke.scalajack -package json4s - -import org.json4s._ -import model._ -import typeadapter.classes.ClassTypeAdapterBase -import scala.collection.mutable -import co.blocke.scala_reflection.RType -import co.blocke.scala_reflection.info.TypeMemberInfo - - -case class Json4sParser(input: JValue, jackFlavor: JackFlavor[JValue]) - extends Parser { - type WIRE = JValue - - def expectString(nullOK: Boolean = true): String = - input match { - case null | JNull => null - case JString(s) => s - case x => - throw new ScalaJackError(s"Expected string here, not '$x'") - } - - def expectList[K, TO]( - KtypeAdapter: TypeAdapter[K], - builder: mutable.Builder[K, TO]): TO = - input match { - // $COVERAGE-OFF$Null caught by TypeAdapter but this left here as a safety - case null | JNull => null.asInstanceOf[TO] - // $COVERAGE-ON$ - case JArray(arr) => - arr.foreach( - a => builder += KtypeAdapter.read(subParser(a)).asInstanceOf[K] - ) - builder.result() - case x => - throw new ScalaJackError(s"Expected list here, not '$x'") - } - - def expectTuple( - tupleFieldTypeAdapters: List[TypeAdapter[_]] - ): List[Object] = - input match { - // $COVERAGE-OFF$Null caught by TypeAdapter but this left here as a safety - case null | JNull => null - // $COVERAGE-ON$ - case JArray(arr) => - tupleFieldTypeAdapters.zip(arr).map { (fieldTypeAdapter, v) => - fieldTypeAdapter.read(subParser(v)).asInstanceOf[Object] - } - case x => - throw new ScalaJackError(s"Expected tuple (list) here, not '$x'") - } - - def expectMap[K, V, TO]( - keyTypeAdapter: TypeAdapter[K], - valueTypeAdapter: TypeAdapter[V], - builder: mutable.Builder[(K, V), TO]): TO = - input match { - // $COVERAGE-OFF$Null caught by TypeAdapter but this left here as a safety - case null | JNull => null.asInstanceOf[TO] - // $COVERAGE-ON$ - case JObject(obj) => - obj.foreach { - case (key, objVal) => - val mapKey = keyTypeAdapter.read(subParser(JString(key))) - val mapValue = valueTypeAdapter.read(subParser(objVal)) - val newElem: (K, V) = (mapKey, mapValue) - builder += newElem - } - builder.result - case x => - throw new ScalaJackError(s"Expected map here, not '$x'") - } - - def expectObject( - classBase: ClassTypeAdapterBase[_], - hintLabel: String - ): (mutable.BitSet, List[Object], java.util.HashMap[String, _]) = - input match { - case JObject(obj) => - val args = classBase.argsTemplate.clone() - val fieldBits = mutable.BitSet() - val captured = - if classBase.isSJCapture then - new java.util.HashMap[String, Any]() - else - null - obj.foreach { - case (key, objVal) => - classBase.fieldMembersByName - .get(key) - .map { field => - fieldBits += field.info.index - args(field.info.index) = - field.valueTypeAdapter.read(subParser(objVal)).asInstanceOf[Object] - } - .getOrElse { - if (captured != null) - captured.put(key, objVal) - } - } - (fieldBits, args.toList, captured) - case x => - throw new ScalaJackError(s"Expected object here, not '$x'") - } - - def expectBoolean(): Boolean = - input match { - case JBool(b) => b - case x => - throw new ScalaJackError(s"Expected boolean here, not '$x'") - } - - def expectNumber(nullOK: Boolean = false): String = - input match { - case null | JNull if nullOK => null - case null | JNull => - throw new ScalaJackError(s"Expected number here, not '$input'") - case JDecimal(num) => num.toString - case JDouble(num) => num.toString - case JInt(num) => num.toString - case JLong(num) => num.toString - case _ => throw new ScalaJackError(s"Expected number here, not '$input'") - } - - def peekForNull: Boolean = input match { - case null | JNull => true - case _ => false - } - - def scanForHint(hint: String, converterFn: HintBijective): Class[_] = - input match { - case JObject(obj) => - obj - .find(_._1 == hint) - .map { - case (label, hintValue) => - hintValue match { - case s: JString => - try { - Class.forName(converterFn.apply(s.s)) - } catch { - case t: Throwable => - throw new ScalaJackError( - s"Couldn't marshal class for ${s.s}" - ) - } - case _ => - throw new ScalaJackError( - s"Hint value $hint must be a string value" - ) - } - } - .getOrElse(throw new ScalaJackError(s"Type hint '$hint' not found")) - case x => - throw new ScalaJackError(s"Expected object here, not '$x'") - } - - // For embedded type members. Convert the type member into runtime "actual" type, e.g. T --> Foo - def resolveTypeMembers( - typeMembersByName: Map[String, TypeMemberInfo], - converterFn: HintBijective - ): Map[String, TypeMemberInfo] = // Returns Map[Type Signature Type (e.g. 'T'), Type] - input match { - case JObject(obj) => - val collected = obj.collect { - case (key, oneValue) if typeMembersByName.contains(key) && oneValue.isInstanceOf[JString] => - ( - key, - TypeMemberInfo(key, typeMembersByName(key).typeSymbol, RType.of(Class.forName(converterFn.apply(oneValue.asInstanceOf[JString].s)))) - ) - } - collected.toMap - case x => - throw new ScalaJackError(s"Expected object here, not '$x'") - } - - // $COVERAGE-OFF$Not needed for Json4s - def showError(msg: String): String = msg - def backspace(): Unit = {} - def mark(): Int = 0 - def revertToMark(mark: Int): Unit = {} - // $COVERAGE-ON$ - - def nextIsString: Boolean = input.isInstanceOf[JString] - def nextIsNumber: Boolean = - input match { - case JDecimal(_) => true - case JDouble(_) => true - case JInt(_) => true - case JLong(_) => true - case _ => false - } - def nextIsObject: Boolean = input.isInstanceOf[JObject] - def nextIsArray: Boolean = input.isInstanceOf[JArray] - def nextIsBoolean: Boolean = input.isInstanceOf[JBool] - - def subParser(input: JValue): Parser = Json4sParser(input, jackFlavor) - def sourceAsString: String = input.toString -} diff --git a/src_old/main/scala/co.blocke.scalajack/json4s/Json4sWriter.scala b/src_old/main/scala/co.blocke.scalajack/json4s/Json4sWriter.scala deleted file mode 100644 index ae1b2d44..00000000 --- a/src_old/main/scala/co.blocke.scalajack/json4s/Json4sWriter.scala +++ /dev/null @@ -1,138 +0,0 @@ -package co.blocke.scalajack -package json4s - -import model._ -import model.Writer - -import scala.collection.{Map, mutable} -import org.json4s._ - -case class Json4sWriter() extends Writer[JValue] { - - def writeArray[Elem](t: Iterable[Elem], elemTypeAdapter: TypeAdapter[Elem], out: collection.mutable.Builder[JValue, JValue]): Unit = - t match { - case null => out += JNull - case a => - var arr = JArray(List.empty[JValue]) - val outBuf = JValueBuilder() - a.iterator.foreach { item => - outBuf.clear() - elemTypeAdapter.write(item, this, outBuf) - arr = JArray(arr.arr :+ outBuf.result) - } - out += arr - } - - def writeRaw(t: JValue, out: mutable.Builder[JValue, JValue]): Unit = - out += t - - def writeBigInt(t: BigInt, out: collection.mutable.Builder[JValue, JValue]): Unit = - out += JInt(t) - - def writeBoolean(t: Boolean, out: collection.mutable.Builder[JValue, JValue]): Unit = - out += JBool(t) - - def writeDecimal(t: BigDecimal, out: collection.mutable.Builder[JValue, JValue]): Unit = - t match { - case null => out += JNull - case d => out += JDecimal(d) - } - - def writeDouble(t: Double, out: collection.mutable.Builder[JValue, JValue]): Unit = - out += JDouble(t) - - def writeInt(t: Int, out: collection.mutable.Builder[JValue, JValue]): Unit = - out += JInt(t) - - def writeLong(t: Long, out: collection.mutable.Builder[JValue, JValue]): Unit = - out += JLong(t) - - def writeMap[Key, Value, To](t: Map[Key, Value], keyTypeAdapter: TypeAdapter[Key], valueTypeAdapter: TypeAdapter[Value], out: mutable.Builder[JValue, JValue]): Unit = - t match { - case null => out += JNull - case daMap => - val outBuf = JValueBuilder() - val outMap = daMap.map { - case (key, value) => - if (key == null) - throw new ScalaJackError("Map keys cannot be null.") - outBuf.clear() - keyTypeAdapter.write(key.asInstanceOf[Key], this, outBuf) - val k = outBuf.result().values.toString - outBuf.clear() - valueTypeAdapter.write(value, this, outBuf) - k -> outBuf.result - }.toList - out += JObject(outMap) - } - - def writeString(t: String, out: collection.mutable.Builder[JValue, JValue]): Unit = - t match { - case null => out += JNull - case _: String => out += JString(t) - } - - def writeNull(out: collection.mutable.Builder[JValue, JValue]): Unit = - out += JNull - - @inline private def writeFields( - fields: List[(String, Any, TypeAdapter[Any])] - ): Map[String, JValue] = { - val outBuf = JValueBuilder() - fields.collect { - case (label, value, valueTypeAdapter) if value != None => - outBuf.clear() - valueTypeAdapter.write(value, this, outBuf) - label -> outBuf.result() - }.toMap - } - - def writeObject[T]( - t: T, - orderedFieldNames: List[String], - fieldMembersByName: Map[String, ClassFieldMember[_,_]], - out: mutable.Builder[JValue, JValue], - extras: List[(String, ExtraFieldValue[_])] = List.empty[(String, ExtraFieldValue[_])] - ): Unit = - t match { - case null => out += JNull - case _ => - val extraFields = writeFields( - extras.map( - e => - ( - e._1, - e._2.value, - e._2.valueTypeAdapter.asInstanceOf[TypeAdapter[Any]] - ) - ) - ) - val classFields = writeFields(orderedFieldNames.map { orn => - val oneField = fieldMembersByName(orn) - (orn, oneField.info.valueOf(t), oneField.valueTypeAdapter.asInstanceOf[TypeAdapter[Any]]) - }) - val captureFields = t match { - case sjc: SJCapture => - import scala.jdk.CollectionConverters._ - sjc.captured.asScala.asInstanceOf[Map[String, JValue]] - case _ => Map.empty[String, JValue] - } - - out += JObject((extraFields ++ classFields ++ captureFields).toList) - } - - def writeTuple[T]( - t: T, - writeFn: (Product) => List[(TypeAdapter[_], Any)], - out: mutable.Builder[JValue, JValue] - ): Unit = { - var arr = JArray(List.empty[JValue]) - val outBuf = JValueBuilder() - writeFn(t.asInstanceOf[Product]).foreach { (fieldTA, fieldValue) => - outBuf.clear() - fieldTA.castAndWrite(fieldValue, this, outBuf) - arr = JArray(arr.arr :+ outBuf.result) - } - out += arr - } -} diff --git a/src_old/main/scala/co.blocke.scalajack/json4s/StringWrapTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/json4s/StringWrapTypeAdapter.scala deleted file mode 100644 index de99213c..00000000 --- a/src_old/main/scala/co.blocke.scalajack/json4s/StringWrapTypeAdapter.scala +++ /dev/null @@ -1,75 +0,0 @@ -package co.blocke.scalajack -package json4s - -import typeadapter.AnyTypeAdapter -import model._ -import org.json4s._ -import co.blocke.scala_reflection.RType -import co.blocke.scala_reflection.impl.PrimitiveType - -import scala.collection.mutable - -// A TypeAdapter for a type T, which is wrapped in a String, a.k.a. "stringified". -// This is used for JSON Map keys, which must be strings. -case class StringWrapTypeAdapter[T](wrappedTypeAdapter: TypeAdapter[T]) - extends TypeAdapter[T] { - - override def isStringish: Boolean = true - val info: RType = wrappedTypeAdapter.info - - def read(parser: Parser): T = { - // 1. Read String (JValue --> String) - val wrappedValueString = parser.expectString() - - wrappedTypeAdapter match { - case value: ScalarTypeAdapter[_] => - value.info match { - case PrimitiveType.Scala_Byte => wrappedValueString.toByte.asInstanceOf[T] - case PrimitiveType.Scala_Int => wrappedValueString.toInt.asInstanceOf[T] - case PrimitiveType.Scala_Long => wrappedValueString.toLong.asInstanceOf[T] - case PrimitiveType.Scala_Double => wrappedValueString.toDouble.asInstanceOf[T] - case PrimitiveType.Scala_Float => wrappedValueString.toFloat.asInstanceOf[T] - case PrimitiveType.Scala_Short => wrappedValueString.toShort.asInstanceOf[T] - case PrimitiveType.Scala_Boolean => wrappedValueString.toBoolean.asInstanceOf[T] - case r: RType if r.name == "scala.math.BigInt" => BigInt(wrappedValueString).asInstanceOf[T] - case r: RType if r.name == "scala.math.BigDecimal" => BigDecimal(wrappedValueString).asInstanceOf[T] - // $COVERAGE-OFF$Currently all scalars in ScalaJack are supported. Here just in case... - case _ => - throw new ScalaJackError( - "Only Scala scalar values are supported as JValue Map keys" - ) - // $COVERAGE-ON$ - } - case value: AnyTypeAdapter => - value.read(parser).asInstanceOf[T] - case _ => - throw new ScalaJackError( - "Only scalar values are supported as JValue Map keys" - ) - } - } - - def write[WIRE]( - t: T, - writer: model.Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit = { - val keyValBuilder = - JValueBuilder().asInstanceOf[collection.mutable.Builder[Any, WIRE]] - wrappedTypeAdapter.write(t, writer, keyValBuilder) - val result = keyValBuilder.result() match { - case r: JBool => r.values.toString - case r: JDecimal => r.values.toString - case r: JDouble => r.values.toString - case r: JInt => r.values.toString - case r: JLong => r.values.toString - // $COVERAGE-OFF$All scalar/wrapped JValues supported as of this writing. Here just in case someone invents a new one. - case r: JString => r.values.toString // just in case someone wraps a String! - case r => - throw new ScalaJackError( - "Json4s type " + r.getClass.getName + " is not supported as a Map key" - ) - // $COVERAGE-ON$ - } - writer.writeString(result, out) - } -} diff --git a/src_old/main/scala/co.blocke.scalajack/model/ClassFieldMember.scala b/src_old/main/scala/co.blocke.scalajack/model/ClassFieldMember.scala deleted file mode 100644 index 73717585..00000000 --- a/src_old/main/scala/co.blocke.scalajack/model/ClassFieldMember.scala +++ /dev/null @@ -1,20 +0,0 @@ -package co.blocke.scalajack -package model - -import co.blocke.scala_reflection.info._ -import typeadapter._ - -case class ClassFieldMember[OWNER,T]( - info: FieldInfo, - valueTypeAdapter: TypeAdapter[T], - outerClass: java.lang.Class[OWNER], // class that "owns" this field - dbKeyIndex: Option[Int], - fieldMapName: Option[String] -): - def name: String = fieldMapName.getOrElse(info.name) - lazy val isOptional: Boolean = valueTypeAdapter match { - case _: OptionTypeAdapter[_] => true - case _: JavaOptionalTypeAdapter[_] => true - case _ if info.annotations.contains(OPTIONAL_ANNO) => true - case _ => false - } \ No newline at end of file diff --git a/src_old/main/scala/co.blocke.scalajack/model/HintValueModifier.scala b/src_old/main/scala/co.blocke.scalajack/model/HintValueModifier.scala deleted file mode 100644 index e1defe0e..00000000 --- a/src_old/main/scala/co.blocke.scalajack/model/HintValueModifier.scala +++ /dev/null @@ -1,38 +0,0 @@ -package co.blocke.scalajack -package model - -// import util.BijectiveFunctionHelpers._ - -trait HintValueModifier extends HintBijective - -/** - * Do-nothing modifier (default if none specified - */ -object DefaultHintModifier extends HintValueModifier { - def apply(rawHint: String): String = rawHint - def unapply(hintFieldType: String): String = hintFieldType -} - - -/** - * Convenience modifier that transforms a hint value string into a fully-qualified class name (and the reverse) - * using passed-in transformation functions. The appropriate Bijective is created under the covers. - */ -case class ClassNameHintModifier( - hintToClassname: (String) => String, - classNameToHint: (String) => String) - extends HintValueModifier { - def apply(rawHint: String): String = hintToClassname(rawHint) - def unapply(hintFieldType: String): String = classNameToHint(hintFieldType) // May explode -} - -/** - * Convenience modifier that transforms a map of string hint values to their respective types. - * Note there is a necessary assumption that the mapping is 1-to-1. If not you'll need to create the - * Bijective function yourself with whatever logic you need, and not use this class. - */ -case class StringMatchHintModifier(hintToType: Map[String, String]) extends HintValueModifier { - val typeToHint: Map[String, String] = hintToType.map(_.swap) - def apply(rawHint: String): String = hintToType(rawHint) - def unapply(hintFieldType: String): String = typeToHint(hintFieldType) -} diff --git a/src_old/main/scala/co.blocke.scalajack/model/JackFlavor.scala b/src_old/main/scala/co.blocke.scalajack/model/JackFlavor.scala deleted file mode 100644 index 43b7faf0..00000000 --- a/src_old/main/scala/co.blocke.scalajack/model/JackFlavor.scala +++ /dev/null @@ -1,115 +0,0 @@ -package co.blocke.scalajack -package model - -import typeadapter._ -import scala.collection.mutable -import co.blocke.scala_reflection._ - -trait JackFlavor[WIRE] extends ViewSplice: // extends Filterable[WIRE] with ViewSplice { - - type WIRE_TYPE = WIRE - - def parse(input: WIRE): Parser - - val defaultHint: String = "_hint" - val stringifyMapKeys: Boolean = false - val hintMap: Map[String, String] = Map.empty[String, String] - val hintValueModifiers: Map[String, HintValueModifier] = Map.empty[String, HintValueModifier] - val typeValueModifier: HintValueModifier = DefaultHintModifier - val enumsAsInt: Boolean = false - val customAdapters: List[TypeAdapterFactory] = List.empty[TypeAdapterFactory] - val parseOrElseMap: Map[Class[_], RType] = Map.empty[Class[_], RType] - val permissivesOk: Boolean = false - - lazy val taCache: TypeAdapterCache = bakeCache() - - def bakeCache(): TypeAdapterCache = - val permissives = - if (permissivesOk) - List( - PermissiveBigDecimalTypeAdapterFactory, - PermissiveBigIntTypeAdapterFactory, - PermissiveBooleanTypeAdapterFactory, - PermissiveByteTypeAdapterFactory, - PermissiveDoubleTypeAdapterFactory, - PermissiveFloatTypeAdapterFactory, - PermissiveIntTypeAdapterFactory, - PermissiveLongTypeAdapterFactory, - PermissiveShortTypeAdapterFactory, - PermissiveJavaBigDecimalTypeAdapterFactory, - PermissiveJavaBigIntegerTypeAdapterFactory, - PermissiveJavaBooleanTypeAdapterFactory, - PermissiveJavaByteTypeAdapterFactory, - PermissiveJavaDoubleTypeAdapterFactory, - PermissiveJavaFloatTypeAdapterFactory, - PermissiveJavaIntTypeAdapterFactory, - PermissiveJavaLongTypeAdapterFactory, - PermissiveJavaNumberTypeAdapterFactory, - PermissiveJavaShortTypeAdapterFactory - ) - else - List.empty[TypeAdapterFactory] - - // Need this (w/o parseOrElseFactories) because resolving the parse-or-else type adapters will cause an endless loop! - val stage1TC = TypeAdapterCache( - this, - permissives ::: customAdapters ::: TypeAdapterCache.StandardFactories - ) - - // ParseOrElse functionality - val parseOrElseFactories: List[TypeAdapterFactory] = parseOrElseMap.map { - case (attemptedTypeClass, fallbackType) => - new TypeAdapterFactory { - def matches(concrete: RType): Boolean = concrete.infoClass == attemptedTypeClass - - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[_] = - FallbackTypeAdapter( stage1TC.typeAdapterOf(concrete), stage1TC.typeAdapterOf(fallbackType) ) - } - }.toList - - TypeAdapterCache( - this, - parseOrElseFactories ::: stage1TC.factories - ) - - final inline def read[T](input: WIRE): T = - val typeAdapter = taCache.typeAdapterOf[T] - _read(input, typeAdapter) - - def _read[T](input: WIRE, typeAdapter: TypeAdapter[T]): T - - final inline def render[T](t: T): WIRE = - val typeAdapter = taCache.typeAdapterOf[T] - _render(t, typeAdapter) - - def _render[T](t: T, typeAdapter: TypeAdapter[T]): WIRE - - // These is so pervasively handy, let's just pre-stage it for easy access - lazy val stringTypeAdapter: TypeAdapter[String] = taCache.typeAdapterOf[String] - lazy val anyTypeAdapter: TypeAdapter[Any] = taCache.typeAdapterOf[Any] - - // Look up any custom hint label for given type, and if none then use default - def getHintLabelFor(tpe: RType): String = hintMap.getOrElse(tpe.name, defaultHint) - - def stringWrapTypeAdapterFactory[T]( - wrappedTypeAdapter: TypeAdapter[T], - emptyStringOk: Boolean = true - ): TypeAdapter[T] - def maybeStringWrapTypeAdapterFactory[T]( - wrappedTypeAdapter: TypeAdapter[T], - emptyStringOk: Boolean = true - ): TypeAdapter[T] - - def enumsAsInts(): JackFlavor[WIRE] - def allowPermissivePrimitives(): JackFlavor[WIRE] - def parseOrElse(poe: (RType, RType)*): JackFlavor[WIRE] - def withAdapters(ta: TypeAdapterFactory*): JackFlavor[WIRE] - def withDefaultHint(hint: String): JackFlavor[WIRE] - def withHints(h: (RType, String)*): JackFlavor[WIRE] - def withHintModifiers(hm: (RType, HintValueModifier)*): JackFlavor[WIRE] - def withTypeValueModifier(tm: HintValueModifier): JackFlavor[WIRE] - - // Need WIRE-specific Builder instance. By default this is StringBuilder. Mongo will overwrite this. - def getBuilder: mutable.Builder[WIRE, WIRE] = - StringBuilder() - .asInstanceOf[mutable.Builder[WIRE, WIRE]] diff --git a/src_old/main/scala/co.blocke.scalajack/model/LazyTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/model/LazyTypeAdapter.scala deleted file mode 100644 index b932238a..00000000 --- a/src_old/main/scala/co.blocke.scalajack/model/LazyTypeAdapter.scala +++ /dev/null @@ -1,38 +0,0 @@ -package co.blocke.scalajack -package model - -import co.blocke.scala_reflection._ -import scala.collection.mutable - -/** This helps fix the concurrent/recursion error on maps. This lets the TypeAdapter resolve later (i.e. Lazy) - */ -case class LazyTypeAdapter[T](taCache: TypeAdapterCache, info: RType) - extends TypeAdapter[T] { - - var resolvedTypeAdapter: TypeAdapter[T] = null - - override def resolved: TypeAdapter[T] = { - var typeAdapter = resolvedTypeAdapter - - // $COVERAGE-OFF$Can't really test as this is triggered by race condition, if it can happen at all. - if (typeAdapter == null) { - typeAdapter = taCache.typeAdapterOf(info).resolved.asInstanceOf[TypeAdapter[T]] - if (typeAdapter.isInstanceOf[LazyTypeAdapter[_]]) { - throw new IllegalStateException( - s"Type adapter for ${info.name} is still being built" - ) - } - resolvedTypeAdapter = typeAdapter - } - // $COVERAGE-ON$ - - typeAdapter - } - - def read(parser: Parser): T = resolved.read(parser) - def write[WIRE]( - t: T, - writer: Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit = - resolved.write(t, writer, out) -} diff --git a/src_old/main/scala/co.blocke.scalajack/model/Parser.scala b/src_old/main/scala/co.blocke.scalajack/model/Parser.scala deleted file mode 100644 index 38952574..00000000 --- a/src_old/main/scala/co.blocke.scalajack/model/Parser.scala +++ /dev/null @@ -1,50 +0,0 @@ -package co.blocke.scalajack -package model - -import typeadapter.classes.ClassTypeAdapterBase -import scala.collection.mutable -import co.blocke.scala_reflection.info.TypeMemberInfo - -trait Parser { - type WIRE - - val jackFlavor: JackFlavor[WIRE] // This is needed and used by permissive type adapters - - def expectString(nullOK: Boolean = true): String - def expectList[K, TO]( - KtypeAdapter: TypeAdapter[K], - builder: mutable.Builder[K, TO]): TO - def expectTuple( - tupleFieldTypeAdapters: List[TypeAdapter[_]] - ): List[Object] - def expectMap[K, V, TO]( - keyTypeAdapter: TypeAdapter[K], - valueTypeAdapter: TypeAdapter[V], - builder: mutable.Builder[(K, V), TO]): TO - def expectObject( - classBase: ClassTypeAdapterBase[_], - hintLabel: String - ): (mutable.BitSet, List[Object], java.util.HashMap[String, _]) - def expectBoolean(): Boolean - def expectNumber(nullOK: Boolean = false): String - def peekForNull: Boolean // peek-ahead to find null - def scanForHint(hint: String, converterFn: HintBijective): Class[_] - - // For embedded type members. Convert the type member into runtime "actual" type, e.g. T --> Foo - def resolveTypeMembers( - typeMembersByName: Map[String, TypeMemberInfo], - converterFn: HintBijective - ): Map[String, TypeMemberInfo] // Returns Map[Type Signature Type (e.g. 'T'), Type] - - def showError(msg: String): String - def backspace(): Unit - def mark(): Int - def revertToMark(mark: Int): Unit - def nextIsString: Boolean - def nextIsNumber: Boolean - def nextIsObject: Boolean - def nextIsArray: Boolean - def nextIsBoolean: Boolean - def subParser(input: WIRE): Parser - def sourceAsString: String -} \ No newline at end of file diff --git a/src_old/main/scala/co.blocke.scalajack/model/StringBuilder.scala b/src_old/main/scala/co.blocke.scalajack/model/StringBuilder.scala deleted file mode 100644 index 4943b8c0..00000000 --- a/src_old/main/scala/co.blocke.scalajack/model/StringBuilder.scala +++ /dev/null @@ -1,18 +0,0 @@ -package co.blocke.scalajack -package model - -import scala.collection.mutable - -case class StringBuilder[WIRE]() extends mutable.Builder[WIRE, WIRE] { - - private val buf = new StringBuffer() - - def addOne(elem: WIRE): StringBuilder.this.type = { - buf.append(elem) - this - } - - def clear() = buf.setLength(0) - - def result() = buf.toString.asInstanceOf[WIRE] -} diff --git a/src_old/main/scala/co.blocke.scalajack/model/TypeAdapaterCache.scala b/src_old/main/scala/co.blocke.scalajack/model/TypeAdapaterCache.scala deleted file mode 100644 index 2a000f11..00000000 --- a/src_old/main/scala/co.blocke.scalajack/model/TypeAdapaterCache.scala +++ /dev/null @@ -1,140 +0,0 @@ -package co.blocke.scalajack -package model - -import typeadapter._ -import typeadapter.classes._ -import scala.util.{ Success, Try } -import co.blocke.scala_reflection._ -import co.blocke.scala_reflection.impl.SelfRefRType -import co.blocke.scala_reflection.info._ - - -object TypeAdapterCache { - - val StandardFactories: List[TypeAdapterFactory] = - List( - BigDecimalTypeAdapterFactory, - BigIntTypeAdapterFactory, - BinaryTypeAdapterFactory, - BooleanTypeAdapterFactory, - ByteTypeAdapterFactory, - CharTypeAdapterFactory, - DoubleTypeAdapterFactory, - FloatTypeAdapterFactory, - IntTypeAdapterFactory, - LongTypeAdapterFactory, - ShortTypeAdapterFactory, - StringTypeAdapterFactory, - OptionTypeAdapterFactory, - TryTypeAdapterFactory, - TupleTypeAdapterFactory, - EitherTypeAdapterFactory, // Either must precede SealedTraitTypeAdapter - UnionTypeAdapterFactory, - IntersectionTypeAdapterFactory, - ArrayTypeAdapterFactory, - EnumTypeAdapterFactory, - UUIDTypeAdapterFactory, - CollectionTypeAdapterFactory, - - // WARNING: These two must precede CaseClassTypeAdapter in this list or all - // ValueClasses will be interpreted as case classes, and case objects - // will likewise be hidden (interpreted as regular classes). - SealedTraitTypeAdapterFactory, - ValueClassTypeAdapterFactory, - ScalaClassTypeAdapterFactory, - - TraitTypeAdapterFactory, - AnyTypeAdapterFactory, - JavaBigDecimalTypeAdapterFactory, - JavaBigIntegerTypeAdapterFactory, - JavaBooleanTypeAdapterFactory, - JavaByteTypeAdapterFactory, - JavaCharacterTypeAdapterFactory, - JavaDoubleTypeAdapterFactory, - JavaFloatTypeAdapterFactory, - JavaIntegerTypeAdapterFactory, - JavaLongTypeAdapterFactory, - JavaNumberTypeAdapterFactory, - JavaShortTypeAdapterFactory, - DurationTypeAdapterFactory, - InstantTypeAdapterFactory, - LocalDateTimeTypeAdapterFactory, - LocalDateTypeAdapterFactory, - LocalTimeTypeAdapterFactory, - OffsetDateTimeTypeAdapterFactory, - OffsetTimeTypeAdapterFactory, - PeriodTypeAdapterFactory, - ZonedDateTimeTypeAdapterFactory, - JavaClassTypeAdapterFactory - ) -} - -case class TypeAdapterCache( - jackFlavor: JackFlavor[_], - factories: List[TypeAdapterFactory]): - - sealed trait Phase - case object Uninitialized extends Phase - case object Initializing extends Phase - case class Initialized(typeAdapterAttempt: Try[TypeAdapter[_]]) extends Phase - - val selfCache = this - - class TypeEntry(tpe: RType): - @volatile - private var phase: Phase = Uninitialized - // println(s"--> TACache (${typeEntries.size}) add [${tpe.name}]") - - def typeAdapter: TypeAdapter[_] = - val attempt = - phase match { - case Initialized(a) => a - - case Uninitialized | Initializing => - synchronized { - phase match { - case Uninitialized => - phase = Initializing - val typeAdapterAttempt = Try { - val foundFactory = factories.find(_.matches(tpe)).get - foundFactory.makeTypeAdapter(tpe)(selfCache) - } - phase = Initialized(typeAdapterAttempt) - typeAdapterAttempt - - case Initializing => - Success(LazyTypeAdapter(TypeAdapterCache.this, tpe)) - - case Initialized(a) => - a - } - } - } - attempt.get - - - private val typeEntries = new java.util.concurrent.ConcurrentHashMap[RType, TypeEntry] - - def withFactory(factory: TypeAdapterFactory): TypeAdapterCache = - copy(factories = factories :+ factory) - - // def typeAdapterOf(tpe: TypeStructure): TypeAdapter[_] = - // typeAdapterOf(RType.ofType(tpe)) - - def typeAdapterOf(concreteType: RType): TypeAdapter[_] = - typeEntries.computeIfAbsent(concreteType, ConcreteTypeEntryFactory).typeAdapter - - inline def typeAdapterOf[T]: TypeAdapter[T] = - typeAdapterOf(RType.of[T]).asInstanceOf[TypeAdapter[T]] - - val self = this - - object ConcreteTypeEntryFactory extends java.util.function.Function[RType, TypeEntry]: - private val AnyRType = RType.of[Any] - private val AnySelfRef = SelfRefRType("scala.Any") - override def apply(concrete: RType): TypeEntry = - concrete match { - case AnySelfRef => new TypeEntry(AnyRType) - case s: SelfRefRType => new TypeEntry(RType.of(s.infoClass)) - case s => new TypeEntry(s) - } diff --git a/src_old/main/scala/co.blocke.scalajack/model/TypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/model/TypeAdapter.scala deleted file mode 100644 index c0bb1576..00000000 --- a/src_old/main/scala/co.blocke.scalajack/model/TypeAdapter.scala +++ /dev/null @@ -1,72 +0,0 @@ -package co.blocke.scalajack -package model - -import scala.collection.mutable -import scala.reflect.ClassTag -import co.blocke.scala_reflection._ -import co.blocke.scala_reflection.info._ -import co.blocke.scala_reflection.impl.Clazzes._ - - -/** - * TypeAdapter includes two matching patterns you can use when you extend trait TypeAdapter for your - * custom adapters. The two matching behaviors are '===' and '=:='. - * - * This difference is because =:= matches children. Consider: - * - * type Phone = String - * case class( name:String, phone:Phone ) - * - * With =:= both name and phone (String and Phone) will match a TypeAdapter derived from =:=. - * This is actually what you want if you haven't overridden Phone with its own TypeAdapter... it should default - * to the TypeAdapter of its base type. - * - * But... if you did provide an override PhoneTypeAdapter you want the matching to be strict, so we use === - * in this case. With strict matching String != Phone. - * - */ - trait TypeAdapter[T] { - self => - - type tpe = T - - val info: RType - def resolved: TypeAdapter[T] = this // Might be something else during Lazy construction - - def defaultValue: Option[T] = None - - // Used to determine if we need to wrap Map keys in quotes (no, if isStringish == true -- the quotes are automatic in this case) - def isStringish: Boolean = false - def maybeStringish: Boolean = false // means we don't know for sure if something is Stringish until read/render time (not in Factory), e.g. Option or Union - - def read(parser: Parser): T - def write[WIRE]( - t: T, - writer: Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit - - def as[U <: TypeAdapter[_]: ClassTag]: U = { - val runtimeClass = implicitly[ClassTag[U]].runtimeClass - try { - runtimeClass.cast(self).asInstanceOf[U] - } catch { - case _: ClassCastException => - throw new RuntimeException( - s"$self is not an instance of ${implicitly[ClassTag[U]].runtimeClass}" - ) - } - } - - // Used to correctly-type tuple fields, which each have separate types that are unknown at write, but... - // each field's TypeAdapter *does* know its type so it can be correctly cast inside the TypeAdapter. - inline def castAndWrite[WIRE]( v: Any, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - write(v.asInstanceOf[tpe], writer, out) -} - -trait ScalarTypeAdapter[T] extends TypeAdapter[T] - -// Marker trait for collections -trait Collectionish - -// Marker trait for classes -trait Classish \ No newline at end of file diff --git a/src_old/main/scala/co.blocke.scalajack/model/TypeAdapterFactory.scala b/src_old/main/scala/co.blocke.scalajack/model/TypeAdapterFactory.scala deleted file mode 100644 index a5b345ac..00000000 --- a/src_old/main/scala/co.blocke.scalajack/model/TypeAdapterFactory.scala +++ /dev/null @@ -1,8 +0,0 @@ -package co.blocke.scalajack -package model - -import co.blocke.scala_reflection._ - -trait TypeAdapterFactory: - def matches(concrete: RType): Boolean - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[_] diff --git a/src_old/main/scala/co.blocke.scalajack/model/ViewSplice.scala b/src_old/main/scala/co.blocke.scalajack/model/ViewSplice.scala deleted file mode 100644 index 14a19f32..00000000 --- a/src_old/main/scala/co.blocke.scalajack/model/ViewSplice.scala +++ /dev/null @@ -1,84 +0,0 @@ -package co.blocke.scalajack -package model - -import typeadapter.classes.CaseClassTypeAdapter - -trait ViewSplice: - - me: JackFlavor[_] => - - /** - * Project fields from given master object to a view object of type T. Field names/types must match master - * precisely. - * @param master the master object from which the smaller object is projected - * @return an object of type T which is a "subset" of the master - */ - // WARNING: Assumes CaseClassTypeAdapter.members is in constructor-order. If not, sort on members.index. - inline def view[T](master: Any): T = { - val viewTarget = taCache.typeAdapterOf[T] match { - case ta: CaseClassTypeAdapter[T] => ta - case ta => - throw new ScalaJackError( - s"Output of view() must be a case class. ${ta.info.name} is not a case class." - ) - } - val masterData = master.getClass.getDeclaredFields - val args = viewTarget.fieldMembersByName.toList.flatMap { - case (fieldName: String, f: ClassFieldMember[_, _]) => - val gotOne = masterData - .find( - md => md.getName == f.name && md.getType == f.outerClass.getMethod(f.name).getReturnType - ) - .map(dataField => { - dataField.setAccessible(true) - dataField.get(master) - }) - if (gotOne.isEmpty && !f.isOptional) - throw new ScalaJackError( - "View master object " + master.getClass.getName + " is missing field " + fieldName + " required to build view object " + viewTarget.info.name - ) - gotOne - } - viewTarget.constructWith(args) - } - - /** - * Splice a view (subset) object's fields into a master object's fields. - * @param view the subset object - * @param master master object - * @return the master object with the view object's corresponding fields merged/overlayed - */ - inline def spliceInto[T, U](view: T, master: U): U = { - val masterTarget = taCache.typeAdapterOf[U] match { - case ta: CaseClassTypeAdapter[U] => ta - case ta => - throw new ScalaJackError( - s"Output of spliceInto() must be a case class. ${ta.info.name} is not a case class." - ) - } - val viewData = view.getClass.getDeclaredFields - val masterData = master.getClass.getDeclaredFields - val args = masterTarget.orderedFieldNames.map { fieldName => - val f = masterTarget.fieldMembersByName(fieldName) - viewData - .find( - vd => vd.getName == f.name && vd.getType == f.outerClass.getMethod(f.name).getReturnType - ) - .map(dataField => { - // Found matching master field in view object - dataField.setAccessible(true) - dataField.get(view) - }) - .getOrElse( - masterData - .find(_.getName == f.name) - .map { dataField => - // Didn't find matching master field in view object--just use original field from master object - dataField.setAccessible(true) - dataField.get(master) - } - .get - ) - } - masterTarget.constructWith(args) - } \ No newline at end of file diff --git a/src_old/main/scala/co.blocke.scalajack/model/Writer.scala b/src_old/main/scala/co.blocke.scalajack/model/Writer.scala deleted file mode 100644 index f1eb6ff4..00000000 --- a/src_old/main/scala/co.blocke.scalajack/model/Writer.scala +++ /dev/null @@ -1,34 +0,0 @@ -package co.blocke.scalajack -package model - -import scala.collection.Map -import scala.collection.mutable -import co.blocke.scala_reflection.info.{TupleInfo, FieldInfo} - -case class ExtraFieldValue[T](value: T, valueTypeAdapter: TypeAdapter[T]) - -trait Writer[WIRE] { - def writeArray[Elem](t: Iterable[Elem], elemTypeAdapter: TypeAdapter[Elem], out: mutable.Builder[WIRE, WIRE]): Unit - def writeBigInt(t: BigInt, out: mutable.Builder[WIRE, WIRE]): Unit - def writeBoolean(t: Boolean, out: mutable.Builder[WIRE, WIRE]): Unit - def writeDecimal(t: BigDecimal, out: mutable.Builder[WIRE, WIRE]): Unit - def writeDouble(t: Double, out: mutable.Builder[WIRE, WIRE]): Unit - def writeInt(t: Int, out: mutable.Builder[WIRE, WIRE]): Unit - def writeLong(t: Long, out: mutable.Builder[WIRE, WIRE]): Unit - def writeMap[Key, Value, To](t: Map[Key, Value], keyTypeAdapter: TypeAdapter[Key], valueTypeAdapter: TypeAdapter[Value], out: mutable.Builder[WIRE, WIRE]): Unit - def writeNull(out: mutable.Builder[WIRE, WIRE]): Unit - def writeObject[T]( - t: T, - orderedFieldNames: List[String], - fieldMembersByName: Map[String, ClassFieldMember[_,_]], - out: mutable.Builder[WIRE, WIRE], - extras: List[(String, ExtraFieldValue[_])] = List.empty[(String, ExtraFieldValue[_])] - ): Unit - def writeString(t: String, out: mutable.Builder[WIRE, WIRE]): Unit - def writeRaw(t: WIRE, out: mutable.Builder[WIRE, WIRE]): Unit // i.e. no quotes for JSON - def writeTuple[T]( - t: T, - writeFn: (Product) => List[(TypeAdapter[_], Any)], - out: mutable.Builder[WIRE, WIRE] - ): Unit -} \ No newline at end of file diff --git a/src_old/main/scala/co.blocke.scalajack/typeadapter/AnyTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/AnyTypeAdapter.scala deleted file mode 100644 index b189d88c..00000000 --- a/src_old/main/scala/co.blocke.scalajack/typeadapter/AnyTypeAdapter.scala +++ /dev/null @@ -1,101 +0,0 @@ -package co.blocke.scalajack -package typeadapter - -import model._ -import classes._ -import co.blocke.scala_reflection._ -import collection._ - -import scala.collection.mutable -import scala.collection.mutable.ListBuffer -import scala.util.{Success, Try} - -object AnyTypeAdapterFactory extends TypeAdapterFactory: - def matches(concrete: RType): Boolean = concrete.infoClass == impl.PrimitiveType.Scala_Any.infoClass - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[_] = - AnyTypeAdapter(concrete, taCache) - - -case class AnyTypeAdapter(info: RType, taCache: TypeAdapterCache) extends TypeAdapter[Any] { - val jackFlavor = taCache.jackFlavor - - lazy val mapAnyTypeAdapter: TypeAdapter[Map[Any, Any]] = taCache.typeAdapterOf[Map[Any, Any]] - lazy val listAnyTypeAdapter: TypeAdapter[List[Any]] = taCache.typeAdapterOf[List[Any]] - lazy val optionAnyTypeAdapter: TypeAdapter[Option[Any]] = taCache.typeAdapterOf[Option[Any]] - - override def maybeStringish: Boolean = true - - def read(parser: Parser): Any = - parser match { - case p if p.peekForNull => null - case p if p.nextIsBoolean => p.expectBoolean() - case p if p.nextIsNumber => - BigDecimal(p.expectNumber()) match { - case i if i.isValidInt => i.toIntExact - case i if i.isValidLong => i.toLongExact - case d if d.isDecimalDouble => d.toDouble - case d if d.ulp == 1 => d.toBigInt - case d => d - } - case p if p.nextIsString && jackFlavor.permissivesOk => - jackFlavor.stringWrapTypeAdapterFactory(this).read(p) - case p if p.nextIsString => p.expectString() - case p if p.nextIsArray => - val listBuilder: ListBuffer[Any] = mutable.ListBuffer.empty[Any] - p.expectList(jackFlavor.anyTypeAdapter, listBuilder) - case p if p.nextIsObject => - val mapBuilder = mutable.Map - .empty[Any, Any] - .asInstanceOf[mutable.Builder[(Any, Any), mutable.Map[Any, Any]]] - val mark = parser.mark() - val foundMap = p.expectMap[Any, Any, mutable.Map[Any, Any]]( - jackFlavor.stringWrapTypeAdapterFactory(this), - this, - mapBuilder - ) - if (foundMap.contains(jackFlavor.defaultHint)) { - parser.revertToMark(mark) - Try( - Class.forName(foundMap(jackFlavor.defaultHint).toString) - ) match { - case Success(concreteTypeClass) => taCache.typeAdapterOf(RType.of(concreteTypeClass)).read(p) - case _ => foundMap - } - } else - foundMap.toMap - case p => p.sourceAsString - } - - // Need this little bit of gymnastics here to unpack the X type parameter so we can use it to case the TypeAdapter - private def unpack[X, WIRE](value: X, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - taCache - .typeAdapterOf(RType.of(value.getClass)) - .asInstanceOf[TypeAdapter[X]] match { - case ta: CaseClassTypeAdapter[X] => - val builder = jackFlavor.getBuilder.asInstanceOf[mutable.Builder[WIRE, WIRE]] - ta.writeWithHint[WIRE](jackFlavor.asInstanceOf[JackFlavor[WIRE]], value, writer, builder) - writer.writeRaw(builder.result(), out) - case ta: NonCaseClassTypeAdapter[X] => - val builder = jackFlavor.getBuilder.asInstanceOf[mutable.Builder[WIRE, WIRE]] - ta.writeWithHint[WIRE](jackFlavor.asInstanceOf[JackFlavor[WIRE]], value, writer, builder) - writer.writeRaw(builder.result(), out) - case ta => - ta.write(value, writer, out) - } - - - // WARNING: JSON output broken for Option[...] where value is None -- especially bad for Map keys! - def write[WIRE]( - t: Any, - writer: Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit = - t match { - case null => writer.writeNull(out) - case e if e.getClass.getName == "scala.Enumeration$Val" => writer.writeString(t.toString, out) - case _: scala.reflect.Enum => writer.writeString(t.toString, out) - case _: Map[_, _] => mapAnyTypeAdapter.write(t.asInstanceOf[Map[Any, Any]], writer, out) - case _: Seq[_] => listAnyTypeAdapter.write(t.asInstanceOf[List[Any]], writer, out) - case _: Option[_] => optionAnyTypeAdapter.write(t.asInstanceOf[Option[Any]], writer, out) - case v => unpack[Any,WIRE](v, writer, out) - } -} diff --git a/src_old/main/scala/co.blocke.scalajack/typeadapter/ArrayTypeAdatper.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/ArrayTypeAdatper.scala deleted file mode 100644 index 3b427038..00000000 --- a/src_old/main/scala/co.blocke.scalajack/typeadapter/ArrayTypeAdatper.scala +++ /dev/null @@ -1,71 +0,0 @@ -package co.blocke.scalajack -package typeadapter - -import model._ - -import co.blocke.scala_reflection.impl.Clazzes._ -import co.blocke.scala_reflection.info._ -import co.blocke.scala_reflection._ - -import scala.collection.mutable -import scala.reflect.ClassTag -import scala.language.implicitConversions - - -object ArrayTypeAdapterFactory extends TypeAdapterFactory: - - def matches(concrete: RType): Boolean = - concrete match { - case _: ArrayInfo => true - case _: JavaArrayInfo => true - case _ => false - } - - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[_] = - concrete match { - case arrInfo: ArrayInfo => - val elementInfo = arrInfo.elementType - ArrayTypeAdapter( - concrete, - elementInfo.isInstanceOf[OptionInfo], - elementInfo, - taCache.typeAdapterOf(elementInfo)) - case javaInfo: JavaArrayInfo => - val elementInfo = javaInfo.elementType - ArrayTypeAdapter( - concrete, - false, // TODO: Support java.Optional ==> elementInfo.isInstanceOf[OptionInfo], - elementInfo, - taCache.typeAdapterOf(elementInfo)) - } - - -case class ArrayTypeAdapter[ELEM]( - info: RType, - elemIsOptional: Boolean, - elementType: RType, - elementTypeAdapter: TypeAdapter[ELEM] - ) extends TypeAdapter[Array[ELEM]] with ScalarTypeAdapter[Array[ELEM]]: - - def read(parser: Parser): Array[ELEM] = - parser.peekForNull match { - case true => null - case _ => - val classtag = ClassTag[ELEM](elementType.infoClass) - val builder: mutable.Builder[ELEM,Array[ELEM]] = Array.newBuilder[ELEM](classtag.asInstanceOf[ClassTag[ELEM]]).asInstanceOf[mutable.Builder[ELEM,Array[ELEM]]] - val values = parser.expectList(elementTypeAdapter, builder) - builder.result - } - - def write[WIRE](t: Array[ELEM], writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - t match { - case null => writer.writeNull(out) - case _ if elemIsOptional => - writer.writeArray( - t.toList.filterNot(_ == None), - elementTypeAdapter, - out - ) - case _ => - writer.writeArray(t.toList, elementTypeAdapter, out) - } diff --git a/src_old/main/scala/co.blocke.scalajack/typeadapter/CollectionTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/CollectionTypeAdapter.scala deleted file mode 100644 index 41c207bc..00000000 --- a/src_old/main/scala/co.blocke.scalajack/typeadapter/CollectionTypeAdapter.scala +++ /dev/null @@ -1,191 +0,0 @@ -package co.blocke.scalajack -package typeadapter - -import model._ -import collection._ - -import co.blocke.scala_reflection._ -import co.blocke.scala_reflection.impl.CollectionRType -import co.blocke.scala_reflection.info._ - - -object CollectionTypeAdapterFactory extends TypeAdapterFactory: - def matches(concrete: RType): Boolean = - concrete match { - case _: CollectionRType => true - case _ => false - } - - inline def isOptionalTA(ta: TypeAdapter[_]) = ta.isInstanceOf[OptionTypeAdapter[_]] || ta.isInstanceOf[JavaOptionalTypeAdapter[_]] - - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[_] = - concrete match { - case c: SeqLikeInfo => - val elementInfo = c.elementType - val companionClass = Class.forName(c.infoClass.getName+"$") - val companionInstance = companionClass.getField("MODULE$").get(companionClass) - val builderMethod = companionClass.getMethod("newBuilder") - SeqLikeTypeAdapter(concrete, elementInfo.isInstanceOf[OptionInfo], taCache.typeAdapterOf(elementInfo), companionInstance, builderMethod) - - case c: MapLikeInfo => - val companionClass = Class.forName(c.infoClass.getName+"$") - val companionInstance = companionClass.getField("MODULE$").get(companionClass) - val builderMethod = companionClass.getMethod("newBuilder") - - val jackFlavor = taCache.jackFlavor - val keyTypeAdapter = taCache.typeAdapterOf(c.elementType) - // Wrap Map keys in a StringWrapTypeAdapter? - val finalKeyTypeAdapter = keyTypeAdapter match { - case k if k.isStringish => k // ready-to-eat - case k if k.maybeStringish => jackFlavor.maybeStringWrapTypeAdapterFactory(k) - case k => jackFlavor.stringWrapTypeAdapterFactory(k) // wrap map keys in quotes - } - val valueTypeAdapter = taCache.typeAdapterOf(c.elementType2) match { - case ta: OptionTypeAdapter[_] => ta.convertNullToNone() - case ta: JavaOptionalTypeAdapter[_] => ta.convertNullToNone() - case ta => ta - } - - // Note: We include Any here because Any *could* be an Option, so we must include it as a possibility - val keyIsOptionalOrAny = - isOptionalTA(keyTypeAdapter) || - (keyTypeAdapter.isInstanceOf[StringWrapTypeAdapter[_]] && isOptionalTA(keyTypeAdapter - .asInstanceOf[StringWrapTypeAdapter[_]] - .wrappedTypeAdapter)) || - keyTypeAdapter.isInstanceOf[AnyTypeAdapter] - // keyTypeAdapter == taCache.jackFlavor.anyMapKeyTypeAdapter - - val valueIsOptionalOrAny = isOptionalTA(valueTypeAdapter) || valueTypeAdapter.isInstanceOf[AnyTypeAdapter] - - MapLikeTypeAdapter( - concrete, - keyIsOptionalOrAny, - valueIsOptionalOrAny, - finalKeyTypeAdapter, - valueTypeAdapter, - companionInstance, - builderMethod) - - - case c: JavaListInfo => - val elementInfo = c.elementType - // For List-like Java collections, use a ListBuilder then convert later to the Java collection - val companionClass = Class.forName("scala.collection.immutable.List$") - val companionInstance = companionClass.getField("MODULE$").get(companionClass) - val builderMethod = companionClass.getMethod("newBuilder") - val javaCollectionConstructor = c.infoClass.getConstructor(Class.forName("java.util.Collection")) - val toArrayMethod = c.infoClass.getMethod("toArray") - val elementTA = taCache.typeAdapterOf(elementInfo) - - JavaSeqLikeTypeAdapter( - concrete, - isOptionalTA(elementTA), - elementTA, - companionInstance, - builderMethod, - javaCollectionConstructor, - toArrayMethod - ) - - case c: JavaSetInfo => - val elementInfo = c.elementType - // For List-like Java collections, use a ListBuilder then convert later to the Java collection - val companionClass = Class.forName("scala.collection.immutable.List$") - val companionInstance = companionClass.getField("MODULE$").get(companionClass) - val builderMethod = companionClass.getMethod("newBuilder") - val javaCollectionConstructor = c.infoClass.getConstructor(Class.forName("java.util.Collection")) - val toArrayMethod = c.infoClass.getMethod("toArray") - val elementTA = taCache.typeAdapterOf(elementInfo) - - JavaSeqLikeTypeAdapter( - concrete, - isOptionalTA(elementTA), - elementTA, - companionInstance, - builderMethod, - javaCollectionConstructor, - toArrayMethod - ) - - case c: JavaQueueInfo => - val elementInfo = c.elementType - // For List-like Java collections, use a ListBuilder then convert later to the Java collection - val companionClass = Class.forName("scala.collection.immutable.List$") - val companionInstance = companionClass.getField("MODULE$").get(companionClass) - val builderMethod = companionClass.getMethod("newBuilder") - val javaCollectionConstructor = c.infoClass.getConstructor(Class.forName("java.util.Collection")) - val toArrayMethod = c.infoClass.getMethod("toArray") - val elementTA = taCache.typeAdapterOf(elementInfo) - - JavaSeqLikeTypeAdapter( - concrete, - isOptionalTA(elementTA), - elementTA, - companionInstance, - builderMethod, - javaCollectionConstructor, - toArrayMethod - ) - - case c: JavaStackInfo => - val elementInfo = c.elementType - // For List-like Java collections, use a ListBuilder then convert later to the Java collection - val companionClass = Class.forName("scala.collection.immutable.List$") - val companionInstance = companionClass.getField("MODULE$").get(companionClass) - val builderMethod = companionClass.getMethod("newBuilder") - val javaCollectionConstructor = c.infoClass.getConstructors.head - val toArrayMethod = c.infoClass.getMethod("toArray") - val elementTA = taCache.typeAdapterOf(elementInfo) - - JavaStackTypeAdapter( - concrete, - isOptionalTA(elementTA), - taCache.typeAdapterOf(elementInfo), - companionInstance, - builderMethod, - javaCollectionConstructor, - toArrayMethod - ) - - case c: JavaMapInfo => - val companionClass = Class.forName("scala.collection.immutable.Map$") - val companionInstance = companionClass.getField("MODULE$").get(companionClass) - val builderMethod = companionClass.getMethod("newBuilder") - val javaMapConstructor = c.infoClass.getConstructor(Class.forName("java.util.Map")) - - val jackFlavor = taCache.jackFlavor - val keyTypeAdapter = taCache.typeAdapterOf(c.elementType) - // Wrap Map keys in a StringWrapTypeAdapter? - val finalKeyTypeAdapter = keyTypeAdapter match { - case k if k.isStringish => k // ready-to-eat - case k if k.maybeStringish => jackFlavor.maybeStringWrapTypeAdapterFactory(k) - case k => jackFlavor.stringWrapTypeAdapterFactory(k) // wrap map keys in quotes - } - val valueTypeAdapter = taCache.typeAdapterOf(c.elementType2) match { - case ta: OptionTypeAdapter[_] => ta.convertNullToNone() - case ta: JavaOptionalTypeAdapter[_] => ta.convertNullToNone() - case ta => ta - } - - // Note: We include Any here because Any *could* be an Option, so we must include it as a possibility - val keyIsOptionalOrAny = - isOptionalTA(keyTypeAdapter) || - (keyTypeAdapter.isInstanceOf[StringWrapTypeAdapter[_]] && isOptionalTA(keyTypeAdapter - .asInstanceOf[StringWrapTypeAdapter[_]] - .wrappedTypeAdapter)) || - keyTypeAdapter.isInstanceOf[AnyTypeAdapter] - // keyTypeAdapter == taCache.jackFlavor.anyMapKeyTypeAdapter - - val valueIsOptionalOrAny = isOptionalTA(valueTypeAdapter) || valueTypeAdapter.isInstanceOf[AnyTypeAdapter] - - JavaMapLikeTypeAdapter( - concrete, - keyIsOptionalOrAny, - valueIsOptionalOrAny, - finalKeyTypeAdapter, - valueTypeAdapter, - companionInstance, - builderMethod, - javaMapConstructor) - - } diff --git a/src_old/main/scala/co.blocke.scalajack/typeadapter/EitherTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/EitherTypeAdapter.scala deleted file mode 100644 index aedde23c..00000000 --- a/src_old/main/scala/co.blocke.scalajack/typeadapter/EitherTypeAdapter.scala +++ /dev/null @@ -1,78 +0,0 @@ -package co.blocke.scalajack -package typeadapter - -import model._ -import co.blocke.scala_reflection._ -import co.blocke.scala_reflection.info.EitherInfo -import co.blocke.scala_reflection.impl.Clazzes._ - -import scala.collection.mutable.Builder -import scala.util.{ Failure, Success, Try } - -object EitherTypeAdapterFactory extends TypeAdapterFactory: - - def matches(concrete: RType): Boolean = - concrete match { - case _: EitherInfo => true - case _ => false - } - - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[_] = - val eitherInfo = concrete.asInstanceOf[EitherInfo] - val leftInfo = eitherInfo.leftType - val rightInfo = eitherInfo.rightType - - if( leftInfo.infoClass <:< rightInfo.infoClass || rightInfo.infoClass <:< leftInfo.infoClass) - throw new IllegalArgumentException( - s"Types ${leftInfo.name} and ${rightInfo.name} are not mutually exclusive" - ) - val leftTypeAdapter = taCache.typeAdapterOf(leftInfo) - val rightTypeAdapter = taCache.typeAdapterOf(rightInfo) - - EitherTypeAdapter( - concrete, - leftTypeAdapter, - rightTypeAdapter) - - -case class EitherTypeAdapter[L, R]( - info: RType, - leftTypeAdapter: TypeAdapter[L], - rightTypeAdapter: TypeAdapter[R]) - extends TypeAdapter[Either[L, R]] { - - override def isStringish: Boolean = leftTypeAdapter.isStringish && rightTypeAdapter.isStringish - override def maybeStringish: Boolean = !isStringish - - def read(parser: Parser): Either[L, R] = { - val savedReader = parser.mark() - if (parser.peekForNull) - null - else - Try(rightTypeAdapter.read(parser)) match { - case Success(rightValue) => - Right(rightValue.asInstanceOf[R]) - case Failure(_) => // Right parse failed... try left - parser.revertToMark(savedReader) - Try(leftTypeAdapter.read(parser)) match { - case Success(leftValue) => - Left(leftValue.asInstanceOf[L]) - case Failure(x) => - parser.backspace() - throw new ScalaJackError( - parser.showError(s"Failed to read either side of Either") - ) - } - } - } - - def write[WIRE]( - t: Either[L, R], - writer: Writer[WIRE], - out: Builder[WIRE, WIRE]): Unit = - t match { - case null => writer.writeNull(out) - case Left(v) => leftTypeAdapter.write(v, writer, out) - case Right(v) => rightTypeAdapter.write(v, writer, out) - } -} diff --git a/src_old/main/scala/co.blocke.scalajack/typeadapter/EnumTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/EnumTypeAdapter.scala deleted file mode 100644 index 00529bb5..00000000 --- a/src_old/main/scala/co.blocke.scalajack/typeadapter/EnumTypeAdapter.scala +++ /dev/null @@ -1,178 +0,0 @@ -package co.blocke.scalajack -package typeadapter - -import model._ - -import scala.collection.mutable -import scala.util.{Try, Success, Failure} -import java.lang.reflect.Method -import co.blocke.scala_reflection._ -import co.blocke.scala_reflection.info._ - -object EnumTypeAdapterFactory extends TypeAdapterFactory: - def matches(concrete: RType): Boolean = - concrete match { - case _: ScalaEnumInfo => true - case _: ScalaEnumerationInfo => true - case _: JavaEnumInfo => true - case _ => false - } - - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[_] = - val enumsAsInt = taCache.jackFlavor.enumsAsInt - concrete match { - // Scala 2.x Enumeration support - case scalaOld: ScalaEnumerationInfo => - val erasedEnumClassName = scalaOld.name + "$" - val enumInstance = Class - .forName(erasedEnumClassName) - .getField(scala.reflect.NameTransformer.MODULE_INSTANCE_NAME) - .get(null) - .asInstanceOf[Enumeration] - ScalaEnumerationTypeAdapter(enumInstance, concrete, enumsAsInt) - - // Scala 3.x Enum support - case scalaNew: ScalaEnumInfo => - ScalaEnumTypeAdapter(concrete, enumsAsInt) - - // Java Enum support - case javaEnum: JavaEnumInfo => - JavaEnumTypeAdapter(concrete, enumsAsInt) - } - - -case class ScalaEnumerationTypeAdapter[E <: Enumeration]( - e: E, - info: RType, - enumsAsInt: Boolean - ) extends TypeAdapter[e.Value]: - override def isStringish: Boolean = !enumsAsInt - - def read(parser: Parser): e.Value = - if (parser.nextIsNumber) { - val en = parser.expectNumber() - Try(e(en.toInt)) match { - case Success(u) => u - case Failure(u) => - parser.backspace() - throw new ScalaJackError( - parser.showError( - s"No value found in enumeration ${e.getClass.getName} for $en" - ) - ) - } - } else if (parser.nextIsString) { - val es = parser.expectString() - if (es == null) - null - else - Try(e.withName(es)) match { - case Success(u) => u - case Failure(u) => - parser.backspace() - throw new ScalaJackError( - parser.showError( - s"No value found in enumeration ${e.getClass.getName} for $es" - ) - ) - } - } else if (parser.peekForNull) - null - else - throw new ScalaJackError( - parser.showError(s"Expected a Number or String here") - ) - - def write[WIRE]( - t: e.Value, - writer: Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit = - t match { - case null => writer.writeNull(out) - case v if enumsAsInt => writer.writeInt(v.id, out) - case v => writer.writeString(v.toString, out) - } - - -case class ScalaEnumTypeAdapter[E <: scala.reflect.Enum]( - info: RType, - enumsAsInt: Boolean - ) extends TypeAdapter[E]: - - val scalaEnum = info.asInstanceOf[ScalaEnumInfo] - override def isStringish: Boolean = !enumsAsInt - - def read(parser: Parser): E = - if (parser.nextIsNumber) { - val en = parser.expectNumber().toInt - Try(scalaEnum.valueOf(en)) match { - case Success(u) => u.asInstanceOf[E] - case Failure(u) => - parser.backspace() - throw new ScalaJackError( - parser.showError( - s"No value found in enumeration ${info.name} for $en" - ) - ) - } - } else if (parser.nextIsString) { - val es = parser.expectString() - if (es == null) - null.asInstanceOf[E] - else - Try(scalaEnum.valueOf(es).asInstanceOf[E]) match { - case Success(u) => u - case Failure(u) => - parser.backspace() - throw new ScalaJackError( - parser.showError( - s"No value found in enumeration ${info.name} for $es" - ) - ) - } - } else if (parser.peekForNull) - null.asInstanceOf[E] - else - throw new ScalaJackError( - parser.showError(s"Expected a Number or String here") - ) - - def write[WIRE]( - t: E, - writer: Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit = - t match { - case null => writer.writeNull(out) - case _ if enumsAsInt => writer.writeInt(t.ordinal, out) - case _ => writer.writeString(t.toString, out) - } - - -case class JavaEnumTypeAdapter[E <: java.lang.Enum[_]]( - info: RType, - enumsAsInt: Boolean - ) extends TypeAdapter[E]: - - val javaEnum = info.asInstanceOf[JavaEnumInfo] - override def isStringish: Boolean = !enumsAsInt - - def read(parser: Parser): E = - if (parser.peekForNull) then - null.asInstanceOf[E] - else - val valueOf = info.infoClass.getDeclaredMethod("valueOf", classOf[String]) - try { - valueOf.invoke(info.infoClass, parser.expectString()).asInstanceOf[E] - } catch { - case ex: java.lang.reflect.InvocationTargetException => throw ex.getCause - case t: Throwable => throw t - } - - def write[WIRE]( - t: E, - writer: Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit = - t match { - case null => writer.writeNull(out) - case _ => writer.writeString(t.toString, out) - } \ No newline at end of file diff --git a/src_old/main/scala/co.blocke.scalajack/typeadapter/FallbackTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/FallbackTypeAdapter.scala deleted file mode 100644 index c914fcd3..00000000 --- a/src_old/main/scala/co.blocke.scalajack/typeadapter/FallbackTypeAdapter.scala +++ /dev/null @@ -1,30 +0,0 @@ -package co.blocke.scalajack -package typeadapter - -import model._ -import co.blocke.scala_reflection.RType - -import scala.collection.mutable -import scala.util.{ Failure, Success, Try } - -case class FallbackTypeAdapter[A, B]( - attemptedTypeAdapter: TypeAdapter[A], - orElseTypeAdapter: TypeAdapter[B] - ) extends TypeAdapter[A]: - - val info: RType = attemptedTypeAdapter.info - - def read(parser: Parser): A = - val mark = parser.mark() - Try(attemptedTypeAdapter.read(parser)) match { - case Success(a) => a - case Failure(_) => - parser.revertToMark(mark) - orElseTypeAdapter.read(parser).asInstanceOf[A] - } - - def write[WIRE]( - t: A, - writer: Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit = - attemptedTypeAdapter.write(t, writer, out) diff --git a/src_old/main/scala/co.blocke.scalajack/typeadapter/IntersectionTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/IntersectionTypeAdapter.scala deleted file mode 100644 index e756c96f..00000000 --- a/src_old/main/scala/co.blocke.scalajack/typeadapter/IntersectionTypeAdapter.scala +++ /dev/null @@ -1,55 +0,0 @@ -package co.blocke.scalajack -package typeadapter - -import model._ -import co.blocke.scala_reflection._ -import co.blocke.scala_reflection.info.IntersectionInfo -import co.blocke.scala_reflection.impl.Clazzes._ - -import scala.collection.mutable.Builder -import scala.util.{ Failure, Success, Try } - -object IntersectionTypeAdapterFactory extends TypeAdapterFactory: - - def matches(concrete: RType): Boolean = - concrete match { - case _: IntersectionInfo => true - case _ => false - } - - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[_] = - val intersectionInfo = concrete.asInstanceOf[IntersectionInfo] - val leftInfo = intersectionInfo.leftType - val rightInfo = intersectionInfo.rightType - - val leftTypeAdapter = taCache.typeAdapterOf(leftInfo) - val rightTypeAdapter = taCache.typeAdapterOf(rightInfo) - - IntersectionTypeAdapter( - concrete, - leftTypeAdapter, - rightTypeAdapter) - - -/** NOTE: This parsing "and" behavior only works for traits. The Dotty Intersection type is - * likely broader than that, but traits is the main use case here. It is unclear (from a - * serialization perspective) how you would combine "fields" from, say, a primitive type. - */ -case class IntersectionTypeAdapter[L, R]( - info: RType, - leftTypeAdapter: TypeAdapter[L], - rightTypeAdapter: TypeAdapter[R])(implicit taCache: TypeAdapterCache) - extends TypeAdapter[L & R]: - - val syntheticTA = taCache.typeAdapterOf[L] - override def isStringish: Boolean = leftTypeAdapter.isStringish && rightTypeAdapter.isStringish - override def maybeStringish: Boolean = !isStringish - - def read(parser: Parser): L & R = - syntheticTA.read(parser).asInstanceOf[L & R] - - def write[WIRE]( - t: L & R, - writer: Writer[WIRE], - out: Builder[WIRE, WIRE]): Unit = - syntheticTA.write(t.asInstanceOf[L], writer, out) diff --git a/src_old/main/scala/co.blocke.scalajack/typeadapter/JavaPrimitives.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/JavaPrimitives.scala deleted file mode 100644 index 96002309..00000000 --- a/src_old/main/scala/co.blocke.scalajack/typeadapter/JavaPrimitives.scala +++ /dev/null @@ -1,237 +0,0 @@ -package co.blocke.scalajack -package typeadapter - -import model._ -import co.blocke.scala_reflection._ -import co.blocke.scala_reflection.info.JavaClassInfo -import co.blocke.scala_reflection.impl.PrimitiveType - -import java.math.BigDecimal -import java.math.BigInteger - -import model._ -import scala.collection.mutable -import scala.language.implicitConversions - -object JavaBigDecimalTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[BigDecimal] with ScalarTypeAdapter[BigDecimal]: - def matches(concrete: RType): Boolean = - concrete match { - case j: JavaClassInfo if j.name == "java.math.BigDecimal" => true - case j => false - } - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[BigDecimal] = this - - val info = RType.of[java.math.BigDecimal] - def read(parser: Parser): BigDecimal = - parser.expectNumber(true) match { - case null => null - case bd => new BigDecimal(bd) - } - def write[WIRE](t: BigDecimal, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - t match { - case null => writer.writeNull(out) - case _ => writer.writeDecimal(t, out) - } - -object JavaBigIntegerTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[BigInteger] with ScalarTypeAdapter[BigInteger]: - def matches(concrete: RType): Boolean = - concrete match { - case j: JavaClassInfo if j.name == "java.math.BigInteger" => true - case _ => false - } - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[BigInteger] = this - - val info = RType.of[java.math.BigInteger] - def read(parser: Parser): BigInteger = - parser.expectNumber(true) match { - case null => null - case bd => new BigInteger(bd) - } - def write[WIRE](t: BigInteger, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = t match { - case null => writer.writeNull(out) - case _ => writer.writeBigInt(t, out) - } - - -object JavaBooleanTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[java.lang.Boolean] with ScalarTypeAdapter[java.lang.Boolean]: - def matches(concrete: RType): Boolean = concrete.infoClass == PrimitiveType.Java_Boolean.infoClass - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[java.lang.Boolean] = this - - val info = RType.of[java.lang.Boolean] - def read(parser: Parser): java.lang.Boolean = - if (parser.peekForNull) - null // Booleans are nullable in Java, but not in Scala - else - java.lang.Boolean.valueOf(parser.expectBoolean()) - def write[WIRE](t: java.lang.Boolean, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - t match { - case null => writer.writeNull(out) - case _ => writer.writeBoolean(t, out) - } - - -object JavaByteTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[java.lang.Byte] with ScalarTypeAdapter[java.lang.Byte]: - def matches(concrete: RType): Boolean = concrete.infoClass == PrimitiveType.Java_Byte.infoClass - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[java.lang.Byte] = this - - val info = RType.of[java.lang.Byte] - def read(parser: Parser): java.lang.Byte = - if (parser.peekForNull) - null // Bytes are nullable in Java, but not Scala - else - java.lang.Byte.valueOf(parser.expectNumber().toInt.toByte) - def write[WIRE](t: java.lang.Byte, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - t match { - case null => writer.writeNull(out) - case _ => writer.writeInt(t.toByte, out) - } - - -object JavaCharacterTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[java.lang.Character] with ScalarTypeAdapter[java.lang.Character]: - def matches(concrete: RType): Boolean = concrete.infoClass == PrimitiveType.Java_Char.infoClass - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[java.lang.Character] = this - - override def isStringish: Boolean = true - val info = RType.of[java.lang.Character] - def read(parser: Parser): java.lang.Character = - if (parser.peekForNull) - null - else - parser.expectString() match { - case "" => - parser.backspace() - throw new ScalaJackError( - parser.showError("Tried to read a Character but empty string found") - ) - case c => java.lang.Character.valueOf(c.toCharArray()(0)) - } - def write[WIRE](t: java.lang.Character, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - t match { - case null => writer.writeNull(out) - case _ => writer.writeString(t.toString, out) - } - - -object JavaDoubleTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[java.lang.Double] with ScalarTypeAdapter[java.lang.Double]: - def matches(concrete: RType): Boolean = concrete.infoClass == PrimitiveType.Java_Double.infoClass - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[java.lang.Double] = this - - val info = RType.of[java.lang.Double] - def read(parser: Parser): java.lang.Double = - if (parser.peekForNull) - null - else - java.lang.Double.valueOf(parser.expectNumber()) - def write[WIRE](t: java.lang.Double, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - t match { - case null => writer.writeNull(out) - case _ => writer.writeDouble(t, out) - } - - -object JavaFloatTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[java.lang.Float] with ScalarTypeAdapter[java.lang.Float]: - def matches(concrete: RType): Boolean = concrete.infoClass == PrimitiveType.Java_Float.infoClass - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[java.lang.Float] = this - - val info = RType.of[java.lang.Float] - def read(parser: Parser): java.lang.Float = - if (parser.peekForNull) - null - else - java.lang.Float.valueOf(parser.expectNumber()) - def write[WIRE](t: java.lang.Float, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - t match { - case null => writer.writeNull(out) - case _ => writer.writeDouble(util.FixFloat.capFloat(t), out) - } - - -object JavaIntegerTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[java.lang.Integer] with ScalarTypeAdapter[java.lang.Integer]: - def matches(concrete: RType): Boolean = concrete.infoClass == PrimitiveType.Java_Int.infoClass - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[java.lang.Integer] = this - - val info = RType.of[java.lang.Integer] - def read(parser: Parser): java.lang.Integer = - if (parser.peekForNull) - null - else - java.lang.Integer.valueOf(parser.expectNumber()) - def write[WIRE](t: java.lang.Integer, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - t match { - case null => writer.writeNull(out) - case _ => writer.writeInt(t, out) - } - - -object JavaLongTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[java.lang.Long] with ScalarTypeAdapter[java.lang.Long]: - def matches(concrete: RType): Boolean = concrete.infoClass == PrimitiveType.Java_Long.infoClass - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[java.lang.Long] = this - - val info = RType.of[java.lang.Long] - def read(parser: Parser): java.lang.Long = - if (parser.peekForNull) - null - else - java.lang.Long.valueOf(parser.expectNumber()) - def write[WIRE](t: java.lang.Long, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - t match { - case null => writer.writeNull(out) - case _ => writer.writeLong(t, out) - } - - -object JavaNumberTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[java.lang.Number] with ScalarTypeAdapter[java.lang.Number]: - def matches(concrete: RType): Boolean = concrete.infoClass == PrimitiveType.Java_Number.infoClass - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[java.lang.Number] = this - - val info = RType.of[java.lang.Number] - def read(parser: Parser): java.lang.Number = - if (parser.peekForNull) - null - else - scala.BigDecimal(parser.expectNumber()) match { - case d if d.isValidByte => java.lang.Byte.valueOf(d.toByteExact) - case d if d.isValidShort => java.lang.Short.valueOf(d.toShortExact) - case d if d.isValidInt => java.lang.Integer.valueOf(d.toIntExact) - case d if d.isValidLong => java.lang.Long.valueOf(d.toLongExact) - case d if d.isDecimalFloat => java.lang.Float.valueOf(d.toFloat) - case d if d.isDecimalDouble => java.lang.Double.valueOf(d.toDouble) - case d => d.bigDecimal - } - def write[WIRE](t: java.lang.Number, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - t match { - case null => writer.writeNull(out) - case t if t.isInstanceOf[java.lang.Integer] => - writer.writeInt(t.intValue, out) - case t if t.isInstanceOf[java.lang.Long] => - writer.writeLong(t.longValue, out) - case t if t.isInstanceOf[java.lang.Byte] => - writer.writeInt(t.byteValue, out) - case t if t.isInstanceOf[java.lang.Short] => - writer.writeInt(t.shortValue, out) - case t if t.isInstanceOf[java.lang.Float] => - writer.writeDouble(util.FixFloat.capFloat(t.floatValue), out) - case t if t.isInstanceOf[java.lang.Double] => - writer.writeDouble(t.doubleValue, out) - case t if t.isInstanceOf[java.math.BigInteger] => - writer.writeBigInt(BigInt(t.asInstanceOf[BigInteger]), out) - case t if t.isInstanceOf[java.math.BigDecimal] => - writer.writeDecimal(scala.BigDecimal(t.asInstanceOf[BigDecimal]), out) - } - - -object JavaShortTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[java.lang.Short] with ScalarTypeAdapter[java.lang.Short]: - def matches(concrete: RType): Boolean = concrete.infoClass == PrimitiveType.Java_Short.infoClass - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[java.lang.Short] = this - - val info = RType.of[java.lang.Short] - def read(parser: Parser): java.lang.Short = - if (parser.peekForNull) - null - else - java.lang.Short.valueOf(parser.expectNumber()) - def write[WIRE](t: java.lang.Short, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - t match { - case null => writer.writeNull(out) - case _ => writer.writeInt(t.intValue, out) - } diff --git a/src_old/main/scala/co.blocke.scalajack/typeadapter/MaybeStringWrapTypeAadapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/MaybeStringWrapTypeAadapter.scala deleted file mode 100644 index 72a577b8..00000000 --- a/src_old/main/scala/co.blocke.scalajack/typeadapter/MaybeStringWrapTypeAadapter.scala +++ /dev/null @@ -1,49 +0,0 @@ -package co.blocke.scalajack -package typeadapter - -import model._ -import co.blocke.scala_reflection._ -import co.blocke.scala_reflection.info._ -import co.blocke.scala_reflection.impl.Clazzes._ - -import scala.collection.mutable - -// A TypeAdapter for a type T, which is wrapped in a String, a.k.a. "stringified". -// This is used for JSON Map keys, which must be strings. -case class MaybeStringWrapTypeAdapter[T]( - jackFlavor: JackFlavor[_], - wrappedTypeAdapter: TypeAdapter[T], - emptyStringOk: Boolean = true - ) extends TypeAdapter[T] { - - private val javaEnumClazz = Class.forName("java.util.Enumeration") - - override def isStringish: Boolean = true - val info: RType = wrappedTypeAdapter.info - - def read(parser: Parser): T = - parser.expectString() match { - case null => null.asInstanceOf[T] - case s if s.isEmpty && !emptyStringOk => - parser.backspace() - throw new ScalaJackError( - parser.showError(s"Expected a ${wrappedTypeAdapter.info.name} here") - ) - case s => - wrappedTypeAdapter.read(parser.subParser(s.asInstanceOf[parser.WIRE])) - } - - def write[WIRE]( - t: T, - writer: Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit = - t match { - case null => writer.writeNull(out) - case t if t.getClass <:< classOf[String] => writer.writeString(t.toString, out) - case e if e.getClass.getName =="scala.Enumeration$Val" && !jackFlavor.enumsAsInt => writer.writeString(t.toString, out) - case _: scala.reflect.Enum if !jackFlavor.enumsAsInt => writer.writeString(t.toString, out) - case t if t.getClass <:< javaEnumClazz && !jackFlavor.enumsAsInt => writer.writeString(t.toString, out) - case _ => - jackFlavor.stringWrapTypeAdapterFactory(wrappedTypeAdapter).write(t, writer, out) - } -} diff --git a/src_old/main/scala/co.blocke.scalajack/typeadapter/OptionTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/OptionTypeAdapter.scala deleted file mode 100644 index d4de70c0..00000000 --- a/src_old/main/scala/co.blocke.scalajack/typeadapter/OptionTypeAdapter.scala +++ /dev/null @@ -1,118 +0,0 @@ -package co.blocke.scalajack -package typeadapter - -import model._ - -import scala.collection.mutable -import co.blocke.scala_reflection._ -import co.blocke.scala_reflection.info._ -import java.util.Optional - -/* - O, the exquisite pain of mapping Option (None) to something in JSON! - Our assumptions (which may be different from earlier versions of ScalaJack: - - * Normal: None is just missing. Doesn't read or write anything at all. Classic example is a class field value--it's - just "missing" from the JSON and understood to be None. - - * Map key fields: Element is dropped from Map, like List element behavior - - * Map value fields: Element is dropped from Map, like List element behavior - - * List elements: Normal, i.e. read/write nothing. None elements just disappear. NOTE this does mean that a read/render - cycle may not yield the same object, which is generally breaking a ScalaJack core behavior goal - - * Tuple elements: Place needs to be preserved in a Tuple, so None becomes JSON null. Really hate this option, but JSON - doesn't leave many choices here. - */ - -object OptionTypeAdapterFactory extends TypeAdapterFactory: - def matches(concrete: RType): Boolean = - concrete match { - case _: OptionInfo => true - case _ => false - } - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[_] = - val optiBase = concrete.asInstanceOf[OptionInfo] - val wrapped = optiBase.optionParamType match { - case c: TypeSymbolInfo => throw new ScalaJackError(s"Unexpected non-concrete type in option: ${c.name}") - case c => taCache.typeAdapterOf(c) - } - concrete match { - case opti: ScalaOptionInfo => OptionTypeAdapter(concrete, wrapped) - case jopti: JavaOptionalInfo => JavaOptionalTypeAdapter(concrete, wrapped) - } - - -case class OptionTypeAdapter[E]( - info: RType, - valueTypeAdapter: TypeAdapter[E], - nullIsNone: Boolean = false - ) extends TypeAdapter[Option[E]]: - - override def defaultValue: Option[Option[E]] = Some(None) - - override def isStringish: Boolean = valueTypeAdapter.isStringish - override def maybeStringish: Boolean = !valueTypeAdapter.isStringish - - def read(parser: Parser): Option[E] = - // We have to do some voodoo here and peek ahead for Null. Some types, e.g. Int, aren't nullable, - // but Option[Int] is nullable, so we can't trust the valueTypeAdapter to catch and handle null in - // these cases. - parser.peekForNull match { - case true if nullIsNone => None - case true => null - case _ => Some(valueTypeAdapter.read(parser)) - } - - def write[WIRE]( - t: Option[E], - writer: Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit = - t match { - case null => writer.writeNull(out) - case Some(e) => valueTypeAdapter.write(e, writer, out) - case None if nullIsNone => writer.writeNull(out) - case None => - } - - def convertNullToNone(): OptionTypeAdapter[E] = this.copy(nullIsNone = true) - - - -case class JavaOptionalTypeAdapter[E]( - info: RType, - valueTypeAdapter: TypeAdapter[E], - nullIsNone: Boolean = false - ) extends TypeAdapter[Optional[E]]: - - val empty = Optional.empty[E]() - override def defaultValue: Option[Optional[E]] = Some(empty) - - override def isStringish: Boolean = valueTypeAdapter.isStringish - override def maybeStringish: Boolean = !valueTypeAdapter.isStringish - - def read(parser: Parser): Optional[E] = - // We have to do some voodoo here and peek ahead for Null. Some types, e.g. Int, aren't nullable, - // but Option[Int] is nullable, so we can't trust the valueTypeAdapter to catch and handle null in - // these cases. - parser.peekForNull match { - case true if nullIsNone => empty - case true => null - case _ => Optional.of[E](valueTypeAdapter.read(parser)) - } - - def write[WIRE]( - t: Optional[E], - writer: Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit = - if t == null then - writer.writeNull(out) - else - if t.isPresent then - valueTypeAdapter.write(t.get, writer, out) - else if nullIsNone then - writer.writeNull(out) - // else write nothing - - def convertNullToNone(): JavaOptionalTypeAdapter[E] = this.copy(nullIsNone = true) \ No newline at end of file diff --git a/src_old/main/scala/co.blocke.scalajack/typeadapter/PermissiveJavaPrimitives.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/PermissiveJavaPrimitives.scala deleted file mode 100644 index e768210c..00000000 --- a/src_old/main/scala/co.blocke.scalajack/typeadapter/PermissiveJavaPrimitives.scala +++ /dev/null @@ -1,191 +0,0 @@ -package co.blocke.scalajack -package typeadapter - -import model._ -import co.blocke.scala_reflection.impl.Clazzes._ -import co.blocke.scala_reflection.impl.PrimitiveType -import co.blocke.scala_reflection.info._ -import co.blocke.scala_reflection._ -import scala.collection.mutable - -import java.math.BigDecimal -import java.math.BigInteger - - -object PermissiveJavaBigDecimalTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[BigDecimal] with ScalarTypeAdapter[BigDecimal]: - def matches(concrete: RType): Boolean = - concrete match { - case u: JavaClassInfo if u.infoClass.getName == "java.math.BigDecimal" => true - case _ => false - } - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[BigDecimal] = this - - val info = RType.of[scala.math.BigDecimal] - def read(parser: Parser): BigDecimal = - if (parser.nextIsString) - parser.jackFlavor - .stringWrapTypeAdapterFactory(JavaBigDecimalTypeAdapterFactory, emptyStringOk = false) - .read(parser) - else - JavaBigDecimalTypeAdapterFactory.read(parser) - - def write[WIRE](t: BigDecimal, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - JavaBigDecimalTypeAdapterFactory.write(t, writer, out) - - -object PermissiveJavaBigIntegerTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[BigInteger] with ScalarTypeAdapter[BigInteger]: - def matches(concrete: RType): Boolean = - concrete match { - case u: JavaClassInfo if u.infoClass.getName == "java.math.BigInteger" => true - case _ => false - } - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[BigInteger] = this - - val info = RType.of[java.math.BigInteger] - def read(parser: Parser): BigInteger = - if (parser.nextIsString) - parser.jackFlavor - .stringWrapTypeAdapterFactory(JavaBigIntegerTypeAdapterFactory, emptyStringOk = false) - .read(parser) - else - JavaBigIntegerTypeAdapterFactory.read(parser) - - def write[WIRE](t: BigInteger, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - JavaBigIntegerTypeAdapterFactory.write(t, writer, out) - - -object PermissiveJavaBooleanTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[java.lang.Boolean] with ScalarTypeAdapter[java.lang.Boolean]: - def matches(concrete: RType): Boolean = concrete.infoClass == PrimitiveType.Java_Boolean.infoClass - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[java.lang.Boolean] = this - - val info = RType.of[java.lang.Boolean] - def read(parser: Parser): java.lang.Boolean = - if (parser.nextIsString) - parser.jackFlavor - .stringWrapTypeAdapterFactory(JavaBooleanTypeAdapterFactory, emptyStringOk = false) - .read(parser) - else - JavaBooleanTypeAdapterFactory.read(parser) - - def write[WIRE](t: java.lang.Boolean, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - JavaBooleanTypeAdapterFactory.write(t, writer, out) - - -object PermissiveJavaByteTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[java.lang.Byte] with ScalarTypeAdapter[java.lang.Byte]: - def matches(concrete: RType): Boolean = concrete.infoClass == PrimitiveType.Java_Byte.infoClass - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[java.lang.Byte] = this - - val info = RType.of[java.lang.Byte] - def read(parser: Parser): java.lang.Byte = - if (parser.nextIsString) - parser.jackFlavor - .stringWrapTypeAdapterFactory(JavaByteTypeAdapterFactory, emptyStringOk = false) - .read(parser) - else - JavaByteTypeAdapterFactory.read(parser) - - def write[WIRE](t: java.lang.Byte, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - JavaByteTypeAdapterFactory.write(t, writer, out) - - -object PermissiveJavaDoubleTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[java.lang.Double] with ScalarTypeAdapter[java.lang.Double]: - def matches(concrete: RType): Boolean = concrete.infoClass == PrimitiveType.Java_Double.infoClass - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[java.lang.Double] = this - - val info = RType.of[java.lang.Double] - def read(parser: Parser): java.lang.Double = - if (parser.nextIsString) - parser.jackFlavor - .stringWrapTypeAdapterFactory(JavaDoubleTypeAdapterFactory, emptyStringOk = false) - .read(parser) - else - JavaDoubleTypeAdapterFactory.read(parser) - - def write[WIRE](t: java.lang.Double, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - JavaDoubleTypeAdapterFactory.write(t, writer, out) - - -object PermissiveJavaFloatTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[java.lang.Float] with ScalarTypeAdapter[java.lang.Float]: - def matches(concrete: RType): Boolean = concrete.infoClass == PrimitiveType.Java_Float.infoClass - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[java.lang.Float] = this - - val info = RType.of[java.lang.Float] - def read(parser: Parser): java.lang.Float = - if (parser.nextIsString) - parser.jackFlavor - .stringWrapTypeAdapterFactory(JavaFloatTypeAdapterFactory, emptyStringOk = false) - .read(parser) - else - JavaFloatTypeAdapterFactory.read(parser) - - def write[WIRE](t: java.lang.Float, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - JavaFloatTypeAdapterFactory.write(t, writer, out) - - -object PermissiveJavaIntTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[java.lang.Integer] with ScalarTypeAdapter[java.lang.Integer]: - def matches(concrete: RType): Boolean = concrete.infoClass == PrimitiveType.Java_Int.infoClass - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[java.lang.Integer] = this - - val info = RType.of[java.lang.Integer] - def read(parser: Parser): java.lang.Integer = - if (parser.nextIsString) - parser.jackFlavor - .stringWrapTypeAdapterFactory(JavaIntegerTypeAdapterFactory, emptyStringOk = false) - .read(parser) - else - JavaIntegerTypeAdapterFactory.read(parser) - - def write[WIRE](t: java.lang.Integer, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - JavaIntegerTypeAdapterFactory.write(t, writer, out) - - -object PermissiveJavaLongTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[java.lang.Long] with ScalarTypeAdapter[java.lang.Long]: - def matches(concrete: RType): Boolean = concrete.infoClass == PrimitiveType.Java_Long.infoClass - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[java.lang.Long] = this - - val info = RType.of[java.lang.Long] - def read(parser: Parser): java.lang.Long = - if (parser.nextIsString) - parser.jackFlavor - .stringWrapTypeAdapterFactory(JavaLongTypeAdapterFactory, emptyStringOk = false) - .read(parser) - else - JavaLongTypeAdapterFactory.read(parser) - - def write[WIRE](t: java.lang.Long, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - JavaLongTypeAdapterFactory.write(t, writer, out) - - -object PermissiveJavaNumberTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[java.lang.Number] with ScalarTypeAdapter[java.lang.Number]: - def matches(concrete: RType): Boolean = concrete.infoClass == PrimitiveType.Java_Number.infoClass - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[java.lang.Number] = this - - val info = RType.of[java.lang.Number] - def read(parser: Parser): java.lang.Number = - if (parser.nextIsString) - parser.jackFlavor - .stringWrapTypeAdapterFactory(JavaNumberTypeAdapterFactory, emptyStringOk = false) - .read(parser) - else - JavaNumberTypeAdapterFactory.read(parser) - - def write[WIRE](t: java.lang.Number, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - JavaNumberTypeAdapterFactory.write(t, writer, out) - - -object PermissiveJavaShortTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[java.lang.Short] with ScalarTypeAdapter[java.lang.Short]: - def matches(concrete: RType): Boolean = concrete.infoClass == PrimitiveType.Java_Short.infoClass - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[java.lang.Short] = this - - val info = RType.of[java.lang.Short] - def read(parser: Parser): java.lang.Short = - if (parser.nextIsString) - parser.jackFlavor - .stringWrapTypeAdapterFactory(JavaShortTypeAdapterFactory, emptyStringOk = false) - .read(parser) - else - JavaShortTypeAdapterFactory.read(parser) - - def write[WIRE](t: java.lang.Short, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - JavaShortTypeAdapterFactory.write(t, writer, out) - \ No newline at end of file diff --git a/src_old/main/scala/co.blocke.scalajack/typeadapter/PermissiveScalaPrimitives.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/PermissiveScalaPrimitives.scala deleted file mode 100644 index c0e65efe..00000000 --- a/src_old/main/scala/co.blocke.scalajack/typeadapter/PermissiveScalaPrimitives.scala +++ /dev/null @@ -1,169 +0,0 @@ -package co.blocke.scalajack -package typeadapter - -import model._ -import co.blocke.scala_reflection.impl.Clazzes._ -import co.blocke.scala_reflection.info._ -import co.blocke.scala_reflection.impl.PrimitiveType -import co.blocke.scala_reflection._ -import scala.collection.mutable - -object PermissiveBigDecimalTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[BigDecimal] with ScalarTypeAdapter[BigDecimal]: - def matches(concrete: RType): Boolean = - concrete match { - case u: Scala2Info if u.infoClass.getName == "scala.math.BigDecimal" => true - case _ => false - } - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[BigDecimal] = this - - val info = RType.of[scala.math.BigDecimal] - def read(parser: Parser): BigDecimal = - if (parser.nextIsString) - parser.jackFlavor - .stringWrapTypeAdapterFactory(BigDecimalTypeAdapterFactory, emptyStringOk = false) - .read(parser) - else - BigDecimalTypeAdapterFactory.read(parser) - - def write[WIRE](t: BigDecimal, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - BigDecimalTypeAdapterFactory.write(t, writer, out) - - -object PermissiveBigIntTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[BigInt] with ScalarTypeAdapter[BigInt]: - def matches(concrete: RType): Boolean = - concrete match { - case u: Scala2Info if u.infoClass.getName == "scala.math.BigInt" => true - case _ => false - } - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[BigInt] = this - - val info = RType.of[scala.math.BigInt] - def read(parser: Parser): BigInt = - if (parser.nextIsString) - parser.jackFlavor - .stringWrapTypeAdapterFactory(BigIntTypeAdapterFactory, emptyStringOk = false) - .read(parser) - else - BigIntTypeAdapterFactory.read(parser) - - def write[WIRE](t: BigInt, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - BigIntTypeAdapterFactory.write(t, writer, out) - - -object PermissiveBooleanTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[Boolean] with ScalarTypeAdapter[Boolean]: - def matches(concrete: RType): Boolean = concrete.infoClass == PrimitiveType.Scala_Boolean.infoClass - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[Boolean] = this - - val info = RType.of[Boolean] - def read(parser: Parser): Boolean = - if (parser.nextIsString) - parser.jackFlavor - .stringWrapTypeAdapterFactory(BooleanTypeAdapterFactory, emptyStringOk = false) - .read(parser) - else - BooleanTypeAdapterFactory.read(parser) - - def write[WIRE](t: Boolean, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - BooleanTypeAdapterFactory.write(t, writer, out) - - -object PermissiveByteTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[Byte] with ScalarTypeAdapter[Byte]: - def matches(concrete: RType): Boolean = concrete.infoClass == PrimitiveType.Scala_Byte.infoClass - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[Byte] = this - - val info = RType.of[Byte] - def read(parser: Parser): Byte = - if (parser.nextIsString) - parser.jackFlavor - .stringWrapTypeAdapterFactory(ByteTypeAdapterFactory, emptyStringOk = false) - .read(parser) - else - ByteTypeAdapterFactory.read(parser) - - def write[WIRE](t: Byte, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - ByteTypeAdapterFactory.write(t, writer, out) - - -object PermissiveDoubleTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[Double] with ScalarTypeAdapter[Double]: - def matches(concrete: RType): Boolean = concrete.infoClass == PrimitiveType.Scala_Double.infoClass - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[Double] = this - - val info = RType.of[Double] - def read(parser: Parser): Double = - if (parser.nextIsString) - parser.jackFlavor - .stringWrapTypeAdapterFactory(DoubleTypeAdapterFactory, emptyStringOk = false) - .read(parser) - else - DoubleTypeAdapterFactory.read(parser) - - def write[WIRE](t: Double, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - DoubleTypeAdapterFactory.write(t, writer, out) - - -object PermissiveFloatTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[Float] with ScalarTypeAdapter[Float]: - def matches(concrete: RType): Boolean = concrete.infoClass == PrimitiveType.Scala_Float.infoClass - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[Float] = this - - val info = RType.of[Float] - def read(parser: Parser): Float = - if (parser.nextIsString) - parser.jackFlavor - .stringWrapTypeAdapterFactory(FloatTypeAdapterFactory, emptyStringOk = false) - .read(parser) - else - FloatTypeAdapterFactory.read(parser) - - def write[WIRE](t: Float, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - FloatTypeAdapterFactory.write(t, writer, out) - - -object PermissiveIntTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[Int] with ScalarTypeAdapter[Int]: - def matches(concrete: RType): Boolean = concrete.infoClass == PrimitiveType.Scala_Int.infoClass - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[Int] = this - - val info = RType.of[Int] - def read(parser: Parser): Int = - if (parser.nextIsString) - parser.jackFlavor - .stringWrapTypeAdapterFactory(IntTypeAdapterFactory, emptyStringOk = false) - .read(parser) - else - IntTypeAdapterFactory.read(parser) - - def write[WIRE](t: Int, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - IntTypeAdapterFactory.write(t, writer, out) - - -object PermissiveLongTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[Long] with ScalarTypeAdapter[Long]: - def matches(concrete: RType): Boolean = concrete.infoClass == PrimitiveType.Scala_Long.infoClass - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[Long] = this - - val info = RType.of[Long] - def read(parser: Parser): Long = - if (parser.nextIsString) - parser.jackFlavor - .stringWrapTypeAdapterFactory(LongTypeAdapterFactory, emptyStringOk = false) - .read(parser) - else - LongTypeAdapterFactory.read(parser) - - def write[WIRE](t: Long, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - LongTypeAdapterFactory.write(t, writer, out) - - -object PermissiveShortTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[Short] with ScalarTypeAdapter[Short]: - def matches(concrete: RType): Boolean = concrete.infoClass == PrimitiveType.Scala_Short.infoClass - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[Short] = this - - val info = RType.of[Short] - def read(parser: Parser): Short = - if (parser.nextIsString) - parser.jackFlavor - .stringWrapTypeAdapterFactory(ShortTypeAdapterFactory, emptyStringOk = false) - .read(parser) - else - ShortTypeAdapterFactory.read(parser) - - def write[WIRE](t: Short, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - ShortTypeAdapterFactory.write(t, writer, out) diff --git a/src_old/main/scala/co.blocke.scalajack/typeadapter/ScalaPrimitives.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/ScalaPrimitives.scala deleted file mode 100644 index 03817884..00000000 --- a/src_old/main/scala/co.blocke.scalajack/typeadapter/ScalaPrimitives.scala +++ /dev/null @@ -1,236 +0,0 @@ -package co.blocke.scalajack -package typeadapter - -import model._ - -import co.blocke.scala_reflection.impl.Clazzes._ -import co.blocke.scala_reflection.impl.PrimitiveType -import co.blocke.scala_reflection.info._ -import co.blocke.scala_reflection._ - -import org.apache.commons.codec.binary.Base64 -import scala.collection.mutable -import scala.language.implicitConversions - - -object BigDecimalTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[BigDecimal] with ScalarTypeAdapter[BigDecimal]: - def matches(concrete: RType): Boolean = - concrete match { - case u: Scala2Info if u.infoClass.getName == "scala.math.BigDecimal" => true - case _ => false - } - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[BigDecimal] = this - - val info = RType.of[scala.math.BigDecimal] - def read(parser: Parser): BigDecimal = - parser.expectNumber(true) match { - case null => null - case bd => BigDecimal(bd) - } - def write[WIRE](t: BigDecimal, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - writer.writeDecimal(t, out) - - -object BigIntTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[BigInt] with ScalarTypeAdapter[BigInt]: - def matches(concrete: RType): Boolean = - concrete match { - case u: Scala2Info if u.infoClass.getName == "scala.math.BigInt" => true - case _ => false - } - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[BigInt] = this - - val info = RType.of[scala.math.BigInt] - def read(parser: Parser): BigInt = - parser.expectNumber(true) match { - case null => null - case bd => BigInt(bd) - } - def write[WIRE](t: BigInt, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = t match { - case null => writer.writeNull(out) - case _ => writer.writeBigInt(t, out) - } - - -object BinaryTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[Array[Byte]] with ScalarTypeAdapter[Array[Byte]]: - def matches(concrete: RType): Boolean = - concrete match { - case ArrayInfo("[B", ic) if ic.infoClass == PrimitiveType.Scala_Byte.infoClass => true - case c => false - } - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[Array[Byte]] = this - - val info = RType.of[Array[Byte]] - def read(parser: Parser): Array[Byte] = - parser.expectString() match { - case null => null - case s: String => Base64.decodeBase64(s) - } - - def write[WIRE](t: Array[Byte], writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - t match { - case null => writer.writeNull(out) - case _ => writer.writeString(Base64.encodeBase64String(t), out) - } - - -object BooleanTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[Boolean] with ScalarTypeAdapter[Boolean]: - def matches(concrete: RType): Boolean = concrete.infoClass == PrimitiveType.Scala_Boolean.infoClass - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[Boolean] = this - - val info = RType.of[Boolean] - def read(parser: Parser): Boolean = parser.expectBoolean() - def write[WIRE](t: Boolean, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - writer.writeBoolean(t, out) - - -object ByteTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[Byte] with ScalarTypeAdapter[Byte]: - def matches(concrete: RType): Boolean = concrete.infoClass == PrimitiveType.Scala_Byte.infoClass - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[Byte] = this - - val info = RType.of[Byte] - def read(parser: Parser): Byte = - Option(parser.expectNumber()) - .flatMap(_.toByteOption) - .getOrElse { - parser.backspace() - throw new ScalaJackError( - parser.showError("Cannot parse an Byte from value") - ) - } - def write[WIRE](t: Byte, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - writer.writeInt(t, out) - - -object CharTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[Char] with ScalarTypeAdapter[Char]: - def matches(concrete: RType): Boolean = concrete.infoClass == PrimitiveType.Scala_Char.infoClass - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[Char] = this - - override def isStringish: Boolean = true - val info = RType.of[Char] - def read(parser: Parser): Char = - parser.expectString() match { - case null => - parser.backspace() - throw new ScalaJackError( - parser.showError("A Char typed value cannot be null") - ) - case "" => - parser.backspace() - throw new ScalaJackError( - parser.showError("Tried to read a Char but empty string found") - ) - case s => s.charAt(0) - } - def write[WIRE](t: Char, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - writer.writeString(t.toString, out) - - -object DoubleTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[Double] with ScalarTypeAdapter[Double]: - def matches(concrete: RType): Boolean = concrete.infoClass == PrimitiveType.Scala_Double.infoClass - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[Double] = this - - val info = RType.of[Double] - def read(parser: Parser): Double = - Option( - parser - .expectNumber()) - .flatMap(_.toDoubleOption) - .getOrElse { - parser.backspace() - throw new ScalaJackError( - parser.showError("Cannot parse an Double from value") - ) - } - def write[WIRE](t: Double, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - writer.writeDouble(t, out) - - -object FloatTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[Float] with ScalarTypeAdapter[Float]: - def matches(concrete: RType): Boolean = concrete.infoClass == PrimitiveType.Scala_Float.infoClass - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[Float] = this - - val info = RType.of[Float] - def read(parser: Parser): Float = - Option( - parser - .expectNumber()) - .flatMap(_.toFloatOption) - .getOrElse { - parser.backspace() - throw new ScalaJackError( - parser.showError("Cannot parse an Float from value") - ) - } - def write[WIRE](t: Float, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - writer.writeDouble(util.FixFloat.capFloat(t), out) - - -object IntTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[Int] with ScalarTypeAdapter[Int]: - def matches(concrete: RType): Boolean = concrete.infoClass == PrimitiveType.Scala_Int.infoClass - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[Int] = this - - val info = RType.of[Int] - def read(parser: Parser): Int = - Option( - parser - .expectNumber()) - .flatMap(_.toIntOption) - .getOrElse { - parser.backspace() - throw new ScalaJackError( - parser.showError("Cannot parse an Int from value") - ) - } - def write[WIRE](t: Int, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - writer.writeInt(t, out) - - -object LongTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[Long] with ScalarTypeAdapter[Long]: - def matches(concrete: RType): Boolean = concrete.infoClass == PrimitiveType.Scala_Long.infoClass - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[Long] = this - - val info = RType.of[Long] - def read(parser: Parser): Long = - Option( - parser - .expectNumber()) - .flatMap(_.toLongOption) - .getOrElse { - parser.backspace() - throw new ScalaJackError( - parser.showError("Cannot parse an Long from value") - ) - } - def write[WIRE](t: Long, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - writer.writeLong(t, out) - - -object ShortTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[Short] with ScalarTypeAdapter[Short]: - def matches(concrete: RType): Boolean = concrete.infoClass == PrimitiveType.Scala_Short.infoClass - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[Short] = this - - val info = RType.of[Short] - def read(parser: Parser): Short = - Option( - parser - .expectNumber()) - .flatMap(_.toShortOption) - .getOrElse { - parser.backspace() - throw new ScalaJackError( - parser.showError("Cannot parse an Short from value") - ) - } - def write[WIRE](t: Short, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - writer.writeInt(t, out) - - -object StringTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[String] with ScalarTypeAdapter[String]: - def matches(concrete: RType): Boolean = concrete.infoClass == PrimitiveType.Scala_String.infoClass - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[String] = this - - override def isStringish: Boolean = true - val info = RType.of[String] - def read(parser: Parser): String = parser.expectString() - def write[WIRE](t: String, writer: Writer[WIRE], out: mutable.Builder[WIRE, WIRE]): Unit = - writer.writeString(t, out) diff --git a/src_old/main/scala/co.blocke.scalajack/typeadapter/SealedTraitTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/SealedTraitTypeAdapter.scala deleted file mode 100644 index ffaa78b4..00000000 --- a/src_old/main/scala/co.blocke.scalajack/typeadapter/SealedTraitTypeAdapter.scala +++ /dev/null @@ -1,118 +0,0 @@ -package co.blocke.scalajack -package typeadapter - -import model._ - -import scala.collection.mutable -import co.blocke.scala_reflection._ -import co.blocke.scala_reflection.impl.Clazzes._ -import co.blocke.scala_reflection.info._ - -object SealedTraitTypeAdapterFactory extends TypeAdapterFactory: - def matches(concrete: RType): Boolean = - concrete match { - case _: SealedTraitInfo => true - case _ => false - } - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[_] = - if concrete.asInstanceOf[SealedTraitInfo].children.head.isInstanceOf[ObjectInfo] then - CaseObjectTypeAdapter( - concrete, - concrete.asInstanceOf[SealedTraitInfo].children.map(_.asInstanceOf[ObjectInfo].infoClass.getSimpleName).toList) - else - val typeAdapters = concrete.asInstanceOf[SealedTraitInfo].children.map(c => c -> taCache.typeAdapterOf(c)).toMap - SealedTraitTypeAdapter(taCache.jackFlavor, concrete, typeAdapters) - - -case class SealedTraitTypeAdapter[T]( - jackFlavor: JackFlavor[_], - info: RType, - typeAdapters: Map[RType, TypeAdapter[_]] - ) extends TypeAdapter[T]: - - val sealedInfo = info.asInstanceOf[SealedTraitInfo] - - def read(parser: Parser): T = - val savedReader = parser.mark() - if (parser.peekForNull) - null.asInstanceOf[T] - else { - val readFieldNames = parser.expectMap[String, Any, Map[String, Any]]( - jackFlavor.stringTypeAdapter, - jackFlavor.anyTypeAdapter, - Map.newBuilder[String,Any] - ).keySet - sealedInfo.children.filter( - _.asInstanceOf[ScalaCaseClassInfo].fields.map(_.name).toSet.intersect(readFieldNames).size == readFieldNames.size - ) match { - case setOfOne if setOfOne.size == 1 => - parser.revertToMark(savedReader) - typeAdapters(setOfOne.head).read(parser).asInstanceOf[T] - - case emptySet if emptySet.isEmpty => - parser.backspace() - throw new ScalaJackError( - parser.showError( - s"No sub-classes of ${info.name} match field names $readFieldNames" - ) - ) - - case _ => - // $COVERAGE-OFF$Should be impossible--here for safety. Something to trigger this would be ambiguous and would then be detected as a WrappedSealedTraitTypeAdapter, not here. - parser.backspace() - throw new ScalaJackError( - parser.showError( - s"Multiple sub-classes of ${info.name} match field names $readFieldNames" - ) - ) - // $COVERAGE-ON$ - } - } - - def write[WIRE]( - t: T, - writer: Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit = - t match { - case null => writer.writeString(null, out) - case _ => - sealedInfo.children.find(f => t.getClass <:< f.infoClass) match { - case Some(implementation) => typeAdapters(implementation).asInstanceOf[TypeAdapter[T]]write(t, writer, out) - // $COVERAGE-OFF$Should be impossible, but including here for safety. Can't think of how to actaully trigger this for testing. - case None => - throw new IllegalStateException( - s"Given object ($t) doesn't seem to be a sealed trait." - ) - // $COVERAGE-ON$ - } - } - -case class CaseObjectTypeAdapter[T]( - info: RType, - values: List[String] - ) extends TypeAdapter[T]: - - val sealedInfo = info.asInstanceOf[SealedTraitInfo] - - def read(parser: Parser): T = - parser.expectString() match { - case null => null.asInstanceOf[T] - case s: String if values.contains(s) => - val simpleNameLen = info.infoClass.getSimpleName.length+1 - val clazz = Class.forName(info.infoClass.getName.dropRight(simpleNameLen) + "." + s + "$") - val objInstance = clazz.getField("MODULE$").get(null).asInstanceOf[T] - objInstance - case x => - parser.backspace() - throw new ScalaJackError(parser.showError(s"Expected a valid subclass of ${info.name} but got $x") - ) - } - - def write[WIRE]( - t: T, - writer: Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit = - t match { - case null => writer.writeString(null, out) - case _ => writer.writeString(t.toString, out) - } \ No newline at end of file diff --git a/src_old/main/scala/co.blocke.scalajack/typeadapter/StringWrapTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/StringWrapTypeAdapter.scala deleted file mode 100644 index d8fceb1f..00000000 --- a/src_old/main/scala/co.blocke.scalajack/typeadapter/StringWrapTypeAdapter.scala +++ /dev/null @@ -1,44 +0,0 @@ -package co.blocke.scalajack -package typeadapter - -import model._ -import co.blocke.scala_reflection._ -import co.blocke.scala_reflection.info._ -import co.blocke.scala_reflection.impl.Clazzes._ - -import scala.collection.mutable - -// A TypeAdapter for a type T, which is wrapped in a String, a.k.a. "stringified". -// This is used for JSON Map keys, which must be strings. -case class StringWrapTypeAdapter[T]( - wrappedTypeAdapter: TypeAdapter[T], - emptyStringOk: Boolean = true - ) extends TypeAdapter[T] { - - override def isStringish: Boolean = true - val info: RType = wrappedTypeAdapter.info - - def read(parser: Parser): T = - parser.expectString() match { - case null => null.asInstanceOf[T] - case s if s.isEmpty && !emptyStringOk => - parser.backspace() - throw new ScalaJackError( - parser.showError(s"Expected a ${wrappedTypeAdapter.info.name} here") - ) - case s => - wrappedTypeAdapter.read(parser.subParser(s.asInstanceOf[parser.WIRE])) - } - - def write[WIRE]( - t: T, - writer: Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit = - val stringBuilder = co.blocke.scalajack.model.StringBuilder() - wrappedTypeAdapter.write( - t, - writer, - stringBuilder.asInstanceOf[mutable.Builder[Any, WIRE]] - ) - writer.writeString(stringBuilder.result(), out) -} diff --git a/src_old/main/scala/co.blocke.scalajack/typeadapter/TimePrimitives.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/TimePrimitives.scala deleted file mode 100644 index 6d5cc430..00000000 --- a/src_old/main/scala/co.blocke.scalajack/typeadapter/TimePrimitives.scala +++ /dev/null @@ -1,323 +0,0 @@ -package co.blocke.scalajack -package typeadapter - -import co.blocke.scala_reflection.impl.Clazzes._ -import co.blocke.scala_reflection.info._ -import co.blocke.scala_reflection._ - -import scala.collection.mutable -import scala.util.{ Failure, Success, Try } -import java.time._ -import java.time.format.DateTimeFormatter._ - -import model._ - - object DurationTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[Duration]: - def matches(concrete: RType): Boolean = - concrete match { - case u: JavaClassInfo if u.infoClass.getName == "java.time.Duration" => true - case _ => false - } - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[Duration] = this - val info = RType.of[Duration] - override def isStringish: Boolean = true - - def read(parser: Parser): Duration = - parser.expectString() match { - case null => null - case s => - Try(Duration.parse(s)) match { - case Success(d) => d - case Failure(u) => - parser.backspace() - throw new ScalaJackError( - parser.showError(s"""Failed to parse Duration from input '$s'""") - ) - } - } - - def write[WIRE]( - t: Duration, - writer: Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit = - t match { - case null => writer.writeNull(out) - case _ => writer.writeString(t.toString, out) - } - - -object InstantTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[Instant]: - def matches(concrete: RType): Boolean = - concrete match { - case u: JavaClassInfo if u.infoClass.getName == "java.time.Instant" => true - case _ => false - } - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[Instant] = this - val info = RType.of[Instant] - override def isStringish: Boolean = true - - def read(parser: Parser): Instant = - parser.expectString() match { - case null => null - case s => - Try(Instant.parse(s)) match { - case Success(d) => d - case Failure(u) => - parser.backspace() - throw new ScalaJackError( - parser.showError(s"""Failed to parse Instant from input '$s'""") - ) - } - } - - def write[WIRE]( - t: Instant, - writer: Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit = - t match { - case null => writer.writeNull(out) - case _ => writer.writeString(t.toString, out) - } - - -object LocalDateTimeTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[LocalDateTime]: - def matches(concrete: RType): Boolean = - concrete match { - case u: JavaClassInfo if u.infoClass.getName == "java.time.LocalDateTime" => true - case _ => false - } - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[LocalDateTime] = this - val info = RType.of[LocalDateTime] - override def isStringish: Boolean = true - - def read(parser: Parser): LocalDateTime = - parser.expectString() match { - case null => null - case s => - Try(LocalDateTime.parse(s, ISO_LOCAL_DATE_TIME)) match { - case Success(d) => d - case Failure(u) => - parser.backspace() - throw new ScalaJackError( - parser - .showError(s"""Failed to parse LocalDateTime from input '$s'""") - ) - } - } - - def write[WIRE]( - t: LocalDateTime, - writer: Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit = - t match { - case null => writer.writeNull(out) - case _ => writer.writeString(t.format(ISO_LOCAL_DATE_TIME), out) - } - - -object LocalDateTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[LocalDate]: - def matches(concrete: RType): Boolean = - concrete match { - case u: JavaClassInfo if u.infoClass.getName == "java.time.LocalDate" => true - case _ => false - } - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[LocalDate] = this - val info = RType.of[LocalDate] - override def isStringish: Boolean = true - - def read(parser: Parser): LocalDate = - parser.expectString() match { - case null => null - case s => - Try(LocalDate.parse(s, ISO_LOCAL_DATE)) match { - case Success(d) => d - case Failure(u) => - parser.backspace() - throw new ScalaJackError( - parser.showError(s"""Failed to parse LocalDate from input '$s'""") - ) - } - } - - def write[WIRE]( - t: LocalDate, - writer: Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit = - t match { - case null => writer.writeNull(out) - case _ => writer.writeString(t.format(ISO_LOCAL_DATE), out) - } - - -object LocalTimeTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[LocalTime]: - def matches(concrete: RType): Boolean = - concrete match { - case u: JavaClassInfo if u.infoClass.getName == "java.time.LocalTime" => true - case _ => false - } - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[LocalTime] = this - val info = RType.of[LocalTime] - override def isStringish: Boolean = true - - def read(parser: Parser): LocalTime = - parser.expectString() match { - case null => null - case s => - Try(LocalTime.parse(s, ISO_LOCAL_TIME)) match { - case Success(d) => d - case Failure(u) => - parser.backspace() - throw new ScalaJackError( - parser.showError(s"""Failed to parse LocalTime from input '$s'""") - ) - } - } - - def write[WIRE]( - t: LocalTime, - writer: Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit = - t match { - case null => writer.writeNull(out) - case _ => writer.writeString(t.format(ISO_LOCAL_TIME), out) - } - - -object OffsetDateTimeTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[OffsetDateTime]: - def matches(concrete: RType): Boolean = - concrete match { - case u: JavaClassInfo if u.infoClass.getName == "java.time.OffsetDateTime" => true - case _ => false - } - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[OffsetDateTime] = this - val info = RType.of[OffsetDateTime] - override def isStringish: Boolean = true - - def read(parser: Parser): OffsetDateTime = - parser.expectString() match { - case null => null - case s => - Try(OffsetDateTime.parse(s, ISO_OFFSET_DATE_TIME)) match { - case Success(d) => d - case Failure(u) => - parser.backspace() - throw new ScalaJackError( - parser.showError( - s"""Failed to parse OffsetDateTime from input '$s'""" - ) - ) - } - } - - def write[WIRE]( - t: OffsetDateTime, - writer: Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit = - t match { - case null => writer.writeNull(out) - case _ => writer.writeString(t.format(ISO_OFFSET_DATE_TIME), out) - } - - -object OffsetTimeTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[OffsetTime]: - def matches(concrete: RType): Boolean = - concrete match { - case u: JavaClassInfo if u.infoClass.getName == "java.time.OffsetTime" => true - case _ => false - } - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[OffsetTime] = this - val info = RType.of[OffsetTime] - override def isStringish: Boolean = true - - def read(parser: Parser): OffsetTime = - parser.expectString() match { - case null => null - case s => - Try(OffsetTime.parse(s, ISO_OFFSET_TIME)) match { - case Success(d) => d - case Failure(u) => - parser.backspace() - throw new ScalaJackError( - parser - .showError(s"""Failed to parse OffsetTime from input '$s'""") - ) - } - } - - def write[WIRE]( - t: OffsetTime, - writer: Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit = - t match { - case null => writer.writeNull(out) - case _ => writer.writeString(t.format(ISO_OFFSET_TIME), out) - } - - -object PeriodTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[Period]: - def matches(concrete: RType): Boolean = - concrete match { - case u: JavaClassInfo if u.infoClass.getName == "java.time.Period" => true - case _ => false - } - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[Period] = this - val info = RType.of[Period] - override def isStringish: Boolean = true - - def read(parser: Parser): Period = - parser.expectString() match { - case null => null - case s => - Try(Period.parse(s)) match { - case Success(d) => d - case Failure(u) => - parser.backspace() - throw new ScalaJackError( - parser.showError(s"""Failed to parse Period from input '$s'""") - ) - } - } - - def write[WIRE]( - t: Period, - writer: Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit = - t match { - case null => writer.writeNull(out) - case _ => writer.writeString(t.toString, out) - } - - -object ZonedDateTimeTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[ZonedDateTime]: - def matches(concrete: RType): Boolean = - concrete match { - case u: JavaClassInfo if u.infoClass.getName == "java.time.ZonedDateTime" => true - case _ => false - } - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[ZonedDateTime] = this - val info = RType.of[ZonedDateTime] - override def isStringish: Boolean = true - - def read(parser: Parser): ZonedDateTime = - parser.expectString() match { - case null => null - case s => - Try(ZonedDateTime.parse(s, ISO_ZONED_DATE_TIME)) match { - case Success(d) => d - case Failure(u) => - parser.backspace() - throw new ScalaJackError( - parser - .showError(s"""Failed to parse ZonedDateTime from input '$s'""") - ) - } - } - - def write[WIRE]( - t: ZonedDateTime, - writer: Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit = - t match { - case null => writer.writeNull(out) - case _ => writer.writeString(t.format(ISO_ZONED_DATE_TIME), out) - } diff --git a/src_old/main/scala/co.blocke.scalajack/typeadapter/TraitTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/TraitTypeAdapter.scala deleted file mode 100644 index 03c7da1a..00000000 --- a/src_old/main/scala/co.blocke.scalajack/typeadapter/TraitTypeAdapter.scala +++ /dev/null @@ -1,75 +0,0 @@ -package co.blocke.scalajack -package typeadapter - -import model._ -import classes._ -import co.blocke.scala_reflection._ -import co.blocke.scala_reflection.info.TraitInfo - -import scala.collection.mutable - -// This should come *after* SealedTraitTypeAdapter in the Context factory list, as all sealed traits are -// also traits, and this factory would pick them all up, hiding the sealed ones. -// -object TraitTypeAdapterFactory extends TypeAdapterFactory: - - def matches(concrete: RType): Boolean = - concrete match { - case _: TraitInfo => true - case _ => false - } - - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[_] = - TraitTypeAdapter(concrete, taCache.jackFlavor.getHintLabelFor(concrete)) - - -case class TraitTypeAdapter[T]( - info: RType, - hintLabel: String -)(implicit taCache: TypeAdapterCache) extends TypeAdapter[T] with Classish: - - inline def calcTA(c: Class[_]): ClassTypeAdapterBase[T] = - taCache.typeAdapterOf(RType.inTermsOf(c, info.asInstanceOf[TraitInfo])).asInstanceOf[ClassTypeAdapterBase[T]] - - // The battle plan here is: Scan the keys of the object looking for type typeHintField. Perform any (optional) - // re-working of the hint value via hintModFn. Look up the correct concete TypeAdapter based on the now-known type - // and re-read the object as a case class. - def read(parser: Parser): T = - if (parser.peekForNull) - null.asInstanceOf[T] - else { - val concreteClass = parser.scanForHint( - hintLabel, - taCache.jackFlavor.hintValueModifiers.getOrElse(info.name, DefaultHintModifier) - ).asInstanceOf[Class[T]] - val ccta = calcTA(concreteClass) - ccta.read(parser).asInstanceOf[T] - } - - def write[WIRE]( - t: T, - writer: Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit = - if (t == null) - writer.writeNull(out) - else { - val ccta = calcTA(t.getClass) - val hintValue = taCache.jackFlavor.hintValueModifiers - .getOrElse(info.name, DefaultHintModifier) - .unapply(t.getClass.getName) - writer.writeObject( - t, - ccta.orderedFieldNames, - ccta.fieldMembersByName, - out, - List( - ( - hintLabel, - ExtraFieldValue( - hintValue, - taCache.jackFlavor.stringTypeAdapter - ) - ) - ) - ) - } \ No newline at end of file diff --git a/src_old/main/scala/co.blocke.scalajack/typeadapter/TryTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/TryTypeAdapter.scala deleted file mode 100644 index d06fb79c..00000000 --- a/src_old/main/scala/co.blocke.scalajack/typeadapter/TryTypeAdapter.scala +++ /dev/null @@ -1,48 +0,0 @@ -package co.blocke.scalajack -package typeadapter - -import model._ - -import scala.collection.mutable -import co.blocke.scala_reflection._ -import co.blocke.scala_reflection.info._ -import scala.util.{Try, Success, Failure} - - -object TryTypeAdapterFactory extends TypeAdapterFactory: - def matches(concrete: RType): Boolean = - concrete match { - case _: TryInfo => true - case _ => false - } - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[_] = - TryTypeAdapter(concrete, taCache.typeAdapterOf(concrete.asInstanceOf[TryInfo].tryType), taCache.jackFlavor) - - -case class TryTypeAdapter[T]( - info: RType, - valueTypeAdapter: TypeAdapter[T], - jackFlavor: JackFlavor[_] - ) extends TypeAdapter[Try[T]]: - - def read(parser: Parser): Try[T] = - val saved = parser.mark() - Try { valueTypeAdapter.read(parser) } match { - case self @ Success(_) => self - case Failure(cause) => - parser.revertToMark(saved) - val f = Failure( - new ScalaJackValueError(jackFlavor.anyTypeAdapter.read(parser), cause) - ) - f - } - - def write[WIRE]( - t: Try[T], - writer: Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit = - t match { - case Success(v) => valueTypeAdapter.write(v, writer, out) - case Failure(e: ScalaJackValueError) => jackFlavor.anyTypeAdapter.write(e.value, writer, out) - case Failure(e) => throw e - } diff --git a/src_old/main/scala/co.blocke.scalajack/typeadapter/TupleTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/TupleTypeAdapter.scala deleted file mode 100644 index 1cd2c13f..00000000 --- a/src_old/main/scala/co.blocke.scalajack/typeadapter/TupleTypeAdapter.scala +++ /dev/null @@ -1,54 +0,0 @@ -package co.blocke.scalajack -package typeadapter - -import model._ -import co.blocke.scala_reflection._ -import co.blocke.scala_reflection.info.TupleInfo -import java.lang.reflect.Method -import scala.collection.mutable -import scala.util.matching.Regex - -object TupleTypeAdapterFactory extends TypeAdapterFactory: - - private val tupleFullName: Regex = """scala.Tuple(\d+)""".r - - def matches(concrete: RType): Boolean = - concrete match { - case ti: TupleInfo => true - case _ => false - } - - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[_] = - val fieldTAs = concrete.asInstanceOf[TupleInfo].tupleTypes.map{ f => - taCache.typeAdapterOf(f) match { - case ota: OptionTypeAdapter[_] => ota.copy(nullIsNone = true) - case jota: JavaOptionalTypeAdapter[_] => jota.copy(nullIsNone = true) - case other => other - } - }.toList - val writeFn = (t: Product) => fieldTAs.zip(t.productIterator) - TupleTypeAdapter(concrete, writeFn, fieldTAs, concrete.infoClass.getConstructors.head) - - -case class TupleTypeAdapter[T]( - info: RType, - writeFn: (Product) => List[(TypeAdapter[_], Any)], - fieldTAs: List[TypeAdapter[_]], - constructor: java.lang.reflect.Constructor[T] - ) extends TypeAdapter[T] with Collectionish { - - def read(parser: Parser): T = - if (parser.peekForNull) then - null.asInstanceOf[T] - else - constructor.newInstance(parser.expectTuple(fieldTAs): _*).asInstanceOf[T] - - def write[WIRE]( - t: T, - writer: Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit = - if (t == null) - writer.writeNull(out) - else - writer.writeTuple(t, writeFn, out) -} diff --git a/src_old/main/scala/co.blocke.scalajack/typeadapter/UUIDTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/UUIDTypeAdapter.scala deleted file mode 100644 index 47817076..00000000 --- a/src_old/main/scala/co.blocke.scalajack/typeadapter/UUIDTypeAdapter.scala +++ /dev/null @@ -1,44 +0,0 @@ -package co.blocke.scalajack -package typeadapter - -import model._ -import co.blocke.scala_reflection._ -import co.blocke.scala_reflection.impl.Clazzes._ -import co.blocke.scala_reflection.info.JavaClassInfo -import java.util.UUID -import scala.collection.mutable -import scala.util.{ Failure, Success, Try } - -object UUIDTypeAdapterFactory extends TypeAdapterFactory with TypeAdapter[UUID]: - override def isStringish: Boolean = true - val uuidClass = classOf[UUID] - def matches(concrete: RType): Boolean = - concrete match { - case j: JavaClassInfo if j.infoClass <:< uuidClass => true - case _ => false - } - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[UUID] = this - - val info = RType.of[java.util.UUID] - def read(parser: Parser): UUID = - val u = parser.expectString() - if (u == null) - null - else { - Try(UUID.fromString(u)) match { - case Success(uuid) => uuid - case Failure(uuid) => - parser.backspace() - throw new ScalaJackError( - parser.showError(s"Failed to create UUID value from parsed text ${u}") - ) - } - } - - def write[WIRE]( - t: UUID, - writer: Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit = t match { - case null => writer.writeNull(out) - case _ => writer.writeString(t.toString, out) - } diff --git a/src_old/main/scala/co.blocke.scalajack/typeadapter/UnionTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/UnionTypeAdapter.scala deleted file mode 100644 index e11e35f1..00000000 --- a/src_old/main/scala/co.blocke.scalajack/typeadapter/UnionTypeAdapter.scala +++ /dev/null @@ -1,67 +0,0 @@ -package co.blocke.scalajack -package typeadapter - -import model._ -import co.blocke.scala_reflection._ -import co.blocke.scala_reflection.info.UnionInfo -import co.blocke.scala_reflection.impl.Clazzes._ - -import scala.collection.mutable.Builder -import scala.util.{ Failure, Success, Try } - -object UnionTypeAdapterFactory extends TypeAdapterFactory: - - def matches(concrete: RType): Boolean = - concrete match { - case _: UnionInfo => true - case _ => false - } - - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[_] = - val unionInfo = concrete.asInstanceOf[UnionInfo] - val leftInfo = unionInfo.leftType - val rightInfo = unionInfo.rightType - - val leftTypeAdapter = taCache.typeAdapterOf(leftInfo) - val rightTypeAdapter = taCache.typeAdapterOf(rightInfo) - - UnionTypeAdapter( - concrete, - leftTypeAdapter, - rightTypeAdapter) - - -case class UnionTypeAdapter[L, R]( - info: RType, - leftTypeAdapter: TypeAdapter[L], - rightTypeAdapter: TypeAdapter[R])(implicit taCache: TypeAdapterCache) - extends TypeAdapter[L | R] { - - override def isStringish: Boolean = leftTypeAdapter.isStringish && rightTypeAdapter.isStringish - override def maybeStringish: Boolean = !isStringish - - def read(parser: Parser): L | R = { - val savedReader = parser.mark() - Try(leftTypeAdapter.read(parser)) match { - case Success(leftValue) => leftValue.asInstanceOf[L] - case Failure(_) => // Left parse failed... try Right - parser.revertToMark(savedReader) - Try(rightTypeAdapter.read(parser)) match { - case Success(rightValue) => rightValue.asInstanceOf[R] - case Failure(x) => - parser.backspace() - throw new ScalaJackError( parser.showError(s"Failed to read any values for union type") ) - } - } - } - - def write[WIRE]( - t: L | R, - writer: Writer[WIRE], - out: Builder[WIRE, WIRE]): Unit = - val trialBuilder = taCache.jackFlavor.getBuilder.asInstanceOf[scala.collection.mutable.Builder[WIRE,WIRE]] - if Try(leftTypeAdapter.write(t.asInstanceOf[L], writer, trialBuilder)).isFailure then - rightTypeAdapter.write(t.asInstanceOf[R], writer, out) - else - out += trialBuilder.result -} diff --git a/src_old/main/scala/co.blocke.scalajack/typeadapter/ValueClassTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/ValueClassTypeAdapter.scala deleted file mode 100644 index cde223f4..00000000 --- a/src_old/main/scala/co.blocke.scalajack/typeadapter/ValueClassTypeAdapter.scala +++ /dev/null @@ -1,43 +0,0 @@ -package co.blocke.scalajack -package typeadapter - -import model._ -import co.blocke.scala_reflection.info._ -import co.blocke.scala_reflection._ -import scala.collection.mutable - -object ValueClassTypeAdapterFactory extends TypeAdapterFactory: - def matches(concrete: RType): Boolean = concrete match { - case c: ScalaCaseClassInfo if c.isValueClass => true - case c: ScalaClassInfo if c.isValueClass => true - case _ => false - } - - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[_] = - val elementType = concrete.asInstanceOf[ClassInfo].fields(0).fieldType - val field0 = concrete match { - case c: ScalaCaseClassInfo => c.fields(0).asInstanceOf[ScalaFieldInfo] - case c: ScalaClassInfo => c.fields(0).asInstanceOf[ScalaFieldInfo] - } - ValueClassTypeAdapter(concrete, field0, taCache.typeAdapterOf(elementType)) - - -case class ValueClassTypeAdapter[VC, Value]( - info: RType, - field0: ScalaFieldInfo, - elementTypeAdapter: TypeAdapter[Value] -) extends TypeAdapter[VC] { - - // For wrapping map keys - override def isStringish: Boolean = elementTypeAdapter.isStringish - override def maybeStringish: Boolean = !elementTypeAdapter.isStringish - - def read(parser: Parser): VC = - info.asInstanceOf[ClassInfo].infoClass.getConstructors.head.newInstance(List(elementTypeAdapter.read(parser).asInstanceOf[Object]):_*).asInstanceOf[VC] - - def write[WIRE]( - t: VC, - writer: Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit = - elementTypeAdapter.write(t.getClass.getMethod(field0.name).invoke(t).asInstanceOf[Value], writer, out) -} diff --git a/src_old/main/scala/co.blocke.scalajack/typeadapter/classes/CaseClassTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/classes/CaseClassTypeAdapter.scala deleted file mode 100644 index fd22927e..00000000 --- a/src_old/main/scala/co.blocke.scalajack/typeadapter/classes/CaseClassTypeAdapter.scala +++ /dev/null @@ -1,41 +0,0 @@ -package co.blocke.scalajack -package typeadapter -package classes - -import model._ -import co.blocke.scala_reflection.info._ -import co.blocke.scala_reflection._ - -import scala.collection.mutable - - -case class CaseClassTypeAdapter[T]( - info: RType, - fieldMembersByName: Map[String, ClassFieldMember[_,_]], - argsTemplate: Array[Object], - fieldBitsTemplate: mutable.BitSet, - typeMembersByName: Map[String, TypeMemberInfo], - orderedFieldNames: List[String], - dbCollectionName: Option[String] = None -)(implicit taCache: TypeAdapterCache) extends ScalaClassTypeAdapter[T]: - - override val isCaseClass = true; - - private val classInfo = info.asInstanceOf[ScalaCaseClassInfo] - - inline def constructWith(args: List[Object]): T = - val const = classInfo.infoClass.getConstructors.head // <-- NOTE: head here isn't bullet-proof, but a generally safe assumption for case classes. (Req because of arg typing mess.) - if (classInfo.typeMembers.nonEmpty) then - val originalArgTypes = classInfo.fields.map(_.fieldType.infoClass) - const.newInstance(args:_*).asInstanceOf[T] - else - const.newInstance(args:_*).asInstanceOf[T] - - def _read_createInstance(args: List[Object], foundBits: mutable.BitSet, captured: java.util.HashMap[String, _]): T = - val asBuilt = constructWith(args) - if isSJCapture then - asBuilt.asInstanceOf[SJCapture].captured = captured - asBuilt - - def _read_updateFieldMembers( fmbn: Map[String, ClassFieldMember[_,_]]): ScalaClassTypeAdapter[T] = - this.copy(fieldMembersByName = fmbn) diff --git a/src_old/main/scala/co.blocke.scalajack/typeadapter/classes/ClassTypeAdapterBase.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/classes/ClassTypeAdapterBase.scala deleted file mode 100644 index 457b2d8f..00000000 --- a/src_old/main/scala/co.blocke.scalajack/typeadapter/classes/ClassTypeAdapterBase.scala +++ /dev/null @@ -1,25 +0,0 @@ -package co.blocke.scalajack -package typeadapter -package classes - -import model._ -import co.blocke.scala_reflection._ -import co.blocke.scala_reflection.info._ - -import scala.collection.mutable - -// For case classes and Java/Scala plain classes, but not traits -trait ClassTypeAdapterBase[T] extends TypeAdapter[T] with Classish: - val info: RType - val argsTemplate: Array[Object] - val fieldBitsTemplate: mutable.BitSet - val isSJCapture: Boolean - val fieldMembersByName: Map[String, ClassFieldMember[_,_]] - val isCaseClass: Boolean = false - val orderedFieldNames: List[String] - val dbCollectionName: Option[String] - - def dbKeys: List[ClassFieldMember[_,_]] = - fieldMembersByName.values.toList - .filter(_.dbKeyIndex.isDefined) - .sortBy(_.dbKeyIndex.get) \ No newline at end of file diff --git a/src_old/main/scala/co.blocke.scalajack/typeadapter/classes/JavaClassTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/classes/JavaClassTypeAdapter.scala deleted file mode 100644 index bd2b48bb..00000000 --- a/src_old/main/scala/co.blocke.scalajack/typeadapter/classes/JavaClassTypeAdapter.scala +++ /dev/null @@ -1,116 +0,0 @@ -package co.blocke.scalajack -package typeadapter -package classes - -import model._ - -import scala.collection.mutable -import co.blocke.scala_reflection._ -import co.blocke.scala_reflection.info._ -import scala.util.Try - -object JavaClassTypeAdapterFactory extends TypeAdapterFactory: - def matches(concrete: RType): Boolean = - concrete match { - case _: JavaClassInfo => true - case _ => false - } - - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[_] = - val classInfo = concrete.asInstanceOf[ClassInfo] - - // Filter out any ignored fields and re-index them all - val fieldsWeCareAbout = classInfo.fields.filterNot(_.annotations.contains(IGNORE)).zipWithIndex.map{ (f,idx) => f.reIndex(idx) } - - val bits = mutable.BitSet() - val args = new Array[Object](fieldsWeCareAbout.size) - - Try(classInfo.asInstanceOf[JavaClassInfo].infoClass.getConstructor()).toOption.orElse( - throw new ScalaJackError("ScalaJack does not support Java classes with a non-empty constructor.") - ) - - val const = classInfo.asInstanceOf[JavaClassInfo].infoClass.getConstructors.head - val phantomInstance = const.newInstance() // used to get default/initially-set values - - val fieldMembersByName = - fieldsWeCareAbout.map { f => - f.fieldType match { - case c: TypeSymbolInfo => throw new ScalaJackError(s"Concrete type expected for class ${concrete.name} field ${f.name}. ${c.getClass.getName} was found.") - case c => - bits += f.index - val fieldMapName = f.annotations.get(CHANGE_ANNO).map(_("name")) - val classFieldMember = ClassFieldMember( - f, - taCache.typeAdapterOf(c), - classInfo.infoClass, - f.annotations.get(DB_KEY).map(_.getOrElse("index","0").toInt), - fieldMapName - ) - if classFieldMember.isOptional then // filter out @Optional annotated fields - bits -= f.index - args(f.index) = f.asInstanceOf[JavaFieldInfo].valueAccessor.invoke(phantomInstance) - fieldMapName.getOrElse(f.name) -> classFieldMember - } - }.toMap - - // Exctract Collection name annotation if present (for plain classes) - val dbCollectionAnnotation = classInfo.annotations.get(DB_COLLECTION).map(_("name")) - - JavaClassTypeAdapter( - concrete, - args, - bits, - fieldMembersByName, - fieldsWeCareAbout.map( f => f.annotations.get(CHANGE_ANNO).map(_("name")).getOrElse(f.name)).toList, - dbCollectionAnnotation - ) - - -case class JavaClassTypeAdapter[J]( - info: RType, - argsTemplate: Array[Object], - fieldBitsTemplate: mutable.BitSet, - fieldMembersByName: Map[String, ClassFieldMember[_,_]], - orderedFieldNames: List[String], - dbCollectionName: Option[String] - )(implicit taCache: TypeAdapterCache) extends ClassTypeAdapterBase[J]: - - val javaClassInfo = info.asInstanceOf[JavaClassInfo] - val isSJCapture = javaClassInfo.hasMixin(SJ_CAPTURE) - - def read(parser: Parser): J = - val (foundBits, args, captured) = parser.expectObject( - this, - taCache.jackFlavor.defaultHint - ) - val testBits = fieldBitsTemplate.collect{ - case b if !foundBits.contains(b) => b - } - if (testBits.isEmpty) then - val const = javaClassInfo.infoClass.getConstructors.head - val asBuilt = const.newInstance().asInstanceOf[J] - if isSJCapture then - asBuilt.asInstanceOf[SJCapture].captured = captured - fieldMembersByName.values.map( f => f.info.asInstanceOf[JavaFieldInfo].valueSetter.invoke(asBuilt, args(f.info.index)) ) - asBuilt - else - parser.backspace() - throw new ScalaJackError( - parser.showError( - s"Class ${info.name} missing required fields: " + testBits - .map(b => orderedFieldNames(b)) - .mkString(", ") - ) - ) - - def write[WIRE]( - t: J, - writer: Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit = - val extras = scala.collection.mutable.ListBuffer.empty[(String, ExtraFieldValue[_])] - writer.writeObject( - t, - orderedFieldNames, - fieldMembersByName, - out, - extras.toList) \ No newline at end of file diff --git a/src_old/main/scala/co.blocke.scalajack/typeadapter/classes/NonCaseClassTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/classes/NonCaseClassTypeAdapter.scala deleted file mode 100644 index e87096e1..00000000 --- a/src_old/main/scala/co.blocke.scalajack/typeadapter/classes/NonCaseClassTypeAdapter.scala +++ /dev/null @@ -1,52 +0,0 @@ -package co.blocke.scalajack -package typeadapter -package classes - -import model._ -import co.blocke.scala_reflection.info._ -import co.blocke.scala_reflection._ -import scala.collection.mutable - - -case class NonCaseClassTypeAdapter[T]( - info: RType, - fieldMembersByName: Map[String, ClassFieldMember[_,_]], - argsTemplate: Array[Object], - fieldBitsTemplate: mutable.BitSet, - typeMembersByName: Map[String, TypeMemberInfo], - orderedFieldNames: List[String], - nonConstructorFields: List[ClassFieldMember[_,_]], - dbCollectionName: Option[String] -)(implicit taCache: TypeAdapterCache) extends ScalaClassTypeAdapter[T]: - - private val classInfo = info.asInstanceOf[ScalaClassInfo] - - inline def fieldName(f: FieldInfo): String = f.annotations.get(CHANGE_ANNO).map(_("name")).getOrElse(f.name) - - def _read_createInstance(args: List[Object], foundBits: mutable.BitSet, captured: java.util.HashMap[String, _]): T = - // Build base object - val asBuilt = - val const = classInfo.infoClass.getConstructors.head - const.newInstance(args.take(classInfo.fields.size):_*).asInstanceOf[T] - // Now call all the non-constructor setters for fields we populate - nonConstructorFields.collect{ - // make sure f is known--in one special case it will not be: "captured" field for SJCapture should be ignored - case f if fieldMembersByName.contains( fieldName(f.info) ) && foundBits.contains(f.info.index) => - // If field is a parameter 'T', the field type for the method is Object - val isTrait = fieldMembersByName(fieldName(f.info)).valueTypeAdapter.isInstanceOf[TraitTypeAdapter[_]] - val fieldMethodType = f.info.originalSymbol.match { - case Some(s) if !isTrait => classOf[Object] - case _ => fieldMembersByName(fieldName(f.info)).valueTypeAdapter.info.infoClass - } - val setter = classInfo.infoClass.getMethod(f.info.name+"_$eq", fieldMethodType ) - args(f.info.index) match { - case m: java.lang.reflect.Method => setter.invoke(asBuilt, m.invoke(asBuilt)) - case thing => setter.invoke(asBuilt, thing) - } - } - if isSJCapture then - asBuilt.asInstanceOf[SJCapture].captured = captured - asBuilt - - def _read_updateFieldMembers( fmbn: Map[String, ClassFieldMember[_,_]]): ScalaClassTypeAdapter[T] = - this.copy(fieldMembersByName = fmbn) diff --git a/src_old/main/scala/co.blocke.scalajack/typeadapter/classes/ScalaClassTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/classes/ScalaClassTypeAdapter.scala deleted file mode 100644 index 7a5a4958..00000000 --- a/src_old/main/scala/co.blocke.scalajack/typeadapter/classes/ScalaClassTypeAdapter.scala +++ /dev/null @@ -1,161 +0,0 @@ -package co.blocke.scalajack -package typeadapter -package classes - -import model._ -import co.blocke.scala_reflection.info._ -import co.blocke.scala_reflection._ - -import scala.collection.mutable - - -trait ScalaClassTypeAdapter[T](implicit taCache: TypeAdapterCache) extends ClassTypeAdapterBase[T]: - val typeMembersByName: Map[String, TypeMemberInfo] - // dbCollectionName: Option[String] - - private val classInfo = info.asInstanceOf[ClassInfo] - - val isSJCapture = classInfo.hasMixin(SJ_CAPTURE) - - def _read_createInstance(args: List[Object], foundBits: mutable.BitSet, captured: java.util.HashMap[String, _]): T - def _read_updateFieldMembers( fmbn: Map[String, ClassFieldMember[_,_]]): ScalaClassTypeAdapter[T] - - def read(parser: Parser): T = - if (parser.peekForNull) then - null.asInstanceOf[T] - else - // External type hint --> Substitute type field's type into the placeholder (i.e.'T') in the class' fields - val (foundBits, args, captured) = { - if (classInfo.typeMembers.nonEmpty) then - val fixedFields = findActualTypeMemberTypes(parser) // Resolve actual type of type member (should be a class) and substitute any fields having that type with the actual - val substitutedClassInfo = _read_updateFieldMembers(fixedFields) - parser.expectObject(substitutedClassInfo, taCache.jackFlavor.defaultHint) - else - parser.expectObject(this, taCache.jackFlavor.defaultHint) - } - - val testBits = fieldBitsTemplate.collect{ - case b if !foundBits.contains(b) => b - } - if (testBits.isEmpty) then - _read_createInstance(args, foundBits, captured) - else - parser.backspace() - throw new ScalaJackError( - parser.showError( - s"Class ${classInfo.name} missing required fields: " + testBits - .map(b => orderedFieldNames(b)) - .mkString(", ") - ) - ) - - - def write[WIRE]( - t: T, - writer: Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit = - - // Resolve actual types (in t) of any type members - val (allFields, filteredTypeMembers) = classInfo match { - case c: ScalaCaseClassInfo => (classInfo.fields.toList, c.filterTraitTypeParams.typeMembers.toList) - case c: ScalaClassInfo => (classInfo.fields.toList ++ c.nonConstructorFields.toList, c.filterTraitTypeParams.typeMembers.toList) - } - val (extras, resolvedFieldMembersByName) = - if filteredTypeMembers.nonEmpty then - val xtras: List[(String, ExtraFieldValue[_])] = filteredTypeMembers.map{ tm => - val foundActualField = allFields.find( _.asInstanceOf[ScalaFieldInfo].originalSymbol == Some(tm.typeSymbol) ) - val resolvedTypeMember = foundActualField.map{ a => - val actualRtype = RType.of(a.valueOf(t).getClass) - tm.copy(memberType = actualRtype) - }.getOrElse(tm) - ( - resolvedTypeMember.name, - ExtraFieldValue( - taCache.jackFlavor.typeValueModifier.unapply(resolvedTypeMember.asInstanceOf[TypeMemberInfo].memberType.name), - taCache.jackFlavor.stringTypeAdapter - ) - ) - } - - val filteredTypeMemberSymbols = filteredTypeMembers.map(_.typeSymbol) - val resolvedFields = fieldMembersByName.map{ case (fname, aField) => - val aScalaField = aField.info.asInstanceOf[ScalaFieldInfo] - if aScalaField.originalSymbol.isDefined && filteredTypeMemberSymbols.contains(aScalaField.originalSymbol.get) then - val actualRtype = RType.of(aScalaField.valueOf(t).getClass) - fname -> aField.copy( info = aScalaField.copy( fieldType = actualRtype ), valueTypeAdapter = taCache.typeAdapterOf(actualRtype) ) - else - fname -> aField - } - - (xtras, resolvedFields) - else - (Nil, fieldMembersByName) - - writer.writeObject( - t, - orderedFieldNames, - resolvedFieldMembersByName, - out, - extras - ) - - // Used by AnyTypeAdapter to insert type hint (not normally needed) into output so object - // may be reconstituted on read - def writeWithHint[WIRE]( - jackFlavor: JackFlavor[WIRE], - t: T, - writer: Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit = - val hintValue = t.getClass.getName - val hintLabel = jackFlavor.getHintLabelFor(info) - val extra = List( - ( - hintLabel, - ExtraFieldValue(hintValue, jackFlavor.stringTypeAdapter) - ) - ) - writer.writeObject(t, orderedFieldNames, fieldMembersByName, out, extra) - - // Use parser to scan JSON for type member name and materialize the TypeMemberInfo with RType of actual/found type. - // (The original TypeMember's RType is likely a trait. The parser-found type should be a concrete class (ScalaCaseClassInfo).) - private def findActualTypeMemberTypes( - parser: Parser - ): Map[String, ClassFieldMember[_,_]] = - val foundByParser: Map[String, TypeMemberInfo] = parser.resolveTypeMembers( - typeMembersByName, - taCache.jackFlavor.typeValueModifier - ) - // Filter any non-trait/class type members... we ignore these so they don't mess up type hint modifiers - val filtered = typeMembersByName.collect { - case (k,tm) if tm.memberType.isInstanceOf[TraitInfo] || tm.memberType.isInstanceOf[ScalaCaseClassInfo] => (k,tm) - } - if (filtered.size != foundByParser.size) - throw new ScalaJackError( - parser.showError( - "Did not find required type member(s): " + typeMembersByName.keySet - .diff(foundByParser.keySet.map(_.toString)) - .mkString(",") - ) - ) - - // Map[TypeSymbol,TypeSymbolInfo] - val invertedBySymbol = foundByParser.map( (name, tpeInfo) => (tpeInfo.typeSymbol, tpeInfo)) - - // NOTE: This is sub-optimal and not "deep". Need a better solution to sew past Level-1 for a general n-deep solution - // As it stands, reflection doesn't provide a way to "sew" types deeply (dynamically). - fieldMembersByName.map { - case (name, fm) if fm.info.originalSymbol.flatMap(s => invertedBySymbol.get(s)).isDefined => - val actualTypeAdapter = taCache.typeAdapterOf(invertedBySymbol(fm.info.originalSymbol.get).memberType) // get TypeAdapter for actual type - val fixedTypeAdapter = fm.valueTypeAdapter match { - case fallback: FallbackTypeAdapter[_, _] => - FallbackTypeAdapter( - actualTypeAdapter.asInstanceOf[TypeAdapter[Any]], - fallback.orElseTypeAdapter.asInstanceOf[TypeAdapter[Any]] - ) - case _ => actualTypeAdapter - } - (name, fm.copy(info = fm.info.asInstanceOf[ScalaFieldInfo].copy(fieldType = invertedBySymbol(fm.info.originalSymbol.get)), valueTypeAdapter = fixedTypeAdapter)) - case (name, fm) => - (name, fm) - } - diff --git a/src_old/main/scala/co.blocke.scalajack/typeadapter/classes/ScalaClassTypeAdapterFactory.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/classes/ScalaClassTypeAdapterFactory.scala deleted file mode 100644 index 78626a6e..00000000 --- a/src_old/main/scala/co.blocke.scalajack/typeadapter/classes/ScalaClassTypeAdapterFactory.scala +++ /dev/null @@ -1,116 +0,0 @@ -package co.blocke.scalajack -package typeadapter -package classes - -import co.blocke.scala_reflection.info._ -import co.blocke.scala_reflection._ -import model._ -import scala.collection.mutable -import java.lang.reflect.Constructor - -object ScalaClassTypeAdapterFactory extends TypeAdapterFactory: - - def matches(concrete: RType): Boolean = - concrete match { - case c: ScalaCaseClassInfo if !c.isValueClass => true - case c: ScalaClassInfo => true - case _ => false - } - - final val CLASSCLASS = Class.forName("java.lang.Class") - - inline def bakeFieldMembersByName( - fields: List[FieldInfo], - constructor: Constructor[_], - infoClass: Class[_] )(implicit taCache: TypeAdapterCache - ): (Map[String,ClassFieldMember[_,_]], mutable.BitSet, Array[Object], List[String]) = - - // Filter out any ignored fields and re-index them all - val fieldsWeCareAbout = fields.filterNot(_.annotations.contains(IGNORE)).zipWithIndex.map{ (f,idx) => f.reIndex(idx) } - - val bits = mutable.BitSet() - val args = new Array[Object](fieldsWeCareAbout.size) - - val fieldsByName = fieldsWeCareAbout.map { f => - val fieldTypeAdapter = f.fieldType match { - case _: TypeSymbolInfo => taCache.typeAdapterOf(impl.PrimitiveType.Scala_Any) // Any unresolved type symbols must be considered Any - case t => - taCache.typeAdapterOf(t) match { - // In certain situations, value classes need to be unwrapped, i.e. use the type adapter of their member. - case vta: ValueClassTypeAdapter[_,_] if f.index < constructor.getParameterTypes().size => // value class in constructor - val constructorParamClass = constructor.getParameterTypes()(f.index).getClass - if constructorParamClass == vta.info.infoClass || constructorParamClass == CLASSCLASS then - vta - else - vta.elementTypeAdapter - case vta: ValueClassTypeAdapter[_,_] => // value class as body member - val returnTypeClass = infoClass.getMethod(f.name).getReturnType - if returnTypeClass == vta.info.infoClass || returnTypeClass == CLASSCLASS then - vta - else - vta.elementTypeAdapter - case other => - other - } - } - - // See if there's a default value set and blip bits/args accordingly to "pre-set" these values - if f.defaultValue.isDefined then - args(f.index) = f.defaultValue.get - else if fieldTypeAdapter.defaultValue.isDefined then - args(f.index) = fieldTypeAdapter.defaultValue.get.asInstanceOf[Object] - else - bits += f.index - - val fieldMapName = f.annotations.get(CHANGE_ANNO).map(_("name")) - val typeAdapter = fieldMapName.getOrElse(f.name) -> ClassFieldMember( - f, - fieldTypeAdapter, - infoClass, - f.annotations.get(DB_KEY).map(_.getOrElse("index","0").toInt), - fieldMapName - ) - // If this field is optional, we need to set the bits accordingly - if typeAdapter._2.isOptional then - bits -= f.index - typeAdapter - }.toMap - (fieldsByName, bits, args, fieldsWeCareAbout.map( f => f.annotations.get(CHANGE_ANNO).map(_("name")).getOrElse(f.name) )) - - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[_] = - concrete match { - case classInfo: ScalaCaseClassInfo => - val (fieldMembersByName, bits, args, orderedFieldNames) = bakeFieldMembersByName(classInfo.fields.toList, classInfo.constructor, classInfo.infoClass) - CaseClassTypeAdapter( - concrete, - fieldMembersByName, - args, - bits, - classInfo.typeMembers.map( tmem => (tmem.name, tmem.asInstanceOf[TypeMemberInfo]) ).toMap, - orderedFieldNames, - classInfo.annotations.get(DB_COLLECTION).map(_("name")) // Exctract Collection name annotation if present (for plain classes) - ) - - case classInfo: ScalaClassInfo => - // Nullify the defaultValue getter for non-constructor fields if there's no @Optional annotation - val nonConstructorFields = classInfo.nonConstructorFields.filterNot(_.name == "captured").map{ _ match { - case f if f.annotations.contains(OPTIONAL_ANNO) => f - case f: ScalaFieldInfo if f.fieldType.isInstanceOf[OptionInfo] => f - case f: ScalaFieldInfo => f.copy(defaultValueAccessorName = None) - } - } - - val (fieldMembersByName, bits, args, orderedFieldNames) = - bakeFieldMembersByName(classInfo.fields.toList ++ nonConstructorFields, classInfo.constructor, classInfo.infoClass) - val paramSize = classInfo.constructor.getParameterTypes().size - NonCaseClassTypeAdapter( - concrete, - fieldMembersByName, - args, - bits, - classInfo.typeMembers.map( tmem => (tmem.name, tmem.asInstanceOf[TypeMemberInfo]) ).toMap, - orderedFieldNames, - fieldMembersByName.values.filter(_.info.asInstanceOf[ScalaFieldInfo].isNonConstructorField).toList, - classInfo.annotations.get(DB_COLLECTION).map(_("name")) // Exctract Collection name annotation if present (for plain classes) - ) - } diff --git a/src_old/main/scala/co.blocke.scalajack/typeadapter/collection/JavaMapLikeTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/collection/JavaMapLikeTypeAdapter.scala deleted file mode 100644 index 78172a8a..00000000 --- a/src_old/main/scala/co.blocke.scalajack/typeadapter/collection/JavaMapLikeTypeAdapter.scala +++ /dev/null @@ -1,73 +0,0 @@ -package co.blocke.scalajack -package typeadapter -package collection - -import model._ -import co.blocke.scala_reflection._ - -import scala.collection.mutable -import java.lang.reflect.{Method, Constructor} -import scala.jdk.CollectionConverters._ - - -case class JavaMapLikeTypeAdapter[KEY, VALUE, TO <: java.util.Map[KEY, VALUE]]( - info: RType, - keyIsOptionalOrAny: Boolean, - valueIsOptionalOrAny: Boolean, - keyTypeAdapter: TypeAdapter[KEY], - valueTypeAdapter: TypeAdapter[VALUE], - companionInstance: Object, - builderMethod: Method, - javaMapConstructor: Constructor[_], - ) extends TypeAdapter[TO]: - - def read(parser: Parser): TO = - // We have to do some voodoo here and peek ahead for Null. Some types, e.g. Int, aren't nullable, - // but Maps are nullable, so we can't trust the valueTypeAdapter to catch and handle null in - // these cases. - parser.peekForNull match { - case true => null.asInstanceOf[TO] - case _ => - val builder = builderMethod.invoke(companionInstance).asInstanceOf[mutable.Builder[(KEY, VALUE),Map[KEY,VALUE]]] - parser.expectMap( - keyTypeAdapter, - valueTypeAdapter, - builder) - javaMapConstructor.newInstance(builder.result.asJava).asInstanceOf[TO] - } - - def write[WIRE]( - t: TO, - writer: Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit = - t match { - case null => writer.writeNull(out) - case _ => - val tScala = t.asScala - val filterKey = - if (keyIsOptionalOrAny && tScala != null) - tScala.asInstanceOf[Map[KEY, VALUE]].filterNot { case (k, v) => k match { - case None => true - case k: java.util.Optional[_] if !k.isPresent => true - case _ => false - }} - else - tScala - - val filterValue = - if valueIsOptionalOrAny && tScala != null then - filterKey.filterNot { case (k, v) => v match { - case None => true - case k: java.util.Optional[_] if !k.isPresent => true - case _ => false - }} - else - filterKey - - writer.writeMap( - filterValue, - keyTypeAdapter, - valueTypeAdapter, - out - ) - } diff --git a/src_old/main/scala/co.blocke.scalajack/typeadapter/collection/JavaSeqLikeTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/collection/JavaSeqLikeTypeAdapter.scala deleted file mode 100644 index 26ae257e..00000000 --- a/src_old/main/scala/co.blocke.scalajack/typeadapter/collection/JavaSeqLikeTypeAdapter.scala +++ /dev/null @@ -1,57 +0,0 @@ -package co.blocke.scalajack -package typeadapter -package collection - -import model._ -import co.blocke.scala_reflection._ - -import scala.collection.mutable -import java.lang.reflect.{Method, Constructor} -import scala.jdk.CollectionConverters._ - - -case class JavaSeqLikeTypeAdapter[ELEM, TO]( - info: RType, - elemIsOptional: Boolean, - elementTypeAdapter: TypeAdapter[ELEM], - companionInstance: Object, - builderMethod: Method, - javaCollConstructor: Constructor[_], - toArrayMethod: Method - ) extends TypeAdapter[TO]: - - def read(parser: Parser): TO = - // We have to do some voodoo here and peek ahead for Null. Some types, e.g. Int, aren't nullable, - // but Seqs are nullable, so we can't trust the valueTypeAdapter to catch and handle null in - // these cases. - parser.peekForNull match { - case true => null.asInstanceOf[TO] - case _ => - val builder = builderMethod.invoke(companionInstance).asInstanceOf[mutable.Builder[ELEM,List[ELEM]]] - parser.expectList( - elementTypeAdapter, - builder - ) - val res: List[ELEM] = builder.result - javaCollConstructor.newInstance(res.asJava).asInstanceOf[TO] - } - - def write[WIRE]( - t: TO, - writer: Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit = - t match { - case null => writer.writeNull(out) - case _ if elemIsOptional => - writer.writeArray( - t.asInstanceOf[Iterable[ELEM]].filterNot{ case v => v match { - case None => true - case v: java.util.Optional[_] if !v.isPresent => true - case _ => false - }}, - elementTypeAdapter, - out - ) - case _ => - writer.writeArray(toArrayMethod.invoke(t).asInstanceOf[Array[ELEM]], elementTypeAdapter, out) - } diff --git a/src_old/main/scala/co.blocke.scalajack/typeadapter/collection/JavaStackTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/collection/JavaStackTypeAdapter.scala deleted file mode 100644 index bdf3d862..00000000 --- a/src_old/main/scala/co.blocke.scalajack/typeadapter/collection/JavaStackTypeAdapter.scala +++ /dev/null @@ -1,59 +0,0 @@ -package co.blocke.scalajack -package typeadapter -package collection - -import model._ -import co.blocke.scala_reflection._ - -import scala.collection.mutable -import java.lang.reflect.{Method, Constructor} -import scala.jdk.CollectionConverters._ - - -case class JavaStackTypeAdapter[ELEM, TO]( - info: RType, - elemIsOptional: Boolean, - elementTypeAdapter: TypeAdapter[ELEM], - companionInstance: Object, - builderMethod: Method, - javaCollConstructor: Constructor[_], - toArrayMethod: Method - ) extends TypeAdapter[TO]: - - def read(parser: Parser): TO = - // We have to do some voodoo here and peek ahead for Null. Some types, e.g. Int, aren't nullable, - // but Seqs are nullable, so we can't trust the valueTypeAdapter to catch and handle null in - // these cases. - parser.peekForNull match { - case true => null.asInstanceOf[TO] - case _ => - val builder = builderMethod.invoke(companionInstance).asInstanceOf[mutable.Builder[ELEM,List[ELEM]]] - parser.expectList( - elementTypeAdapter, - builder - ) - val res: List[ELEM] = builder.result - val stackInstance = javaCollConstructor.newInstance().asInstanceOf[java.util.Stack[ELEM]] - res.map( e => stackInstance.push(e) ) - stackInstance.asInstanceOf[TO] - } - - def write[WIRE]( - t: TO, - writer: Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit = - t match { - case null => writer.writeNull(out) - case _ if elemIsOptional => - writer.writeArray( - t.asInstanceOf[Iterable[ELEM]].filterNot{ case v => v match { - case None => true - case v: java.util.Optional[_] if !v.isPresent => true - case _ => false - }}, - elementTypeAdapter, - out - ) - case _ => - writer.writeArray(toArrayMethod.invoke(t).asInstanceOf[Array[ELEM]], elementTypeAdapter, out) - } diff --git a/src_old/main/scala/co.blocke.scalajack/typeadapter/collection/MapLikeTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/collection/MapLikeTypeAdapter.scala deleted file mode 100644 index 9ef620cb..00000000 --- a/src_old/main/scala/co.blocke.scalajack/typeadapter/collection/MapLikeTypeAdapter.scala +++ /dev/null @@ -1,70 +0,0 @@ -package co.blocke.scalajack -package typeadapter -package collection - -import model._ -import co.blocke.scala_reflection._ - -import scala.collection.mutable -import java.lang.reflect.Method - - -case class MapLikeTypeAdapter[KEY, VALUE, TO <: Map[KEY, VALUE]]( - info: RType, - keyIsOptionalOrAny: Boolean, - valueIsOptionalOrAny: Boolean, - keyTypeAdapter: TypeAdapter[KEY], - valueTypeAdapter: TypeAdapter[VALUE], - companionInstance: Object, - builderMethod: Method - ) extends TypeAdapter[TO]: - - def read(parser: Parser): TO = - // We have to do some voodoo here and peek ahead for Null. Some types, e.g. Int, aren't nullable, - // but Maps are nullable, so we can't trust the valueTypeAdapter to catch and handle null in - // these cases. - parser.peekForNull match { - case true => null.asInstanceOf[TO] - case _ => - val builder = builderMethod.invoke(companionInstance).asInstanceOf[mutable.Builder[(KEY, VALUE),TO]] - parser.expectMap( - keyTypeAdapter, - valueTypeAdapter, - builder) - builder.result - } - - def write[WIRE]( - t: TO, - writer: Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit = - t match { - case null => writer.writeNull(out) - case _ => - val filterKey = - if (keyIsOptionalOrAny && t != null) - t.asInstanceOf[Map[KEY, VALUE]].filterNot { case (k, v) => k match { - case None => true - case k: java.util.Optional[_] if !k.isPresent => true - case _ => false - }} - else - t - - val filterValue = - if (valueIsOptionalOrAny && t != null) - filterKey.asInstanceOf[Map[KEY, VALUE]].filterNot { case (k, v) => v match { - case None => true - case k: java.util.Optional[_] if !k.isPresent => true - case _ => false - }} - else - filterKey - - writer.writeMap( - filterValue.asInstanceOf[Map[KEY, VALUE]], - keyTypeAdapter, - valueTypeAdapter, - out - ) - } diff --git a/src_old/main/scala/co.blocke.scalajack/typeadapter/collection/SeqLikeTypeAdapter.scala b/src_old/main/scala/co.blocke.scalajack/typeadapter/collection/SeqLikeTypeAdapter.scala deleted file mode 100644 index 20001c2a..00000000 --- a/src_old/main/scala/co.blocke.scalajack/typeadapter/collection/SeqLikeTypeAdapter.scala +++ /dev/null @@ -1,53 +0,0 @@ -package co.blocke.scalajack -package typeadapter -package collection - -import model._ -import co.blocke.scala_reflection._ - -import scala.collection.mutable -import java.lang.reflect.Method - - -case class SeqLikeTypeAdapter[ELEM, TO]( - info: RType, - elemIsOptional: Boolean, - elementTypeAdapter: TypeAdapter[ELEM], - companionInstance: Object, - builderMethod: Method - ) extends TypeAdapter[TO]: - - def read(parser: Parser): TO = - // We have to do some voodoo here and peek ahead for Null. Some types, e.g. Int, aren't nullable, - // but Seqs are nullable, so we can't trust the valueTypeAdapter to catch and handle null in - // these cases. - parser.peekForNull match { - case true => null.asInstanceOf[TO] - case _ => - val builder = builderMethod.invoke(companionInstance).asInstanceOf[mutable.Builder[ELEM,TO]] - parser.expectList( - elementTypeAdapter, - builder - ) - builder.result - } - - def write[WIRE]( - t: TO, - writer: Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit = - t match { - case null => writer.writeNull(out) - case _ if elemIsOptional => - writer.writeArray( - t.asInstanceOf[Iterable[ELEM]].filterNot{ case v => v match { - case None => true - case v: java.util.Optional[_] if !v.isPresent => true - case _ => false - }}, - elementTypeAdapter, - out - ) - case _ => - writer.writeArray(t.asInstanceOf[Iterable[ELEM]], elementTypeAdapter, out) - } diff --git a/src_old/main/scala/co.blocke.scalajack/util/BijectiveFunction.scala b/src_old/main/scala/co.blocke.scalajack/util/BijectiveFunction.scala deleted file mode 100644 index a0ff7281..00000000 --- a/src_old/main/scala/co.blocke.scalajack/util/BijectiveFunction.scala +++ /dev/null @@ -1,74 +0,0 @@ -package co.blocke.scalajack -package util - -object BijectiveFunction { - - def apply[A, B](apply: A => B, unapply: B => A): BijectiveFunction[A, B] = - BijectiveFunctionPair(apply, unapply) - - object Implicits { - - implicit final class FunctionReverse[A, B](private val apply: A => B) - extends AnyVal { - @inline def <=>(unapply: B => A): BijectiveFunction[A, B] = - BijectiveFunction(apply, unapply) - def ⇄(unapply: B => A): BijectiveFunction[A, B] = <=>(unapply) - } - - } - -} - -trait BijectiveFunction[A, B] extends Function[A, B] { - - override def apply(a: A): B - def unapply(b: B): A - - def inverse: BijectiveFunction[B, A] = InvertedBijectiveFunction(this) - def compose[X](f: BijectiveFunction[X, A]): BijectiveFunction[X, B] = - ComposedBijectiveFunction(f, this) - def andThen[C](g: BijectiveFunction[B, C]): BijectiveFunction[A, C] = - ComposedBijectiveFunction(this, g) - // $COVERAGE-OFF$Unused right now--may be part of future functionality - def memoized: BijectiveFunction[A, B] = MemoizedBijectiveFunction(this) - // $COVERAGE-ON$ -} - -case class BijectiveFunctionPair[A, B](applyFn: A => B, unapplyFn: B => A) - extends BijectiveFunction[A, B] { - - override def apply(a: A): B = applyFn(a) - override def unapply(b: B): A = unapplyFn(b) -} - -case class InvertedBijectiveFunction[A, B](f: BijectiveFunction[A, B]) - extends BijectiveFunction[B, A] { - - override def apply(b: B): A = f.unapply(b) - override def unapply(a: A): B = f.apply(a) - override def inverse: BijectiveFunction[A, B] = f -} - -case class ComposedBijectiveFunction[A, B, C]( - f: BijectiveFunction[A, B], - g: BijectiveFunction[B, C]) - extends BijectiveFunction[A, C] { - - override def apply(a: A): C = g.apply(f.apply(a)) - override def unapply(c: C): A = f.unapply(g.unapply(c)) -} - -// $COVERAGE-OFF$Unused right now--may be part of future functionality -case class MemoizedBijectiveFunction[A, B](f: BijectiveFunction[A, B]) - extends BijectiveFunction[A, B] { - - import scala.collection.mutable - - val applyCache = new mutable.WeakHashMap[A, B] - val unapplyCache = new mutable.WeakHashMap[B, A] - - override def apply(a: A): B = applyCache.getOrElseUpdate(a, f.apply(a)) - override def unapply(b: B): A = unapplyCache.getOrElseUpdate(b, f.unapply(b)) - override def memoized: BijectiveFunction[A, B] = this -} -// $COVERAGE-ON$ diff --git a/src_old/main/scala/co.blocke.scalajack/util/FixFloat.scala b/src_old/main/scala/co.blocke.scalajack/util/FixFloat.scala deleted file mode 100644 index 4e35e79d..00000000 --- a/src_old/main/scala/co.blocke.scalajack/util/FixFloat.scala +++ /dev/null @@ -1,11 +0,0 @@ -package co.blocke.scalajack -package util - -object FixFloat { - // Bizzare set of magic to try to "fix" the precision slop when moving from Float->Double (prints extra digits in JSON) - def capFloat(f: Float): Double = { - val d = f.toString.toDouble - val diff = f.toDouble - d - f - diff - } -} diff --git a/src_old/main/scala/co.blocke.scalajack/yaml/YamlBuilder.scala b/src_old/main/scala/co.blocke.scalajack/yaml/YamlBuilder.scala deleted file mode 100644 index 7ff15765..00000000 --- a/src_old/main/scala/co.blocke.scalajack/yaml/YamlBuilder.scala +++ /dev/null @@ -1,20 +0,0 @@ -package co.blocke.scalajack -package yaml - -import org.snakeyaml.engine.v2.nodes.Node -import scala.collection.mutable - -case class YamlBuilder() extends mutable.Builder[Node, Node] { - private var internalValue: Option[Node] = None - - def addOne(elem: Node): this.type = { - internalValue = Some(elem) - this - } - - def clear(): Unit = internalValue = None - - def result(): Node = internalValue.getOrElse( - throw new ScalaJackError("No value set for internal yaml builder") - ) -} \ No newline at end of file diff --git a/src_old/main/scala/co.blocke.scalajack/yaml/YamlFlavor.scala b/src_old/main/scala/co.blocke.scalajack/yaml/YamlFlavor.scala deleted file mode 100644 index 3b8418f9..00000000 --- a/src_old/main/scala/co.blocke.scalajack/yaml/YamlFlavor.scala +++ /dev/null @@ -1,77 +0,0 @@ -package co.blocke.scalajack -package yaml - -import model._ -import co.blocke.scala_reflection.RType -import typeadapter.MaybeStringWrapTypeAdapter - -import scala.collection.mutable -import org.snakeyaml.engine.v2.api.DumpSettings -import org.snakeyaml.engine.v2.api.lowlevel.{Present, Serialize} - - -opaque type YAML = String - -case class YamlFlavor( - override val defaultHint: String = "_hint", - override val permissivesOk: Boolean = false, - override val customAdapters: List[TypeAdapterFactory] = List.empty[TypeAdapterFactory], - override val hintMap: Map[String, String] = Map.empty[String, String], - override val hintValueModifiers: Map[String, HintValueModifier] = Map.empty[String, HintValueModifier], - override val typeValueModifier: HintValueModifier = DefaultHintModifier, - override val parseOrElseMap: Map[Class[_], RType] = Map.empty[Class[_], RType], - override val enumsAsInt: Boolean = false -) extends JackFlavor[YAML] { - - private val settings = DumpSettings.builder().setIndent(2).build() - private val serializer = new Serialize(settings) - private val presenter = new Present(settings) - - def _read[T](input: YAML, typeAdapter: TypeAdapter[T]): T = - val parser = YamlParser(input, this) - typeAdapter.read(parser).asInstanceOf[T] - - def _render[T](t: T, typeAdapter: TypeAdapter[T]): YAML = - val sb = YamlBuilder() - typeAdapter.write(t, writer, sb) - presenter.emitToString(serializer.serializeOne(sb.result()).iterator).asInstanceOf[YAML] - - def parse(input: YAML): Parser = YamlParser(input, this) - - private val writer = YamlWriter() //(this) - - override val stringifyMapKeys: Boolean = true - - def allowPermissivePrimitives(): JackFlavor[YAML] = - throw new ScalaJackError("Not available for YAML encoding") - def enumsAsInts(): JackFlavor[YAML] = this.copy(enumsAsInt = true) - def parseOrElse(poe: (RType, RType)*): JackFlavor[YAML] = - this.copy(parseOrElseMap = this.parseOrElseMap ++ poe.map{(p,oe) => p.infoClass->oe}) - def withAdapters(ta: TypeAdapterFactory*): JackFlavor[YAML] = - this.copy(customAdapters = this.customAdapters ++ ta.toList) - def withDefaultHint(hint: String): JackFlavor[YAML] = - this.copy(defaultHint = hint) - def withHints(h: (RType, String)*): JackFlavor[YAML] = - this.copy(hintMap = this.hintMap ++ h.map{(rt,hint) => rt.name->hint}) - def withHintModifiers(hm: (RType, HintValueModifier)*): JackFlavor[YAML] = - this.copy(hintValueModifiers = this.hintValueModifiers ++ hm.map{(rt,hintM) => rt.name->hintM}) - def withTypeValueModifier(tm: HintValueModifier): JackFlavor[YAML] = - this.copy(typeValueModifier = tm) - - def stringWrapTypeAdapterFactory[T]( - wrappedTypeAdapter: TypeAdapter[T], - emptyStringOk: Boolean = true - ): TypeAdapter[T] = wrappedTypeAdapter // no need to stringify--YAML handles complex key values! - - def maybeStringWrapTypeAdapterFactory[T]( - wrappedTypeAdapter: TypeAdapter[T], - emptyStringOk: Boolean = true - ): TypeAdapter[T] = wrappedTypeAdapter // no need to stringify--YAML handles complex key values! - - // OK... for YAML I've done a bad thing here and lied. The JackFlavor trait requires Builder[WIRE,WIRE], - // which is Builder[YAML,YAML]. But the internals of snakeyaml require me to use a Builder[Node,Node]. - // Correctly untangling this would be a huge amount of work...and unnecessary. Both producer and consumer - // of this Builder know and expect Builder[Node,Node], so it was expedient to just lie to the trait and - // say this is a Builder[YAML,YAML] to make the compiler happy. Sue me. - override def getBuilder: mutable.Builder[YAML, YAML] = YamlBuilder().asInstanceOf[mutable.Builder[YAML, YAML]] -} \ No newline at end of file diff --git a/src_old/main/scala/co.blocke.scalajack/yaml/YamlParser.scala b/src_old/main/scala/co.blocke.scalajack/yaml/YamlParser.scala deleted file mode 100644 index 7516899e..00000000 --- a/src_old/main/scala/co.blocke.scalajack/yaml/YamlParser.scala +++ /dev/null @@ -1,278 +0,0 @@ -package co.blocke.scalajack -package yaml - -import model._ -import typeadapter.classes.ClassTypeAdapterBase -import co.blocke.scala_reflection._ -import co.blocke.scala_reflection.info.TypeMemberInfo - -import scala.collection.mutable -import scala.jdk.CollectionConverters._ -import org.snakeyaml.engine.v2.parser.ParserImpl -import org.snakeyaml.engine.v2.scanner.StreamReader -import org.snakeyaml.engine.v2.api.LoadSettings -import org.snakeyaml.engine.v2.events._ - -case class YamlParser(input: YAML, jackFlavor: JackFlavor[YAML]) extends Parser { - - private val loadSettings = LoadSettings.builder().build() - private val snake = new ParserImpl(loadSettings, new StreamReader(loadSettings, input.asInstanceOf[String])) - - private var indentLevel = 0 - - private def doICare(e: Event) = e match { - case _: StreamStartEvent => false - case _: StreamEndEvent => false - case _: DocumentStartEvent => false - case _: DocumentEndEvent => false - case _ => true - } - - type WIRE = YAML - - // Just the Events I care about - private val events = snake.asScala.filter(doICare).toList - - private val indentLevelMap = mutable.Map.empty[Int, Int] - - private var i = 0 - - def expectString(nullOK: Boolean = true): String = { - events(i) match { - case s: ScalarEvent => - i += 1 - s.getScalarStyle.toString match { - case "|" | ">" => s.getValue().stripTrailing() - case _ if s.getValue == "null" && nullOK => null - case _ => s.getValue - } - case e => - throw new ScalaJackError(showError("Expected a String here: " + e)) - } - } - - private def expectCollecton[K, TO](nextTest: Boolean, builder: mutable.Builder[K, TO], fn: () => Unit, errStr: String): TO = { - indentLevel += 1 - val startIndent = indentLevel - if (!nextTest) - throw new ScalaJackError(showError(errStr + events(i))) - i += 1 - while (!events(i).isInstanceOf[CollectionEndEvent] || indentLevel != startIndent) - fn() - i += 1 // consume collection end event - indentLevel -= 1 - builder.result() - } - - def expectList[K, TO](elemTypeAdapter: TypeAdapter[K], builder: mutable.Builder[K, TO]): TO = - expectCollecton(nextIsArray, builder, () => { - builder += elemTypeAdapter.read(this) - }, "Expected a List here: ") - - def expectTuple( - tupleFieldTypeAdapters: List[TypeAdapter[_]] - ): List[Object] = { - val builder = new mutable.ListBuffer[Object]() - expectCollecton(nextIsArray, builder, () => { - tupleFieldTypeAdapters.foreach { fieldTypeAdapter => - builder += fieldTypeAdapter.read(this).asInstanceOf[Object] - } - }, "Expected a Tuple here: ") - } - - def expectMap[K, V, TO](keyTypeAdapter: TypeAdapter[K], valueTypeAdapter: TypeAdapter[V], builder: mutable.Builder[(K, V), TO]): TO = - expectCollecton( - nextIsObject, - builder, - () => { - val mapKey = keyTypeAdapter.read(this) - val mapValue = valueTypeAdapter.read(this) - builder += ((mapKey, mapValue)) - }, - "Expected a Map or Object here: " - ) - - def expectObject( - classBase: ClassTypeAdapterBase[_], - hintLabel: String - ): (mutable.BitSet, List[Object], java.util.HashMap[String, _]) = { - indentLevel += 1 - val startIndent = indentLevel - if (!nextIsObject) - throw new ScalaJackError(showError("Expected an Object here: " + events(i))) - i += 1 - - val args = classBase.argsTemplate.clone() - val fieldBits = mutable.BitSet() - val captured = - if (classBase.isSJCapture) new java.util.HashMap[String, List[Event]]() - else null - while (!events(i) - .isInstanceOf[CollectionEndEvent] || indentLevel != startIndent) { - val key = expectString(false) - classBase.fieldMembersByName.get(key) match { - case Some(field) => - fieldBits += field.info.index - args(field.info.index) = field.valueTypeAdapter.read(this).asInstanceOf[Object] - case None => // found some input field not present in class - val mark = i - skipOverElement(startIndent) - if (classBase.isSJCapture && key != hintLabel) - // This is weird but snakeyaml's internals expect a stream start/end event, so we need to - // create these or boom happens. - captured.put(key, new StreamStartEvent() +: events.slice(mark, i) :+ new StreamEndEvent()) - } - } - - i += 1 // consume collection end event - indentLevel -= 1 - (fieldBits, args.toList, captured) - } - - def expectBoolean(): Boolean = events(i) match { - case n: ScalarEvent if n.getValue == "true" => - i += 1 - true - case n: ScalarEvent if n.getValue == "false" => - i += 1 - false - case e => - throw new ScalaJackError(showError("Expected a Boolean value here: " + e)) - } - - @inline def isNumberChar(char: Char): Boolean = - ('0' <= char && char <= '9') || char == '.' || char == 'e' || char == 'E' || char == '-' || char == '+' - @inline def isNumber(s: String): Boolean = s.toCharArray.foldLeft(true) { - case (acc, c) => acc && isNumberChar(c) - } - def expectNumber(nullOK: Boolean = false): String = events(i) match { - case n: ScalarEvent if n.getValue == "null" || n.getValue == "" => - i += 1 - null - case n: ScalarEvent if isNumber(n.getValue) => - i += 1 - n.getValue - case e => - throw new ScalaJackError(showError("Expected a Number value here: " + e)) - } - - def peekForNull: Boolean = events(i) match { - case n: ScalarEvent if n.getValue == "null" => - i += 1 - true - case _ => false - } - - def skipOverElement(startIndent: Int): Unit = { - var done = false - while (!done) { - events(i) match { - case _: ScalarEvent if indentLevel == startIndent => - done = true - case _: CollectionStartEvent => - indentLevel += 1 - case _: CollectionEndEvent => // close of a nested collection - indentLevel -= 1 - if (indentLevel == startIndent) - done = true - case _ => - } - i += 1 - } - } - - def scanForHint(hint: String, converterFn: HintBijective): Class[_] = { - val mark = i - indentLevel += 1 - val startIndent = indentLevel - if (!nextIsObject) - throw new ScalaJackError( - showError("Expected an Object here: " + events(i)) - ) - i += 1 - - val max = events.length - var found = false - while (!found && i < max && !events(i).isInstanceOf[CollectionEndEvent]) { - found = expectString(false) == hint - if (!found) - skipOverElement(startIndent) - } - if (!found) - throw new ScalaJackError(showError(s"Type hint '$hint' not found")) - - // Found hint or we wouldn't be here - val rawHintString = expectString(false) - val hintType = try { - converterFn.apply(rawHintString) - } catch { - case t: Throwable => - throw new ScalaJackError( - showError(s"Couldn't marshal class for $rawHintString") - ) - } - i = mark // we found hint, but go back to parse object - indentLevel -= 1 - Class.forName(hintType) - } - - // For embedded type members. Convert the type member into runtime "actual" type, e.g. T --> Foo - def resolveTypeMembers( - typeMembersByName: Map[String, TypeMemberInfo], - converterFn: HintBijective - ): Map[String, TypeMemberInfo] = { - val mark = i - indentLevel += 1 - val startIndent = indentLevel - if (!nextIsObject) - throw new ScalaJackError( - showError("Expected an Object here: " + events(i)) - ) - i += 1 - - val collected = new java.util.HashMap[String, TypeMemberInfo]() - while (!events(i).isInstanceOf[CollectionEndEvent]) { - val key = expectString() - if (typeMembersByName.contains(key)) - collected.put( - key, - TypeMemberInfo(key, typeMembersByName(key).typeSymbol, RType.of(Class.forName(converterFn.apply(expectString())))) - ) - else - skipOverElement(startIndent) - } - i = mark // we found hint, but go back to parse object - indentLevel -= 1 - collected.asScala.toMap - } - - def showError(msg: String): String = s"Line ${events(i).getStartMark.get().getLine}: " + msg - def backspace(): Unit = i -= 1 - def mark(): Int = { - indentLevelMap(i) = indentLevel - i - } - def revertToMark(mark: Int): Unit = { - i = mark - indentLevel = indentLevelMap(i) - } - def nextIsString: Boolean = events(i).isInstanceOf[ScalarEvent] - def nextIsNumber: Boolean = - events(i).isInstanceOf[ScalarEvent] && isNumber( - events(i).asInstanceOf[ScalarEvent].getValue - ) - def nextIsObject: Boolean = - events(i).isInstanceOf[CollectionStartEvent] && events(i).toString - .startsWith("+MAP") - def nextIsArray: Boolean = - events(i).isInstanceOf[CollectionStartEvent] && events(i).toString - .startsWith("+SEQ") - def nextIsBoolean: Boolean = - events(i).isInstanceOf[ScalarEvent] && List("true", "false").contains( - events(i).asInstanceOf[ScalarEvent].getValue - ) - // $COVERAGE-OFF$Unused, un-called by YamlFlavor machinery - def subParser(input: YAML): Parser = this.copy(input = input) - def sourceAsString: String = input.asInstanceOf[String] - // $COVERAGE-ON$ -} \ No newline at end of file diff --git a/src_old/main/scala/co.blocke.scalajack/yaml/YamlWriter.scala b/src_old/main/scala/co.blocke.scalajack/yaml/YamlWriter.scala deleted file mode 100644 index 9243a120..00000000 --- a/src_old/main/scala/co.blocke.scalajack/yaml/YamlWriter.scala +++ /dev/null @@ -1,177 +0,0 @@ -package co.blocke.scalajack -package yaml - -import model._ - -import scala.collection.mutable -import org.snakeyaml.engine.v2.nodes._ -import org.snakeyaml.engine.v2.api.LoadSettings -import org.snakeyaml.engine.v2.common.{FlowStyle, ScalarStyle} -import org.snakeyaml.engine.v2.composer.Composer -import org.snakeyaml.engine.v2.events.Event -import org.snakeyaml.engine.v2.resolver.JsonScalarResolver - -import scala.jdk.CollectionConverters._ - -case class YamlWriter() extends Writer[Node] { - - def writeArray[Elem](t: Iterable[Elem], elemTypeAdapter: TypeAdapter[Elem], out: mutable.Builder[Node, Node]): Unit = t match { - case null => writeNull(out) - case a => - val arr = mutable.ListBuffer.empty[Node] - val outBuf = YamlBuilder() - a.iterator.foreach { item => - outBuf.clear() - elemTypeAdapter.write(item, this, outBuf) - arr += outBuf.result - } - val flow = elemTypeAdapter match { - case _: ScalarTypeAdapter[_] => FlowStyle.FLOW - case _ => FlowStyle.BLOCK - } - out += new SequenceNode(Tag.SEQ, arr.asJava, flow) - } - - def writeBigInt(t: BigInt, out: mutable.Builder[Node, Node]): Unit = - t match { - case null => writeNull(out) - case _ => out += new ScalarNode(Tag.INT, t.toString, ScalarStyle.PLAIN) - } - def writeBoolean(t: Boolean, out: mutable.Builder[Node, Node]): Unit = - out += new ScalarNode(Tag.BOOL, t.toString, ScalarStyle.PLAIN) - def writeDecimal(t: BigDecimal, out: mutable.Builder[Node, Node]): Unit = - t match { - case null => writeNull(out) - case _ => out += new ScalarNode(Tag.FLOAT, t.toString, ScalarStyle.PLAIN) - } - def writeDouble(t: Double, out: mutable.Builder[Node, Node]): Unit = - out += new ScalarNode(Tag.FLOAT, t.toString, ScalarStyle.PLAIN) - def writeInt(t: Int, out: mutable.Builder[Node, Node]): Unit = - out += new ScalarNode(Tag.INT, t.toString, ScalarStyle.PLAIN) - def writeLong(t: Long, out: mutable.Builder[Node, Node]): Unit = - out += new ScalarNode(Tag.INT, t.toString, ScalarStyle.PLAIN) - - def writeMap[Key, Value, To](t: collection.Map[Key, Value], keyTypeAdapter: TypeAdapter[Key], valueTypeAdapter: TypeAdapter[Value], out: mutable.Builder[Node, Node]): Unit = - t match { - case null => writeNull(out) - case daMap => - val outBuf = YamlBuilder() - val outMap = daMap - .map { - case (key, value) => - if (key == null) - throw new ScalaJackError("Map keys cannot be null.") - outBuf.clear() - keyTypeAdapter.write(key, this, outBuf) - val k = outBuf.result() - outBuf.clear() - valueTypeAdapter.write(value, this, outBuf) - new NodeTuple(k, outBuf.result()) - } - .toList - .asJava - out += new MappingNode(Tag.MAP, outMap, FlowStyle.AUTO) - } - - def writeNull(out: mutable.Builder[Node, Node]): Unit = - out += new ScalarNode(Tag.NULL, "null", ScalarStyle.PLAIN) - - @inline private def writeFields( - fields: List[(String, Any, TypeAdapter[Any])] - ): Map[Node, Node] = { - val outBuf = YamlBuilder() - fields.collect { - case (label, value, valueTypeAdapter) if value != None => - outBuf.clear() - valueTypeAdapter.write(value, this, outBuf) - new ScalarNode(Tag.STR, label, ScalarStyle.PLAIN) -> outBuf.result() - }.toMap - } - - def writeObject[T]( - t: T, - orderedFieldNames: List[String], - fieldMembersByName: collection.Map[String, ClassFieldMember[_,_]], - out: mutable.Builder[Node, Node], - extras: List[(String, ExtraFieldValue[_])] - ): Unit = { - t match { - case null => writeNull(out.asInstanceOf[collection.mutable.Builder[Node,Node]]) - case _ => - val extraFields = writeFields( - extras.map( - e => - ( - e._1, - e._2.value, - e._2.valueTypeAdapter.asInstanceOf[TypeAdapter[Any]] - ) - ) - ) - val classFields = writeFields(orderedFieldNames.map { orn => - val oneField = fieldMembersByName(orn) - (orn, oneField.info.valueOf(t), oneField.valueTypeAdapter.asInstanceOf[TypeAdapter[Any]]) - }) - val captureFields = t match { - case sjc: SJCapture => - import scala.jdk.CollectionConverters._ - - sjc.captured.asScala.map { - case (k, v) => - val composer = new Composer(LoadSettings.builder.build, new EventParser(v.asInstanceOf[List[Event]])) - (new ScalarNode(Tag.STR, k, ScalarStyle.PLAIN), composer.next) - } - case _ => Map.empty[Node, Node] - } - - val mapNodes = (extraFields ++ classFields ++ captureFields).map { case (k, v) => new NodeTuple(k, v) } - out += new MappingNode(Tag.MAP, mapNodes.toList.asJava, FlowStyle.AUTO).asInstanceOf[Node] - } - } - - def writeString(t: String, out: mutable.Builder[Node, Node]): Unit = t match { - case null => writeNull(out) - case _ => out += new ScalarNode(Tag.STR, t, ScalarStyle.PLAIN) - } - - def writeRaw(t: Node, out: mutable.Builder[Node, Node]): Unit = - out += t - - def writeTuple[T]( - t: T, - writeFn: (Product) => List[(TypeAdapter[_], Any)], - out: mutable.Builder[Node, Node] - ): Unit = { - val arr = mutable.ListBuffer.empty[Node] - val outBuf = YamlBuilder() - val flowStyle = writeFn(t.asInstanceOf[Product]).foldLeft(true) { case (acc, (fieldTA, fieldValue)) => - outBuf.clear() - fieldTA.castAndWrite(fieldValue, this, outBuf) - arr += outBuf.result - if fieldTA.isInstanceOf[ScalarTypeAdapter[_]] then - acc - else - false - } - out += new SequenceNode(Tag.SEQ, arr.asJava, {if flowStyle then FlowStyle.FLOW else FlowStyle.BLOCK}) - } -} - -// $COVERAGE-OFF$This is a snakeyaml thing. It works if consuming it works. -case class EventParser(events: List[Event]) extends org.snakeyaml.engine.v2.parser.Parser { - private var i = 0 - - def checkEvent(choice: Event.ID): Boolean = - if (i < events.length) - events(i).getEventId == choice - else - false - def peekEvent(): Event = events(i) - def next(): Event = { - val ret = events(i) - i += 1 - ret - } - def hasNext: Boolean = i < events.length - // $COVERAGE-ON$ -} \ No newline at end of file diff --git a/src_old/test/java/co/blocke/scalajack/JavaArray.java b/src_old/test/java/co/blocke/scalajack/JavaArray.java deleted file mode 100644 index 49400a5a..00000000 --- a/src_old/test/java/co/blocke/scalajack/JavaArray.java +++ /dev/null @@ -1,53 +0,0 @@ -package co.blocke.scalajack; - -import co.blocke.scalajack.SJCaptureJava; - -import java.math.*; - -public class JavaArray { - - private BigDecimal[] bigDecs; - private BigInteger[] bigInts; - private java.lang.Boolean[] booleans; - private java.lang.Byte[] bytes; - private Character[] characters; - private java.lang.Double[] doubles; - private java.lang.Float[] floats; - private java.lang.Integer[] integers; - private java.lang.Long[] longs; - private java.lang.Short[] shorts; - private BigInteger[][] multi; - - public BigDecimal[] getBigDecs() { return bigDecs; } - public void setBigDecs(BigDecimal[] n) { bigDecs = n; } - - public BigInteger[] getBigInts() { return bigInts; } - public void setBigInts(BigInteger[] n) { bigInts = n; } - - public java.lang.Boolean[] getBooleans() { return booleans; } - public void setBooleans(java.lang.Boolean[] n) { booleans = n; } - - public java.lang.Byte[] getBytes() { return bytes; } - public void setBytes(java.lang.Byte[] n) { bytes = n; } - - public Character[] getCharacters() { return characters; } - public void setCharacters(Character[] n) { characters = n; } - - public java.lang.Double[] getDoubles() { return doubles; } - public void setDoubles(java.lang.Double[] n) { doubles = n; } - - public java.lang.Float[] getFloats() { return floats; } - public void setFloats(java.lang.Float[] n) { floats = n; } - - public java.lang.Integer[] getIntegers() { return integers; } - public void setIntegers(java.lang.Integer[] n) { integers = n; } - - public java.lang.Long[] getLongs() { return longs; } - public void setLongs(java.lang.Long[] n) { longs = n; } - - public java.lang.Short[] getShorts() { return shorts; } - public void setShorts(java.lang.Short[] n) { shorts = n; } - - public BigInteger[][] getMulti() { return multi; } - public void setMulti(BigInteger[][] n) { multi = n; } -} \ No newline at end of file diff --git a/src_old/test/java/co/blocke/scalajack/JavaCap.java b/src_old/test/java/co/blocke/scalajack/JavaCap.java deleted file mode 100644 index a6242585..00000000 --- a/src_old/test/java/co/blocke/scalajack/JavaCap.java +++ /dev/null @@ -1,12 +0,0 @@ -package co.blocke.scalajack; - -import co.blocke.scalajack.SJCaptureJava; - -public class JavaCap extends SJCaptureJava { - - private String name; - - public String getName() { return name; } - public void setName(String n) { name = n; } -} - diff --git a/src_old/test/java/co/blocke/scalajack/JavaEnum.java b/src_old/test/java/co/blocke/scalajack/JavaEnum.java deleted file mode 100644 index beccf90e..00000000 --- a/src_old/test/java/co/blocke/scalajack/JavaEnum.java +++ /dev/null @@ -1,9 +0,0 @@ -package co.blocke.scalajack; - -public class JavaEnum { - - private Temperature temp; - - public Temperature getTemp() { return temp; } - public void setTemp(Temperature n) { temp = n; } -} diff --git a/src_old/test/java/co/blocke/scalajack/JavaSimpleBase.java b/src_old/test/java/co/blocke/scalajack/JavaSimpleBase.java deleted file mode 100644 index 5a4d9265..00000000 --- a/src_old/test/java/co/blocke/scalajack/JavaSimpleBase.java +++ /dev/null @@ -1,22 +0,0 @@ -package co.blocke.scalajack; - -import co.blocke.scalajack.*; - -public class JavaSimpleBase { - - private int two; - @DBKey - @Change(name="dos") public int getTwo(){ return two; } - public void setTwo(int v) { two = v; } - - private int three = -10; - @DBKey(index = 99) @Optional - public int getThree(){ return three; } - public void setThree(int v) { three = v; } - - private int bogus = -1; - public int getBogus(){ return bogus; } - @Ignore - public void setBogus(int v) { bogus = v; } - -} diff --git a/src_old/test/java/co/blocke/scalajack/JavaSimpleChild.java b/src_old/test/java/co/blocke/scalajack/JavaSimpleChild.java deleted file mode 100644 index 80ad0aca..00000000 --- a/src_old/test/java/co/blocke/scalajack/JavaSimpleChild.java +++ /dev/null @@ -1,4 +0,0 @@ -package co.blocke.scalajack; - -public class JavaSimpleChild extends JavaSimpleBase { -} diff --git a/src_old/test/java/co/blocke/scalajack/Maybe.java b/src_old/test/java/co/blocke/scalajack/Maybe.java deleted file mode 100644 index 66da7327..00000000 --- a/src_old/test/java/co/blocke/scalajack/Maybe.java +++ /dev/null @@ -1,15 +0,0 @@ -package co.blocke.scalajack; - -import java.util.Optional; - -public class Maybe { - - private Optional one = Optional.empty(); - public Optional getOne(){ return one; } - public void setOne(Optional v) { one = v; } - - private Optional two = Optional.of("stuff"); - public Optional getTwo(){ return two; } - public void setTwo(Optional v) { two = v; } - -} diff --git a/src_old/test/java/co/blocke/scalajack/Maybe2.java b/src_old/test/java/co/blocke/scalajack/Maybe2.java deleted file mode 100644 index 77897adf..00000000 --- a/src_old/test/java/co/blocke/scalajack/Maybe2.java +++ /dev/null @@ -1,15 +0,0 @@ -package co.blocke.scalajack; - -import java.util.Optional; - -public class Maybe2 { - - private String one = "foom"; - public String getOne(){ return one; } - public void setOne(String v) { one = v; } - - private Optional two = Optional.of("stuff"); - public Optional getTwo(){ return two; } - public void setTwo(Optional v) { two = v; } - -} diff --git a/src_old/test/java/co/blocke/scalajack/OnSetter.java b/src_old/test/java/co/blocke/scalajack/OnSetter.java deleted file mode 100644 index 279893a7..00000000 --- a/src_old/test/java/co/blocke/scalajack/OnSetter.java +++ /dev/null @@ -1,11 +0,0 @@ -package co.blocke.scalajack; - -import co.blocke.scalajack.*; - -public class OnSetter { - - private int two; - public int getTwo(){ return two; } - @Change(name="dos") public void setTwo(int v) { two = v; } - -} \ No newline at end of file diff --git a/src_old/test/java/co/blocke/scalajack/Temperature.java b/src_old/test/java/co/blocke/scalajack/Temperature.java deleted file mode 100644 index 1c2351b8..00000000 --- a/src_old/test/java/co/blocke/scalajack/Temperature.java +++ /dev/null @@ -1,6 +0,0 @@ -package co.blocke.scalajack; - -public enum Temperature -{ - Hot, Cold; -} diff --git a/src_old/test/java/co/blocke/scalajack/Unsupported.java b/src_old/test/java/co/blocke/scalajack/Unsupported.java deleted file mode 100644 index f7c41fc6..00000000 --- a/src_old/test/java/co/blocke/scalajack/Unsupported.java +++ /dev/null @@ -1,5 +0,0 @@ -package co.blocke.scalajack; - -public class Unsupported { - public Unsupported(String item){} -} diff --git a/src_old/test/scala/co.blocke.scalajack/ConverterSpec.scala b/src_old/test/scala/co.blocke.scalajack/ConverterSpec.scala deleted file mode 100644 index a551c0b6..00000000 --- a/src_old/test/scala/co.blocke.scalajack/ConverterSpec.scala +++ /dev/null @@ -1,173 +0,0 @@ -package co.blocke.scalajack - -import co.blocke.scalajack.json.JsonFlavor -import co.blocke.scalajack.model.JackFlavor -import TestUtil._ -import munit._ -import munit.internal.console - -import yaml._ -import json._ -import json4s._ -import delimited._ -import Converters._ -import org.json4s._ - -trait Human -case class Person(name: String, age: Int) extends Human -case class Typey[T](thing: T) { - type foom = T -} - -class ConverterSpec extends FunSuite: - - implicit val sj: JsonFlavor = ScalaJack() - implicit val sjY: JackFlavor[YAML] = ScalaJack(YamlFlavor()) - implicit val sjV: JackFlavor[JValue] = ScalaJack(Json4sFlavor()) - implicit val sjD: JackFlavor[DELIMITED] = ScalaJack(DelimitedFlavor()) - - val simple: Person = Person("Fred", 34) - val complex: Typey[Human] = Typey[Human](Person("Fred", 34)) - val simpleJson = """{"name":"Fred","age":34}""".asInstanceOf[JSON] - val complexJson = """{"foom":"co.blocke.scalajack.Person","thing":{"name":"Fred","age":34}}""".asInstanceOf[JSON] - val simpleJson4s = JObject(List(("name", JString("Fred")), ("age", JInt(34)))) - val complexJson4s = JObject( - List( - ("foom", JString("co.blocke.scalajack.Person")), - ("thing", JObject(List(("name", JString("Fred")), ("age", JInt(34))))) - )) - val simpleYaml = """name: Fred - |age: 34 - |""".stripMargin.asInstanceOf[YAML] - val complexYaml = """foom: co.blocke.scalajack.Person - |thing: - | name: Fred - | age: 34 - |""".stripMargin.asInstanceOf[YAML] - val simpleDelimited = "Fred,34".asInstanceOf[DELIMITED] - - test("mapJson") { - describe( - "-----------------------------\n: Converter Mapping Tests :\n-----------------------------", Console.BLUE - ) - assertEquals(simpleJson.mapJson[Person](_.copy(age = 45)), """{"name":"Fred","age":45}""".asInstanceOf[JSON]) - } - - test("mapJson4s") { - assertEquals(simpleJson4s.mapJson4s[Person](_.copy(age = 45)), JObject(List(("name", JString("Fred")), ("age", JInt(45))))) - } - - test("mapYaml") { - assertEquals(simpleYaml.mapYaml[Person](_.copy(age = 45)), """name: Fred - |age: 45 - |""".stripMargin.asInstanceOf[YAML]) - } - - test("mapDelimited") { - assertEquals(simpleDelimited.mapDelimited[Person](_.copy(age = 45)), """Fred,45""".asInstanceOf[DELIMITED]) - } - - test("mapJsonTo") { - assertEquals(simpleJson.mapJsonTo[Person, YAML](ScalaJack(YamlFlavor()))(_.copy(age = 45)), """name: Fred - |age: 45 - |""".stripMargin.asInstanceOf[YAML]) - } - - test("mapJson4sTo") { - assertEquals(simpleJson4s.mapJson4sTo[Person, JSON](ScalaJack())(_.copy(age = 45)), """{"name":"Fred","age":45}""".asInstanceOf[JSON]) - } - - test("mapYamlTo") { - assertEquals(simpleYaml.mapYamlTo[Person, JSON](ScalaJack())(_.copy(age = 45)), """{"name":"Fred","age":45}""".asInstanceOf[JSON]) - } - - test("mapDelimitedTo") { - assertEquals(simpleDelimited.mapDelimitedTo[Person, JSON](ScalaJack())(_.copy(age = 45)), """{"name":"Fred","age":45}""".asInstanceOf[JSON]) - } - - test("toJson") { - describe( - "---------------------------------\n: Convenience \"to/from\" Tests :\n---------------------------------", Console.BLUE - ) - assertEquals((simple: Person).toJson, simpleJson) - assertEquals(toJson[Person](simple), simpleJson) - assertEquals(toJson[Typey[Human]](complex), complexJson) - } - - test("fromJson") { - assertEquals(simpleJson.fromJson[Person], simple) - assertEquals(complexJson.fromJson[Typey[Human]], complex) - } - - test("toJson4s") { - assertEquals(toJson4s[Person](simple), simpleJson4s) - assertEquals(toJson4s[Typey[Human]](complex), complexJson4s) - } - - test("fromJson4s") { - assertEquals(simpleJson4s.fromJson4s[Person], simple) - assertEquals(complexJson4s.fromJson4s[Typey[Human]], complex) - } - - test("toYaml") { - assertEquals(toYaml[Person](simple), simpleYaml) - assertEquals(toYaml[Typey[Human]](complex), complexYaml) - } - - test("fromYaml") { - assertEquals(simpleYaml.fromYaml[Person], simple) - assertEquals(complexYaml.fromYaml[Typey[Human]], complex) - } - - test("toDelimited") { - assertEquals(toDelimited[Person](simple), simpleDelimited) - } - - test("fromDelimited") { - assertEquals(simpleDelimited.fromDelimited[Person], simple) - } - - test("yamlToJson") { - describe( - "----------------------\n: Converters Tests :\n----------------------", Console.BLUE - ) - assertEquals(simpleYaml.yamlToJson[Person], simpleJson) - assertEquals(complexYaml.yamlToJson[Typey[Human]], complexJson) - } - - test("yamlToJson4s") { - assertEquals(simpleYaml.yamlToJson4s[Person], simpleJson4s) - assertEquals(complexYaml.yamlToJson4s[Typey[Human]], complexJson4s) - } - - test("jsonToYaml") { - assertEquals(simpleJson.jsonToYaml[Person], simpleYaml) - assertEquals(complexJson.jsonToYaml[Typey[Human]], complexYaml) - } - - test("jsonToJson4s") { - assertEquals(simpleJson.jsonToJson4s[Person], simpleJson4s) - assertEquals(complexJson.jsonToJson4s[Typey[Human]], complexJson4s) - } - - test("delimitedToYaml") { - assertEquals(simpleDelimited.delimitedToYaml[Person], simpleYaml) - } - - test("delimitedToJson4s") { - assertEquals(simpleDelimited.delimitedToJson4s[Person], simpleJson4s) - } - - test("delimitedToJson") { - assertEquals(simpleDelimited.delimitedToJson[Person], simpleJson) - } - - test("json4sToYaml") { - assertEquals(simpleJson4s.json4sToYaml[Person], simpleYaml) - assertEquals(complexJson4s.json4sToYaml[Typey[Human]], complexYaml) - } - - test("json4sToJson") { - assertEquals(simpleJson4s.json4sToJson[Person], simpleJson) - assertEquals(complexJson4s.json4sToJson[Typey[Human]], complexJson) - } diff --git a/src_old/test/scala/co.blocke.scalajack/JsonDiff.scala b/src_old/test/scala/co.blocke.scalajack/JsonDiff.scala deleted file mode 100644 index acafcefa..00000000 --- a/src_old/test/scala/co.blocke.scalajack/JsonDiff.scala +++ /dev/null @@ -1,58 +0,0 @@ -package co.blocke.scalajack -package json - -import org.json4s.JsonAST.{ JNothing, JObject, JValue } - -object JsonDiff { - - def compare( - left: JValue, - right: JValue, - leftLabel: String = "left", - rightLabel: String = "right"): Seq[JsonDiff] = { - (left, right) match { - case (JObject(leftFields), JObject(rightFields)) => - val allFieldNames = - (leftFields.map(_._1) ++ rightFields.map(_._1)).distinct - allFieldNames.sorted flatMap { fieldName => - val leftFieldValue = leftFields - .collectFirst({ case (`fieldName`, fieldValue) => fieldValue }) - .getOrElse(JNothing) - val rightFieldValue = rightFields - .collectFirst({ case (`fieldName`, fieldValue) => fieldValue }) - .getOrElse(JNothing) - compare(leftFieldValue, rightFieldValue, leftLabel, rightLabel) - } - - // ---- Not used/needed at present, and I have questions about the correct behavior here. Exactly how do you - // "diff" two arrays (not necessarily homogeneous typed)? - // - // case (JArray(leftElements), JArray(rightElements)) => - // (0 until (leftElements.size max rightElements.size)) flatMap { elementIndex => - // val leftElement = leftElements.applyOrElse(elementIndex, (_: Int) => JNothing) - // val rightElement = rightElements.applyOrElse(elementIndex, (_: Int) => JNothing) - // compare(path \ elementIndex, leftElement, rightElement, leftLabel, rightLabel) - // } - - case _ => - if (left == right) { - Seq.empty - } else { - val outerLeft = left - val outerRight = right - Seq(new JsonDiff { - override val left: JValue = outerLeft - override val right: JValue = outerRight - override def toString: String = - s"JsonDiff($leftLabel: $left, $rightLabel: $right)" - }) - } - } - } - -} - -trait JsonDiff { - val left: JValue - val right: JValue -} diff --git a/src_old/test/scala/co.blocke.scalajack/JsonMatcher.scala b/src_old/test/scala/co.blocke.scalajack/JsonMatcher.scala deleted file mode 100644 index ab19edc6..00000000 --- a/src_old/test/scala/co.blocke.scalajack/JsonMatcher.scala +++ /dev/null @@ -1,21 +0,0 @@ -package co.blocke.scalajack -package json - -import co.blocke.scalajack.json.JSON -import org.json4s.JsonAST.JValue -import org.json4s.native.JsonMethods._ -import org.json4s.string2JsonInput - -object JsonMatcher { - - def jsonMatches( expected: JSON, actual: JSON ): Boolean = - val diffs = JsonDiff.compare( - parseJValue(expected.asInstanceOf[String]), - parseJValue(actual.asInstanceOf[String]), - "expected", - "actual" - ) - diffs.isEmpty - - implicit def parseJValue(string: String): JValue = parse(string) -} diff --git a/src_old/test/scala/co.blocke.scalajack/TestUtil.scala b/src_old/test/scala/co.blocke.scalajack/TestUtil.scala deleted file mode 100644 index c8365bdf..00000000 --- a/src_old/test/scala/co.blocke.scalajack/TestUtil.scala +++ /dev/null @@ -1,42 +0,0 @@ -package co.blocke.scalajack - -import munit.internal.console - -object TestUtil { - - inline def describe(message: String, color: String = Console.MAGENTA): Unit = println(s"$color$message${Console.RESET}") - inline def pending = describe(" << Test Pending (below) >>", Console.YELLOW) - - def hexStringToByteArray(s: String): Array[Byte] = { - val len = s.length - val data = new Array[Byte](len / 2) - var i = 0 - while ({ - i < len - }) { - data(i / 2) = ((Character.digit(s.charAt(i), 16) << 4) + Character.digit( - s.charAt(i + 1), - 16 - )).toByte - - i += 2 - } - data - } - - // Utility to generate test code quickly - def showException(label: String, fnStr: String, fn: () => Any) = - try { - fn() - } catch { - case x: IndexOutOfBoundsException => throw x - case t: Throwable => - if (!t.getMessage.contains("\n")) - throw t - val msg = "\"\"\"" + t.getMessage().replace("\n", "\n |") + "\"\"\"" - println( - label + " >> " + t.getClass.getName + "\n-----------------------\n" + - s"val msg = $msg.stripMargin\nthe[${t.getClass.getName}] thrownBy $fnStr should have message msg\n" - ) - } -} \ No newline at end of file diff --git a/src_old/test/scala/co.blocke.scalajack/delimited/DelimitedSpec.scala b/src_old/test/scala/co.blocke.scalajack/delimited/DelimitedSpec.scala deleted file mode 100644 index 13c44bed..00000000 --- a/src_old/test/scala/co.blocke.scalajack/delimited/DelimitedSpec.scala +++ /dev/null @@ -1,374 +0,0 @@ -package co.blocke.scalajack -package delimited - -import TestUtil._ -import munit._ -import munit.internal.console -import co.blocke.scala_reflection.RType - - -class DelimSpec extends FunSuite: - - val sj = ScalaJack(DelimitedFlavor()) - - test("Basic DelimSpec") { - describe("---------------------\n: Delimited Tests :\n---------------------", Console.BLUE) - describe("DelimSpec") - - val all = AllPrim( - 5, - 25L, - 123.45, - 12.3F, - 'x', - "Hey", - b = true, - BigInt(12345678), - BigDecimal(0.123458867) - ) - val delim = sj.render(all) - assertEquals(delim, """5,25,123.45,12.3,x,Hey,true,12345678,0.123458867""".asInstanceOf[DELIMITED]) - val inst = sj.read[AllPrim](delim) - assertEquals(inst, all) - } - - test("SalaJack configurations (DelimitedFlavor)") { - intercept[ScalaJackError]{ - ScalaJack(DelimitedFlavor()).withAdapters() - } - intercept[ScalaJackError]{ - ScalaJack(DelimitedFlavor()).withDefaultHint(null) - } - intercept[ScalaJackError]{ - ScalaJack(DelimitedFlavor()).withHintModifiers() - } - intercept[ScalaJackError]{ - ScalaJack(DelimitedFlavor()).withHints() - } - intercept[ScalaJackError]{ - ScalaJack(DelimitedFlavor()).withTypeValueModifier(null) - } - intercept[ScalaJackError]{ - ScalaJack(DelimitedFlavor()).allowPermissivePrimitives() - } - val sjx = ScalaJack(DelimitedFlavor()) - .parseOrElse(RType.of[Address] -> RType.of[DefaultAddress]) - assert(sjx.read[Address]("a;sdlfj".asInstanceOf[DELIMITED]) == DefaultAddress("a;sdlfj")) - } - - test("Enum support") { - val sjy = ScalaJack(DelimitedFlavor()).enumsAsInts() - val i = Shirt(1, Size.Small) - val d = sjy.render(i) - assertEquals(d, "1,0".asInstanceOf[DELIMITED]) - assertEquals(sjy.read[Shirt](d), i) - - val sjz = ScalaJack(DelimitedFlavor()) - val d2 = sjz.render(i) - assertEquals(d2, "1,Small".asInstanceOf[DELIMITED]) - assertEquals(sjz.read[Shirt](d2), i) - - val d3 = "1,\"Small\"".asInstanceOf[DELIMITED] - assertEquals(sjz.read[Shirt](d3), i) - - val d4 = "1,0".asInstanceOf[DELIMITED] - assertEquals(sjz.read[Shirt](d4), i) - - val d6 = "1,Huge".asInstanceOf[DELIMITED] - val msg = - """No value found in enumeration co.blocke.scalajack.delimited.Size$ for Huge - |1,Huge - |--^""".stripMargin - interceptMessage[ScalaJackError](msg){ - sjz.read[Shirt](d6) - } - } - - test("Empty input") { - assertEquals(sj.render("".asInstanceOf[DELIMITED]), "".asInstanceOf[DELIMITED]) - assertEquals(sj.read[Shirt]("".asInstanceOf[DELIMITED]), null) - } - - test("Unnecessary quotes on fields work") { // "a",b,"c" - val delim = "\"a\",b,\"c\"" - val inst = sj.read[ThreeStrings](delim.asInstanceOf[DELIMITED]) - assertEquals(inst, ThreeStrings("a", "b", "c")) - } - - test("Escaped quotes in string work") { // a,b""c,d - val delim = "a,\"b\"\"x\",c" - val inst = sj.read[ThreeStrings](delim.asInstanceOf[DELIMITED]) - assertEquals(inst, ThreeStrings("a", "b\"x", "c")) - } - - test("Basic fails") { - val delim = """5,25,123.45,12.3,x,Hey,true,bogus12345678,0.123458867""" - val msg = - """Expected a Number here - |5,25,123.45,12.3,x,Hey,true,bogus12345678,0.123458867 - |----------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[AllPrim](delim.asInstanceOf[DELIMITED]) - } - } - - test("Nested case class") { - describe("Classes") - val n = Nested("item", Inside(1, "One"), Inside(2, "Two")) - val delim = sj.render(n) - assertEquals(delim, """item,"1,One","2,Two"""".asInstanceOf[DELIMITED]) - val inst = sj.read[Nested](delim) - assertEquals(inst, n) - } - - test("Null class field value (nullable) w/no default") { - val delim = """item,,"2,Two"""".asInstanceOf[DELIMITED] - val inst = Nested("item", null, Inside(2, "Two")) - assertEquals(sj.read[Nested](delim), inst) - assertEquals(sj.render(inst), delim) - } - - test("Empty (String) and null primitive class field") { - val delim = ""","1,One","2,Two"""".asInstanceOf[DELIMITED] - val inst = Nested("", Inside(1, "One"), Inside(2, "Two")) - assertEquals(sj.read[Nested](delim), inst) - assertEquals(sj.render(inst), delim) - } - - test("Null class field value with default") { - val delim = """item,"1,One",""".asInstanceOf[DELIMITED] - assertEquals(sj.read[Nested](delim), - Nested("item", Inside(1, "One"), Inside(99, "dunno")) - ) - } - - test("Simple list works") { - describe("Lists") - val s = WithList(5, List("a", "b", "c")) - val delim = sj.render(s) - assertEquals(delim, """5,"a,b,c"""".asInstanceOf[DELIMITED]) - assertEquals(sj.read[WithList[String]](delim), s) - } - - test("Nullables") { - val s = WithList(5, List(BigInt(5), null, BigInt(7))) - val delim = sj.render(s) - assertEquals(delim, """5,"5,,7"""".asInstanceOf[DELIMITED]) - assertEquals(sj.read[WithList[BigInt]](delim), s) - } - - test("Null list") { - val s = WithList[Int](5, null) - val delim = sj.render[WithList[Int]](s) - assertEquals(delim, "5,".asInstanceOf[DELIMITED]) - } - - test("Empty List") { - val inst = sj.read[WithList[Int]]("5,".asInstanceOf[DELIMITED]) - assertEquals(inst, WithList(5, null)) - assertEquals(sj.render(inst), "5,".asInstanceOf[DELIMITED]) - } - - test("Non-nullables") { - val delim = """5,"1,,3"""".asInstanceOf[DELIMITED] - val msg = """Expected a Number here - |,,3 - |-^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[WithList[Int]](delim) - } - } - - test("Nested lists") { - val s = WithList(3, List(List("a,b", "c"), List("x", "y"))) - val delim = sj.render(s) - assertEquals(delim , "3,\"\"\"\"\"\"\"a,b\"\"\"\",c\"\",\"\"x,y\"\"\"".asInstanceOf[DELIMITED]) - assertEquals(sj.read[WithList[List[String]]](delim), s) - } - - test("Optional case class fields") { - describe("Options") - val s = HasOption(None, Some(7)) - val delim = sj.render(s) - assertEquals(delim, ",7".asInstanceOf[DELIMITED]) - assertEquals(sj.read[HasOption](delim), s) - } - - test("Missing optional case class field with default") { - val s = HasOption(Some(7), None) - val delim = sj.render(s) - assertEquals(delim, "7,".asInstanceOf[DELIMITED]) - assertEquals(sj.read[HasOption](delim), HasOption(Some(7), Some(5))) - } - - test("Optional list members") { - val s = WithList(3, List(Some(1), None, Some(2))) - val delim = sj.render(s) - assertEquals(delim, "3,\"1,2\"".asInstanceOf[DELIMITED]) - assertEquals(sj.read[WithList[Option[Int]]](delim), - WithList(3, List(Some(1), Some(2))) - ) - assertEquals(sj.read[WithList[Option[Int]]]("3,\"1,,2\"".asInstanceOf[DELIMITED]), s) - } - - test("Tuple case class fields") { - describe("Tuples") - val s = HasTuples(("a", 3), (false, 9)) - val delim = sj.render(s) - assertEquals(delim, "\"a,3\",\"false,9\"".asInstanceOf[DELIMITED]) - assertEquals(sj.read[HasTuples](delim), s) - } - - test("Tuple with class value") { - val delim = "\"thing,\"\"1,foo\"\"\"".asInstanceOf[DELIMITED] - assertEquals(sj.read[HasTuples2](delim), - HasTuples2(("thing", Inside(1, "foo"))) - ) - } - - test("Tuple with null value") { - val delim = "\"thing,\"".asInstanceOf[DELIMITED] - assertEquals(sj.read[HasTuples2](delim), HasTuples2(("thing", null))) - } - - test("Tuple with escaped quote in value") { - val s = HasTuples(("a\"b", 3), (false, 9)) - val delim = sj.render(s) - assertEquals(delim , "\"\"\"a\"\"\"\"b\"\",3\",\"false,9\"".asInstanceOf[DELIMITED]) - assertEquals(sj.read[HasTuples](delim), s) - } - - test("Missing optional case class field with default") { - val delim = "\"a,3\",".asInstanceOf[DELIMITED] - assertEquals(sj.read[HasTuples](delim), HasTuples(("a", 3), (true, 1))) - } - - test("Tuple with Classish elements") { - val inst = HasTuples3((true, Inside(4, "foom"))) - val delim = sj.render(inst) - assertEquals(delim, "\"true,\"\"4,foom\"\"\"".asInstanceOf[DELIMITED]) - assertEquals(sj.read[HasTuples3](delim), inst) - } - - test("Supports Either parsing") { - describe("Either") - val s1 = HasEither(1, Left(3)) - val delim1 = sj.render(s1) - assertEquals(delim1, "1,3".asInstanceOf[DELIMITED]) - assertEquals(sj.read[HasEither](delim1), s1) - - val s2 = HasEither(2, Right(Inside(99, "foo"))) - val delim2 = sj.render(s2) - assertEquals(delim2, "2,\"99,foo\"".asInstanceOf[DELIMITED]) - assertEquals(sj.read[HasEither](delim2), s2) - - val s3 = HasEither(2, Right(null)) - assertEquals( sj.render(s3), "2,".asInstanceOf[DELIMITED]) - } - - test("Null object") { - assertEquals(sj.render[HasEither](null), "".asInstanceOf[DELIMITED]) - } - - test("Either missing a value (no default)") { - val delim = ",\"99,foo\"".asInstanceOf[DELIMITED] - val msg = """Expected a Number here - |,"99,foo" - |^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[HasEither](delim) - } - } - - test("Supports Either field value with default specified") { - val delim = "15,".asInstanceOf[DELIMITED] - val i = sj.read[HasEither2](delim) - assertEquals(i, HasEither2(15, Right(Inside(1, "ok")))) - } - - test("Can't parse either side of Either") { - val msg = - """Failed to read either side of Either - |3,true - |^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[Shirt2]("3,true".asInstanceOf[DELIMITED]) - } - } - - test("Either with embedded quote in string value") { - val s = HasEither3(1, Left("a\"b")) - val delim = sj.render(s) - assertEquals(delim, """1,"a""b"""".asInstanceOf[DELIMITED]) - assertEquals(sj.read[HasEither3](delim), s) - } - - test("Write a null Either value") { - val delim = "5,".asInstanceOf[DELIMITED] - val inst = HasEither(5, null) - assertEquals(sj.render(inst), delim) - assertEquals(sj.read[HasEither](delim), inst) - } - - test("Write a Right non-Classish value") { - val delim = "5,true".asInstanceOf[DELIMITED] - val inst = HasEitherRev(5, Right(true)) - assertEquals(sj.render(inst), delim) - assertEquals(sj.read[HasEitherRev](delim), inst) - } - - test("Write a Left Classish value") { - val delim = "5,\"15,Mike\"".asInstanceOf[DELIMITED] - val inst = HasEitherRev(5, Left(Inside(15, "Mike"))) - assertEquals(sj.render(inst), delim) - assertEquals(sj.read[HasEitherRev](delim), inst) - } - - test("Non-case classes fail") { - describe("Plug coverage holes") - val delim = "John,35".asInstanceOf[DELIMITED] - val msg = - """Only case classes with non-empty constructors are supported for delimited data. - |John,35 - |^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[Busted](delim) - } - } - - test("No Map support") { - val delim = "John,35".asInstanceOf[DELIMITED] - val msg = """No Map support for delimited data. - |John,35 - |-----^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[Busted2](delim) - } - val msg2 = "Map-typed data is not supported for delimited output" - interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ - sj.render(Busted2("foo", Map("a" -> "b"))) - } - } - - test("Write null string") { - assertEquals(sj.render(Inside(5, null)), "5,".asInstanceOf[DELIMITED]) - } - - test("Invalid Boolean value") { - val delim = "John,35".asInstanceOf[DELIMITED] - val msg = """Expected a Boolean here - |John,35 - |-----^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[Busted3](delim) - } - } - - test("Null List value") { - val delim = "John,".asInstanceOf[DELIMITED] - assertEquals(sj.read[Busted4](delim), Busted4("John", null)) - } - - test("Parser") { - sj.parse("John,".asInstanceOf[DELIMITED]) - } diff --git a/src_old/test/scala/co.blocke.scalajack/delimited/Model.scala b/src_old/test/scala/co.blocke.scalajack/delimited/Model.scala deleted file mode 100644 index c6daab5f..00000000 --- a/src_old/test/scala/co.blocke.scalajack/delimited/Model.scala +++ /dev/null @@ -1,36 +0,0 @@ -package co.blocke.scalajack -package delimited - -case class AllPrim(i: Int, l: Long, d: Double, f: Float, c: Char, s: String, b: Boolean, bi: BigInt, bd: BigDecimal) - -case class Inside(id: Int, desc: String) -case class Nested(thing: String, in: Inside, other: Inside = Inside(99, "dunno")) - -case class ThreeStrings(a: String, b: String, c: String) - -case class WithList[T](id: Int, someList: List[T]) - -case class HasOption(one: Option[Int], two: Option[Int] = Some(5)) - -case class HasTuples(one: (String, Int), two: (Boolean, Int) = (true, 1)) -case class HasTuples2(one: (String, Inside)) -case class HasTuples3(a: (Boolean, Inside)) - -case class HasEither(one: Int, two: Either[Int, Inside]) -case class HasEitherRev(one: Int, two: Either[Inside, Boolean]) -case class HasEither2(one: Int, two: Either[Int, Inside] = Right(Inside(1, "ok"))) -case class HasEither3(one: Int, two: Either[String, Inside]) - -object Size extends Enumeration { - val Small, Medium, Large = Value -} -case class Shirt(id: Int, size: Size.Value) -case class Shirt2(id: Int, size: Either[Size.Value, Int]) - -class Busted(val name: String, val age: Int) -case class Busted2(name: String, m: Map[String, String]) -case class Busted3(name: String, isOk: Boolean) -case class Busted4(name: String, stuff: List[Int]) - -trait Address { val postalCode: String } -case class DefaultAddress( postalCode: String = "unknown" ) extends Address \ No newline at end of file diff --git a/src_old/test/scala/co.blocke.scalajack/json/collections/AnyCollections.scala b/src_old/test/scala/co.blocke.scalajack/json/collections/AnyCollections.scala deleted file mode 100644 index 83f1d8eb..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json/collections/AnyCollections.scala +++ /dev/null @@ -1,66 +0,0 @@ -package co.blocke.scalajack -package json.collections - -import TestUtil._ -import munit._ -import munit.internal.console -import co.blocke.scalajack.json.JSON -import co.blocke.scala_reflection._ - -class AnyCollections() extends FunSuite: - - val sj = co.blocke.scalajack.ScalaJack() - - test("List works (Int)") { - describe("--------------------------\n: Any Collection Tests :\n--------------------------", Console.BLUE) - val inst: Any = List(1, 2, 3) - val js = sj.render(inst) - assertEquals("""[1,2,3]""".asInstanceOf[JSON], js) - assert(List(1, 2, 3) == sj.read[Any](js)) - } - - test("List works (String)") { - val inst: Any = List("one", "two", "three") - val js = sj.render(inst) - assertEquals("""["one","two","three"]""".asInstanceOf[JSON], js) - assert(List("one", "two", "three") == sj.read[Any](js)) - } - - test("First-Level List works (Class)") { - val inst: Any = List(Player("Mike", 34), Player("Sarah", 29)) - val js = sj.render(inst) - assertEquals( - """[{"_hint":"co.blocke.scalajack.json.collections.Player","name":"Mike","age":34},{"_hint":"co.blocke.scalajack.json.collections.Player","name":"Sarah","age":29}]""".asInstanceOf[JSON], js) - assert(List(Player("Mike", 34), Player("Sarah", 29)) == sj.read[List[Any]](js)) - } - - test("Map works (Int,Int)") { - val inst: Any = Map(1 -> 2, 3 -> 4) - val js = sj.render(inst) - assertEquals("""{"1":2,"3":4}""".asInstanceOf[JSON], js) - assertEquals(inst,sj.read[Any](js)) - } - - test("Map works (String,Int)") { - val inst: Any = Map("yes" -> 1, "no" -> 2) - val js = sj.render(inst) - assertEquals("""{"yes":1,"no":2}""".asInstanceOf[JSON], js) - assert(Map("yes" -> 1, "no" -> 2) == sj.read[Any](js)) - } - - test("First-Level Map works (Class)") { - val js = """{"_hint":"co.blocke.scalajack.json.collections.Player","name":"Mike","age":34}""".asInstanceOf[JSON] - assert(Player("Mike", 34) == sj.read[Any](js)) - } - - test("Second-Level Map works (Class keys) (Class)") { - val js = - """{"{\"_hint\":\"co.blocke.scalajack.json.collections.Player\",\"name\":\"Mike\",\"age\":34}":15, "{\"name\":\"Mike\",\"age\":34}":16}""".asInstanceOf[JSON] - assert(Map(Player("Mike", 34) -> 15, Map("name" -> "Mike", "age" -> 34) -> 16) == sj.read[Any](js)) - } - - test("Second-Level Map (List keys) works (Class)") { - val js = - """{"[{\"_hint\":\"co.blocke.scalajack.json.collections.Player\",\"name\":\"Mike\",\"age\":34},{\"name\":\"Mike\",\"age\":34}]":15}""".asInstanceOf[JSON] - assert(Map(List(Player("Mike", 34), Map("name" -> "Mike", "age" -> 34)) -> 15) == sj.read[Any](js)) - } diff --git a/src_old/test/scala/co.blocke.scalajack/json/collections/Arrays.scala b/src_old/test/scala/co.blocke.scalajack/json/collections/Arrays.scala deleted file mode 100644 index 1993b1dd..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json/collections/Arrays.scala +++ /dev/null @@ -1,244 +0,0 @@ -package co.blocke.scalajack -package json.collections - -import co.blocke.scala_reflection._ -import scala.math._ -import java.util.UUID -import TestUtil._ -import munit._ -import munit.internal.console -import co.blocke.scalajack.json.JSON - -class Arrays() extends FunSuite: - - val sj = co.blocke.scalajack.ScalaJack() - - test("BigDecimal must work") { - describe("-----------------------\n: Scala Array Tests :\n-----------------------", Console.BLUE) - describe("+++ Primitive Types +++") - - val inst = BigDecimalArr(null, Array(BigDecimal(123.456),BigDecimal(78.91))) - val js = sj.render(inst) - assertEquals( - """{"a1":null,"a2":[123.456,78.91]}""".asInstanceOf[JSON], - js - ) - val i2 = sj.read[BigDecimalArr](js) - assertEquals(i2.a1, null) - assert(inst.a2.sameElements(i2.a2)) - } - - test("BigInt must work") { - val inst = BigIntArr(null, Array(BigInt(123),BigInt(78))) - val js = sj.render(inst) - assertEquals( - """{"a1":null,"a2":[123,78]}""".asInstanceOf[JSON], - js - ) - val i2 = sj.read[BigIntArr](js) - assertEquals(i2.a1, null) - assert(inst.a2.sameElements(i2.a2)) - } - - test("Boolean must work") { - val inst = BooleanArr(null, Array(true,false)) - val js = sj.render(inst) - assertEquals( - """{"a1":null,"a2":[true,false]}""".asInstanceOf[JSON], - js - ) - val i2 = sj.read[BooleanArr](js) - assertEquals(i2.a1, null) - assert(inst.a2.sameElements(i2.a2)) - } - - // No Array[Byte] because that's seen as binary data and treated differently - test("Char must work") { - val inst = CharArr(null, Array('a','b','c')) - val js = sj.render(inst) - assertEquals( - """{"a1":null,"a2":["a","b","c"]}""".asInstanceOf[JSON], - js - ) - val i2 = sj.read[CharArr](js) - assertEquals(i2.a1, null) - assert(inst.a2.sameElements(i2.a2)) - } - - test("Double must work") { - val inst = DoubleArr(null, Array(12.34,56.78)) - val js = sj.render(inst) - assertEquals( - """{"a1":null,"a2":[12.34,56.78]}""".asInstanceOf[JSON], - js - ) - val i2 = sj.read[DoubleArr](js) - assertEquals(i2.a1, null) - assert(inst.a2.sameElements(i2.a2)) - } - - test("Float must work") { - val inst = FloatArr(null, Array(12.34F,56.78F)) - val js = sj.render(inst) - assertEquals( - """{"a1":null,"a2":[12.34,56.78]}""".asInstanceOf[JSON], - js - ) - val i2 = sj.read[FloatArr](js) - assertEquals(i2.a1, null) - assert(inst.a2.sameElements(i2.a2)) - } - - test("Int must work") { - val inst = IntArr(null, Array(1,2,3)) - val js = sj.render(inst) - assertEquals( - """{"a1":null,"a2":[1,2,3]}""".asInstanceOf[JSON], - js - ) - val i2 = sj.read[IntArr](js) - assertEquals(i2.a1, null) - assert(inst.a2.sameElements(i2.a2)) - } - - test("Long must work") { - val inst = LongArr(null, Array(1L,2L,3L)) - val js = sj.render(inst) - assertEquals( - """{"a1":null,"a2":[1,2,3]}""".asInstanceOf[JSON], - js - ) - val i2 = sj.read[LongArr](js) - assertEquals(i2.a1, null) - assert(inst.a2.sameElements(i2.a2)) - } - - test("Short must work") { - val inst = ShortArr(null, Array(1.toShort,2.toShort,3.toShort)) - val js = sj.render(inst) - assertEquals( - """{"a1":null,"a2":[1,2,3]}""".asInstanceOf[JSON], - js - ) - val i2 = sj.read[ShortArr](js) - assertEquals(i2.a1, null) - assert(inst.a2.sameElements(i2.a2)) - } - - test("String must work") { - val inst = StringArr(null, Array("a","b","c")) - val js = sj.render(inst) - assertEquals( - """{"a1":null,"a2":["a","b","c"]}""".asInstanceOf[JSON], - js - ) - val i2 = sj.read[StringArr](js) - assertEquals(i2.a1, null) - assert(inst.a2.sameElements(i2.a2)) - } - - test("Lists must work") { - describe("+++ Collection Types +++") - val inst = ListArr(Array( List(1,2,3), List(4,5,6) )) - val js = sj.render(inst) - assertEquals( - """{"a1":[[1,2,3],[4,5,6]]}""".asInstanceOf[JSON], - js - ) - val i2 = sj.read[ListArr](js) - assert(inst.a1.sameElements(i2.a1)) - - // Try another Seq variant - val inst2 = SetArr(Array( Set(1,2,3), Set(4,5,6) )) - val js2 = sj.render(inst2) - assertEquals( - """{"a1":[[1,2,3],[4,5,6]]}""".asInstanceOf[JSON], - js2 - ) - val i3: SetArr = sj.read[SetArr](js) - assert(inst2.a1.sameElements(i3.a1)) - } - - test("Maps must work") { - val inst = MapArr(Array( Map("a"->1,"b"->2), Map("c"->3,"d"->4) )) - val js = sj.render(inst) - assertEquals( - """{"a1":[{"a":1,"b":2},{"c":3,"d":4}]}""".asInstanceOf[JSON], - js - ) - val i2 = sj.read[MapArr](js) - assert(inst.a1.sameElements(i2.a1)) - } - - test("Classes must work") { - describe("+++ Class Types +++") - val inst = ClassArr(Array(IntArr(null,Array(1,2)), IntArr(null,Array(1,2)))) - val js = sj.render(inst) - assertEquals( - """{"a1":[{"a1":null,"a2":[1,2]},{"a1":null,"a2":[1,2]}]}""".asInstanceOf[JSON], - js - ) - val i2 = sj.read[ClassArr](js) - assertEquals(inst.a1.size, i2.a1.size) - assertEquals(i2.a1(0).a1,null) - assert(inst.a1(0).a2.sameElements(i2.a1(0).a2)) - assertEquals(i2.a1(1).a1,null) - assert(inst.a1(1).a2.sameElements(i2.a1(1).a2)) - } - - test("Multidimensional arrays must work") { - describe("+++ Complex Types +++") - val inst = MultiArr(null, Array(Array( Array(1L,2L), Array(3L,4L) ), Array(Array(5L,6L), Array(7L,8L)) ), - Array(Array(BigInt(12),BigInt(13)), Array(BigInt(14),BigInt(15)))) - val js = sj.render(inst) - assertEquals( - """{"a0":null,"a1":[[[1,2],[3,4]],[[5,6],[7,8]]],"a2":[[12,13],[14,15]]}""".asInstanceOf[JSON], - js - ) - val i2 = sj.read[MultiArr](js) - assertEquals(inst.a1.size, i2.a1.size) - assertEquals(inst.a2.size, i2.a2.size) - assertEquals(i2.a0,null) - assert(inst.a1(0)(0).sameElements(i2.a1(0)(0))) - assert(inst.a1(0)(1).sameElements(i2.a1(0)(1))) - assert(inst.a1(1)(0).sameElements(i2.a1(1)(0))) - assert(inst.a1(1)(1).sameElements(i2.a1(1)(1))) - assert(inst.a2(0).sameElements(i2.a2(0))) - assert(inst.a2(1).sameElements(i2.a2(1))) - } - - - test("Java primitives must work") { - describe("----------------------\n: Java Array Tests :\n----------------------", Console.BLUE) - describe("+++ Primitive Types +++") - val inst = JavaArray() - inst.setBigDecs(Array(java.math.BigDecimal.valueOf(123.4),java.math.BigDecimal.valueOf(456.7))) - inst.setBigInts(Array(java.math.BigInteger.valueOf(123),java.math.BigInteger.valueOf(456))) - inst.setBooleans(Array(java.lang.Boolean.valueOf(true),java.lang.Boolean.valueOf("false"))) - inst.setBytes(Array(java.lang.Byte.valueOf(24.toByte),java.lang.Byte.valueOf(12.toByte))) - inst.setCharacters(Array(java.lang.Character.valueOf('a'),java.lang.Character.valueOf('z'))) - inst.setDoubles(Array(java.lang.Double.valueOf(12.34),java.lang.Double.valueOf(-56.78)).asInstanceOf[Array[java.lang.Double]]) - inst.setFloats(Array(java.lang.Float.valueOf(12.34F),java.lang.Float.valueOf(-56.78F))) - inst.setIntegers(Array(java.lang.Integer.valueOf(1),java.lang.Integer.valueOf(2))) - inst.setLongs(Array(java.lang.Long.valueOf(1),java.lang.Long.valueOf(2))) - inst.setShorts(Array(java.lang.Short.valueOf(1.toShort),java.lang.Short.valueOf(2.toShort))) - inst.setMulti(Array(Array(java.math.BigInteger.valueOf(123),java.math.BigInteger.valueOf(456)), - Array(java.math.BigInteger.valueOf(543),java.math.BigInteger.valueOf(222)))) - val js = sj.render(inst) - assertEquals( - """{"bigDecs":[123.4,456.7],"bigInts":[123,456],"booleans":[true,false],"bytes":[24,12],"characters":["a","z"],"doubles":[12.34,-56.78],"floats":[12.34,-56.78],"integers":[1,2],"longs":[1,2],"multi":[[123,456],[543,222]],"shorts":[1,2]}""".asInstanceOf[JSON], - js) - val readIn = sj.read[JavaArray](js) - assert(inst.getBigDecs.sameElements(readIn.getBigDecs)) - assert(inst.getBigInts.sameElements(readIn.getBigInts)) - assert(inst.getBooleans.sameElements(readIn.getBooleans)) - assert(inst.getBytes.sameElements(readIn.getBytes)) - assert(inst.getCharacters.sameElements(readIn.getCharacters)) - assert(inst.getDoubles.sameElements(readIn.getDoubles)) - assert(inst.getFloats.sameElements(readIn.getFloats)) - assert(inst.getIntegers.sameElements(readIn.getIntegers)) - assert(inst.getLongs.sameElements(readIn.getLongs)) - assert(inst.getShorts.sameElements(readIn.getShorts)) - assert(inst.getMulti()(0).sameElements(readIn.getMulti()(0))) - assert(inst.getMulti()(1).sameElements(readIn.getMulti()(1))) - } diff --git a/src_old/test/scala/co.blocke.scalajack/json/collections/Maps.scala b/src_old/test/scala/co.blocke.scalajack/json/collections/Maps.scala deleted file mode 100644 index 9fac2c16..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json/collections/Maps.scala +++ /dev/null @@ -1,342 +0,0 @@ -package co.blocke.scalajack -package json.collections - -import co.blocke.scala_reflection._ -import scala.math._ -import java.util.UUID -import TestUtil._ -import munit._ -import munit.internal.console -import co.blocke.scalajack.json.JSON -import scala.collection.immutable._ -import scala.jdk.CollectionConverters._ -import scala.language.implicitConversions - -class Maps() extends FunSuite: - - val sj = co.blocke.scalajack.ScalaJack() - - test("BigDecimal must work") { - describe("---------------------\n: Scala Map Tests :\n---------------------", Console.BLUE) - describe("+++ Primitive Types +++") - - val inst = BigDecimalMap(null, Map(BigDecimal(123.456)->"a",BigDecimal(78.91)->"b"), Map("a"->BigDecimal(123.456),"b"->BigDecimal(78.91))) - val js = sj.render(inst) - assertEquals( - """{"a0":null,"a1":{"123.456":"a","78.91":"b"},"a2":{"a":123.456,"b":78.91}}""".asInstanceOf[JSON], - js - ) - assertEquals(inst, sj.read[BigDecimalMap](js)) - } - - test("BigInt must work") { - val inst = BigIntMap(null, Map(BigInt(123)->"a",BigInt(456)->"b"), Map("a"->BigInt(789),"b"->BigInt(321))) - val js = sj.render(inst) - assertEquals( - """{"a0":null,"a1":{"123":"a","456":"b"},"a2":{"a":789,"b":321}}""".asInstanceOf[JSON], - js - ) - assertEquals(inst, sj.read[BigIntMap](js)) - } - - test("Boolean must work") { - val inst = BooleanMap(null, Map(true->"a",false->"b"), Map("a"->true,"b"->false)) - val js = sj.render(inst) - assertEquals( - """{"a0":null,"a1":{"true":"a","false":"b"},"a2":{"a":true,"b":false}}""".asInstanceOf[JSON], - js - ) - assertEquals(inst, sj.read[BooleanMap](js)) - } - - test("Byte must work") { - val inst = ByteMap(null, Map(250.toByte->"a",200.toByte->"b"), Map("a"->150.toByte,"b"->100.toByte)) - val js = sj.render(inst) - assertEquals( - """{"a0":null,"a1":{"-6":"a","-56":"b"},"a2":{"a":-106,"b":100}}""".asInstanceOf[JSON], - js - ) - assertEquals(inst, sj.read[ByteMap](js)) - } - - test("Char must work") { - val inst = CharMap(null, Map('t'->"a",'u'->"b"), Map("a"->'v',"b"->'w')) - val js = sj.render(inst) - assertEquals( - """{"a0":null,"a1":{"t":"a","u":"b"},"a2":{"a":"v","b":"w"}}""".asInstanceOf[JSON], - js - ) - assertEquals(inst, sj.read[CharMap](js)) - } - - test("Double must work") { - val inst = DoubleMap(null, Map(12.34->"a",45.67->"b"), Map("a"->67.89,"b"->1923.432)) - val js = sj.render(inst) - assertEquals( - """{"a0":null,"a1":{"12.34":"a","45.67":"b"},"a2":{"a":67.89,"b":1923.432}}""".asInstanceOf[JSON], - js - ) - assertEquals(inst, sj.read[DoubleMap](js)) - } - - test("Float must work") { - val inst = FloatMap(null, Map(12.34F->"a",45.67F->"b"), Map("a"->67.89F,"b"->1923.432F)) - val js = sj.render(inst) - assertEquals( - """{"a0":null,"a1":{"12.34":"a","45.67":"b"},"a2":{"a":67.89,"b":1923.432}}""".asInstanceOf[JSON], - js - ) - assertEquals(inst, sj.read[FloatMap](js)) - } - - test("Int must work") { - val inst = IntMap2(null, Map(12->"a",-45->"b"), Map("a"->67,"b"->1923)) - val js = sj.render(inst) - assertEquals( - """{"a0":null,"a1":{"12":"a","-45":"b"},"a2":{"a":67,"b":1923}}""".asInstanceOf[JSON], - js - ) - assertEquals(inst, sj.read[IntMap2](js)) - } - - test("Long must work") { - val inst = LongMap2(null, Map(12L->"a",-45L->"b"), Map("a"->67L,"b"->1923L)) - val js = sj.render(inst) - assertEquals( - """{"a0":null,"a1":{"12":"a","-45":"b"},"a2":{"a":67,"b":1923}}""".asInstanceOf[JSON], - js - ) - assertEquals(inst, sj.read[LongMap2](js)) - } - - test("Short must work") { - val inst = ShortMap2(null, Map(12.toShort->"a",-45.toShort->"b"), Map("a"->67.toShort,"b"->19.toShort)) - val js = sj.render(inst) - assertEquals( - """{"a0":null,"a1":{"12":"a","-45":"b"},"a2":{"a":67,"b":19}}""".asInstanceOf[JSON], - js - ) - assertEquals(inst, sj.read[ShortMap2](js)) - } - - test("Arrays must work") { - describe("+++ Collection Types +++") - val inst = ArrayMap( Map("a"->Array(1,2,3),"b"->Array(4,5,6)), Map(Array(1,2,3)->"a",Array(4,5,6)->"b") ) - val js = sj.render(inst) - assertEquals( - """{"a1":{"a":[1,2,3],"b":[4,5,6]},"a2":{"[1,2,3]":"a","[4,5,6]":"b"}}""".asInstanceOf[JSON], - js - ) - val i2 = sj.read[ArrayMap](js) - assert( inst.a1("a").sameElements(i2.a1("a")) ) - assert( inst.a1("b").sameElements(i2.a1("b")) ) - assert( inst.a2.keySet.toList(0).sameElements(i2.a2.keySet.toList(0))) - assert( inst.a2.keySet.toList(1).sameElements(i2.a2.keySet.toList(1))) - } - - test("Maps must work") { - val inst = SeqMap2( Map("a"->List(1,2,3),"b"->List(4,5,6)), Map(List(1,2,3)->"a",List(4,5,6)->"b") ) - val js = sj.render(inst) - assertEquals( - """{"a1":{"a":[1,2,3],"b":[4,5,6]},"a2":{"[1,2,3]":"a","[4,5,6]":"b"}}""".asInstanceOf[JSON], - js - ) - assertEquals(inst, sj.read[SeqMap2](js)) - } - - test("Classes must work") { - describe("+++ Class Types +++") - val inst = ClassMap(Map("a"->IntArr(Array(1,2),null),"b"->IntArr(Array(3,4),null)), Map(IntArr(Array(1,2),null)->"a",IntArr(Array(3,4),null)->"b")) - val js = sj.render(inst) - assertEquals( - """{"a1":{"a":{"a1":[1,2],"a2":null},"b":{"a1":[3,4],"a2":null}},"a2":{"{\"a1\":[1,2],\"a2\":null}":"a","{\"a1\":[3,4],\"a2\":null}":"b"}}""".asInstanceOf[JSON], - js - ) - val i2 = sj.read[ClassMap](js) - assert( inst.a1("a").a1.sameElements(i2.a1("a").a1) ) - assertEquals( i2.a1("a").a2, null ) - assert( inst.a1("b").a1.sameElements(i2.a1("b").a1) ) - assertEquals( i2.a1("b").a2, null ) - assert( inst.a2.keySet.toList(0).a1.sameElements(i2.a2.keySet.toList(0).a1) ) - assertEquals( i2.a2.keySet.toList(0).a2, null ) - assert( inst.a2.keySet.toList(1).a1.sameElements(i2.a2.keySet.toList(1).a1) ) - assertEquals( i2.a2.keySet.toList(1).a2, null ) - } - - test("Multidimensional arrays must work") { - describe("+++ Complex Types +++") - val inst = MultiMap( Map( Map("a"->true,"b"->false)->1, Map("c"->true,"d"->false)->2), Map( 3->Map("a"->true,"b"->false), 4->Map("c"->true,"d"->false)) ) - val js = sj.render(inst) - assertEquals( - """{"a1":{"{\"a\":true,\"b\":false}":1,"{\"c\":true,\"d\":false}":2},"a2":{"3":{"a":true,"b":false},"4":{"c":true,"d":false}}}""".asInstanceOf[JSON], - js - ) - assertEquals(inst, sj.read[MultiMap](js)) - } - - test("BigDecimal must work") { - describe("--------------------\n: Java Map Tests :\n--------------------", Console.BLUE) - describe("+++ Primitive Types +++") - - val inst = JBigDecimalMap(null, new java.util.HashMap( Map(BigDecimal(123.456)->"a",BigDecimal(78.91)->"b").asJava ), java.util.HashMap( Map("a"->BigDecimal(123.456),"b"->BigDecimal(78.91)).asJava )) - val js = sj.render(inst) - assertEquals( - """{"a0":null,"a1":{"123.456":"a","78.91":"b"},"a2":{"a":123.456,"b":78.91}}""".asInstanceOf[JSON], - js - ) - assertEquals(inst, sj.read[JBigDecimalMap](js)) - } - - test("BigInt must work") { - val inst = JBigIntMap(null, new java.util.HashMap(Map(BigInt(123)->"a",BigInt(456)->"b").asJava), new java.util.HashMap(Map("a"->BigInt(789),"b"->BigInt(321)).asJava)) - val js = sj.render(inst) - assertEquals( - """{"a0":null,"a1":{"456":"b","123":"a"},"a2":{"a":789,"b":321}}""".asInstanceOf[JSON], - js - ) - assertEquals(inst, sj.read[JBigIntMap](js)) - } - - test("Boolean must work") { - val inst = JBooleanMap(null, java.util.HashMap(Map(true->"a",false->"b").asJava), java.util.HashMap(Map("a"->true,"b"->false).asJava)) - val js = sj.render(inst) - assertEquals( - """{"a0":null,"a1":{"false":"b","true":"a"},"a2":{"a":true,"b":false}}""".asInstanceOf[JSON], - js - ) - assertEquals(inst, sj.read[JBooleanMap](js)) - } - - test("Byte must work") { - val inst = JByteMap(null, java.util.HashMap(Map(250.toByte->"a",200.toByte->"b").asJava), java.util.HashMap(Map("a"->150.toByte,"b"->100.toByte).asJava)) - val js = sj.render(inst) - assertEquals( - """{"a0":null,"a1":{"-6":"a","-56":"b"},"a2":{"a":-106,"b":100}}""".asInstanceOf[JSON], - js - ) - assertEquals(inst, sj.read[JByteMap](js)) - } - - test("Char must work") { - val inst = JCharMap(null, java.util.HashMap(Map('t'->"a",'u'->"b").asJava), java.util.HashMap(Map("a"->'v',"b"->'w').asJava)) - val js = sj.render(inst) - assertEquals( - """{"a0":null,"a1":{"t":"a","u":"b"},"a2":{"a":"v","b":"w"}}""".asInstanceOf[JSON], - js - ) - assertEquals(inst, sj.read[JCharMap](js)) - } - - test("Double must work") { - val inst = JDoubleMap(null, java.util.HashMap(Map(12.34->"a",45.67->"b").asJava), java.util.HashMap(Map("a"->67.89,"b"->1923.432).asJava)) - val js = sj.render(inst) - assertEquals( - """{"a0":null,"a1":{"45.67":"b","12.34":"a"},"a2":{"a":67.89,"b":1923.432}}""".asInstanceOf[JSON], - js - ) - assertEquals(inst, sj.read[JDoubleMap](js)) - } - - test("Float must work") { - val inst = JFloatMap(null, java.util.HashMap(Map(12.34F->"a",45.67F->"b").asJava), java.util.HashMap(Map("a"->67.89F,"b"->1923.432F).asJava)) - val js = sj.render(inst) - assertEquals( - """{"a0":null,"a1":{"12.34":"a","45.67":"b"},"a2":{"a":67.89,"b":1923.432}}""".asInstanceOf[JSON], - js - ) - assertEquals(inst, sj.read[JFloatMap](js)) - } - - test("Int must work") { - val inst = JIntMap2(null, java.util.HashMap(Map(12->"a",-45->"b").asJava), java.util.HashMap(Map("a"->67,"b"->1923).asJava)) - val js = sj.render(inst) - assertEquals( - """{"a0":null,"a1":{"12":"a","-45":"b"},"a2":{"a":67,"b":1923}}""".asInstanceOf[JSON], - js - ) - assertEquals(inst, sj.read[JIntMap2](js)) - } - - test("Long must work") { - val inst = JLongMap2(null, java.util.HashMap(Map(12L->"a",-45L->"b").asJava), java.util.HashMap(Map("a"->67L,"b"->1923L).asJava)) - val js = sj.render(inst) - assertEquals( - """{"a0":null,"a1":{"12":"a","-45":"b"},"a2":{"a":67,"b":1923}}""".asInstanceOf[JSON], - js - ) - assertEquals(inst, sj.read[JLongMap2](js)) - } - - test("Short must work") { - val inst = JShortMap2(null, java.util.HashMap(Map(12.toShort->"a",-45.toShort->"b").asJava), java.util.HashMap(Map("a"->67.toShort,"b"->19.toShort).asJava)) - val js = sj.render(inst) - assertEquals( - """{"a0":null,"a1":{"12":"a","-45":"b"},"a2":{"a":67,"b":19}}""".asInstanceOf[JSON], - js - ) - assertEquals(inst, sj.read[JShortMap2](js)) - } - - test("Lists must work") { - val hm1 = new java.util.HashMap[String,scala.collection.mutable.Seq[Int]]() - hm1.put("a",scala.collection.mutable.Seq(1,2,3) ) - hm1.put("b",scala.collection.mutable.Seq(4,5,6) ) - val hm2 = new java.util.HashMap[scala.collection.mutable.Seq[Int],String]() - hm2.put(scala.collection.mutable.Seq(1,2,3),"a" ) - hm2.put(scala.collection.mutable.Seq(4,5,6),"b" ) - - val inst = JSeqMap2(hm1, hm2) - val js = sj.render(inst) - assertEquals( - """{"a1":{"a":[1,2,3],"b":[4,5,6]},"a2":{"[1,2,3]":"a","[4,5,6]":"b"}}""".asInstanceOf[JSON], - js - ) - assertEquals(inst, sj.read[JSeqMap2](js)) - } - - test("Classes must work") { - describe("+++ Class Types +++") - - val hm1 = new java.util.HashMap[String,IntSeq]() - hm1.put("a",IntSeq(List(1,2),null)) - hm1.put("b",IntSeq(List(3,4),null)) - val hm2 = new java.util.HashMap[IntSeq,String]() - hm2.put(IntSeq(List(1,2),null),"a") - hm2.put(IntSeq(List(3,4),null),"b") - - val inst = JClassMap(hm1, hm2) - val js = sj.render(inst) - assertEquals( - """{"a1":{"a":{"a1":[1,2],"a2":null},"b":{"a1":[3,4],"a2":null}},"a2":{"{\"a1\":[1,2],\"a2\":null}":"a","{\"a1\":[3,4],\"a2\":null}":"b"}}""".asInstanceOf[JSON], - js - ) - assertEquals(inst, sj.read[JClassMap](js)) - } - - test("Multidimensional arrays must work") { - describe("+++ Complex Types +++") - - //case class JMultiMap( a1: java.util.HashMap[java.util.HashMap[String,Boolean],Int], a2: java.util.HashMap[Int,java.util.HashMap[String,Boolean]] ) - val m1: java.util.HashMap[String,Boolean] = java.util.HashMap( Map( ("a",true), ("b",false) ).asJava ) - val m2: java.util.HashMap[String,Boolean] = java.util.HashMap( Map( ("c",true), ("d",false) ).asJava ) - val inst = JMultiMap( - java.util.HashMap( - Map( - (m1, 1), - (m2, 2) - ).asJava - ), - java.util.HashMap( - Map( - (1, m1), - (2, m2) - ).asJava - ) - ) - val js = sj.render(inst) - assertEquals( - """{"a1":{"{\"a\":true,\"b\":false}":1,"{\"d\":false,\"c\":true}":2},"a2":{"1":{"a":true,"b":false},"2":{"d":false,"c":true}}}""".asInstanceOf[JSON], - js - ) - assertEquals(inst, sj.read[JMultiMap](js)) - } diff --git a/src_old/test/scala/co.blocke.scalajack/json/collections/Model.scala b/src_old/test/scala/co.blocke.scalajack/json/collections/Model.scala deleted file mode 100644 index c5334dee..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json/collections/Model.scala +++ /dev/null @@ -1,105 +0,0 @@ -package co.blocke.scalajack -package json.collections - -import scala.math._ -import scala.collection.Map -import scala.collection.Seq - -//------ Arrays (Scala) -case class BigDecimalArr( a1: Array[BigDecimal], a2: Array[BigDecimal] ) -case class BigIntArr( a1: Array[BigInt], a2: Array[BigInt] ) -case class BooleanArr( a1: Array[Boolean], a2: Array[Boolean] ) -case class CharArr( a1: Array[Char], a2: Array[Char] ) -case class DoubleArr( a1: Array[Double], a2: Array[Double] ) -case class FloatArr( a1: Array[Float], a2: Array[Float] ) -case class IntArr( a1: Array[Int], a2: Array[Int] ) -case class LongArr( a1: Array[Long], a2: Array[Long] ) -case class ShortArr( a1: Array[Short], a2: Array[Short] ) -case class StringArr( a1: Array[String], a2: Array[String] ) - -case class MultiArr( a0: Array[Array[Boolean]], a1: Array[Array[Array[Long]]], a2: Array[Array[BigInt]] ) -case class ClassArr( a1: Array[IntArr] ) - -case class ListArr( a1: Array[List[Int]]) -case class SetArr( a1: Array[Set[Int]]) -case class MapArr( a1: Array[Map[String,Int]]) - -//------ Seqs (Scala) -case class BigDecimalSeq( a1: Seq[BigDecimal], a2: Seq[BigDecimal] ) -case class BigIntSeq( a1: Seq[BigInt], a2: Seq[BigInt] ) -case class BooleanSeq( a1: Seq[Boolean], a2: Seq[Boolean] ) -case class ByteSeq( a1: Seq[Byte], a2: Seq[Byte] ) -case class CharSeq( a1: Seq[Char], a2: Seq[Char] ) -case class DoubleSeq( a1: Seq[Double], a2: Seq[Double] ) -case class FloatSeq( a1: Seq[Float], a2: Seq[Float] ) -case class IntSeq( a1: Seq[Int], a2: Seq[Int] ) -case class LongSeq( a1: Seq[Long], a2: Seq[Long] ) -case class ShortSeq( a1: Seq[Short], a2: Seq[Short] ) -case class StringSeq( a1: Seq[String], a2: Seq[String] ) - -case class MultiSeq( a0: Seq[Seq[Boolean]], a1: Seq[Seq[Seq[Long]]], a2: Seq[Seq[BigInt]] ) -case class ClassSeq( a1: Seq[IntArr] ) - -case class SeqSeq( a1: Seq[List[Int]]) -case class MapSeq( a1: Seq[Map[String,Int]]) - -//------ Seqs (Java) -case class JBigDecimalSeq( a1: java.util.LinkedList[BigDecimal], a2: java.util.LinkedList[BigDecimal] ) -case class JBigIntSeq( a1: java.util.Vector[BigInt], a2: java.util.Vector[BigInt] ) -case class JBooleanSeq( a1: java.util.ArrayList[Boolean], a2: java.util.ArrayList[Boolean] ) -case class JCharSeq( a1: java.util.PriorityQueue[Char], a2: java.util.PriorityQueue[Char] ) -case class JIntSeq( a1: java.util.Stack[Int], a2: java.util.Stack[Int] ) -// Other primitive-type tests would simply be redundant in this series... we've already confirmed Java primtivie serializations. -case class JClassSeq( a1: java.util.LinkedList[IntArr] ) -case class JMapSeq( a1: java.util.ArrayList[Map[String,Int]]) - - -//------ Maps (Some "2" variants to avoid same-name collision with Scala collection classes) -case class BigDecimalMap( a0: Map[BigDecimal,String], a1: Map[BigDecimal,String], a2: Map[String,BigDecimal] ) -case class BigIntMap( a0: Map[BigInt,String], a1: Map[BigInt,String], a2: Map[String,BigInt] ) -case class BooleanMap( a0: Map[Boolean,String], a1: Map[Boolean,String], a2: Map[String,Boolean] ) -case class ByteMap( a0: Map[Byte,String], a1: Map[Byte,String], a2: Map[String,Byte] ) -case class CharMap( a0: Map[Char,String], a1: Map[Char,String], a2: Map[String,Char] ) -case class DoubleMap( a0: Map[Double,String], a1: Map[Double,String], a2: Map[String,Double] ) -case class FloatMap( a0: Map[Float,String], a1: Map[Float,String], a2: Map[String,Float] ) -case class IntMap2( a0: Map[Int,String], a1: Map[Int,String], a2: Map[String,Int] ) -case class LongMap2( a0: Map[Long,String], a1: Map[Long,String], a2: Map[String,Long] ) -case class ShortMap2( a0: Map[Short,String], a1: Map[Short,String], a2: Map[String,Short] ) - -case class MultiMap( a1: Map[Map[String,Boolean],Int], a2: Map[Int,Map[String,Boolean]] ) -case class ClassMap( a1: Map[String,IntArr], a2: Map[IntArr,String] ) -case class ArrayMap( a1: Map[String,Array[Int]], a2:Map[Array[Int],String]) -case class SeqMap2( a1: Map[String,Seq[Int]], a2: Map[Seq[Int],String]) - -//------ Maps (Java) -case class JBigDecimalMap( a0: java.util.HashMap[BigDecimal,String], a1: java.util.HashMap[BigDecimal,String], a2: java.util.HashMap[String,BigDecimal] ) -case class JBigIntMap( a0: java.util.HashMap[BigInt,String], a1: java.util.HashMap[BigInt,String], a2: java.util.HashMap[String,BigInt] ) -case class JBooleanMap( a0: java.util.HashMap[Boolean,String], a1: java.util.HashMap[Boolean,String], a2: java.util.HashMap[String,Boolean] ) -case class JByteMap( a0: java.util.HashMap[Byte,String], a1: java.util.HashMap[Byte,String], a2: java.util.HashMap[String,Byte] ) -case class JCharMap( a0: java.util.HashMap[Char,String], a1: java.util.HashMap[Char,String], a2: java.util.HashMap[String,Char] ) -case class JDoubleMap( a0: java.util.HashMap[Double,String], a1: java.util.HashMap[Double,String], a2: java.util.HashMap[String,Double] ) -case class JFloatMap( a0: java.util.HashMap[Float,String], a1: java.util.HashMap[Float,String], a2: java.util.HashMap[String,Float] ) -case class JIntMap2( a0: java.util.HashMap[Int,String], a1: java.util.HashMap[Int,String], a2: java.util.HashMap[String,Int] ) -case class JLongMap2( a0: java.util.HashMap[Long,String], a1: java.util.HashMap[Long,String], a2: java.util.HashMap[String,Long] ) -case class JShortMap2( a0: java.util.HashMap[Short,String], a1: java.util.HashMap[Short,String], a2: java.util.HashMap[String,Short] ) - -case class JMultiMap( a1: java.util.HashMap[java.util.HashMap[String,Boolean],Int], a2: java.util.HashMap[Int,java.util.HashMap[String,Boolean]] ) -case class JClassMap( a1: java.util.HashMap[String,IntSeq], a2: java.util.HashMap[IntSeq,String] ) -case class JArrayMap( a1: java.util.HashMap[String,Array[Int]], a2:java.util.HashMap[Array[Int],String]) -case class JSeqMap2( a1: java.util.HashMap[String,scala.collection.mutable.Seq[Int]], a2: java.util.HashMap[scala.collection.mutable.Seq[Int],String]) - -//------- Options -trait Person { val name: String } -case class SomeClass(name: String, age: Int) extends Person -trait Thing[A, B] { val a: A; val b: B } -case class AThing[Y, X](a: X, b: Y) extends Thing[X, Y] -case class OptionBigInt(o: Option[BigInt]) -case class OptionClass(name: String, age: Option[Int]) -case class OptionTuple(foo: Int, t: (Boolean, Option[String], Int)) - -case class OptionalBigInt(o: java.util.Optional[BigInt]) -case class OptionalClass(name: String, age: java.util.Optional[Int]) -case class OptionalTuple(foo: Int, t: (Boolean, java.util.Optional[String], Int)) - -//------- Any -case class Player(name: String, age: Int) \ No newline at end of file diff --git a/src_old/test/scala/co.blocke.scalajack/json/collections/Options.scala b/src_old/test/scala/co.blocke.scalajack/json/collections/Options.scala deleted file mode 100644 index 35ba45bc..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json/collections/Options.scala +++ /dev/null @@ -1,435 +0,0 @@ -package co.blocke.scalajack -package json.collections - -import co.blocke.scala_reflection._ -import scala.math._ -import TestUtil._ -import munit._ -import munit.internal.console -import co.blocke.scalajack.json.JSON -import scala.collection.immutable._ -import scala.jdk.CollectionConverters._ -import scala.language.implicitConversions -import java.util.Optional -import json.JsonMatcher - - -class Options() extends FunSuite: - - val sj = co.blocke.scalajack.ScalaJack() - - test("Option of primitive (naked)") { - describe("--------------------------------------------\n: Option (Scala) & Optional (Java) Tests :\n--------------------------------------------", Console.BLUE) - describe("+++ Scala Options") - - val inst: Option[BigInt] = Some(BigInt(5)) - val js = sj.render(inst) - assertEquals("5".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[Option[BigInt]](js)) - } - - test("Option of primitive (in class)") { - val inst = OptionBigInt(Some(BigInt(5))) - val js = sj.render(inst) - assertEquals("""{"o":5}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[OptionBigInt](js)) - } - - test("Option of List") { - val inst: Option[List[Int]] = Some(List(1, 2, 3)) - val js = sj.render(inst) - assertEquals("""[1,2,3]""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[Option[List[Int]]](js)) - - val inst2: Option[List[Int]] = None - val js2 = sj.render(inst2) - assertEquals("".asInstanceOf[JSON],js2) - // Can't read nothing into something - - val inst3: Map[Option[List[Int]], Int] = - Map(None -> 2, Some(List(1, 2, 3)) -> 1) - val js3 = sj.render(inst3) - assertEquals("""{"[1,2,3]":1}""".asInstanceOf[JSON],js3) - assertEquals(Map(Some(List(1, 2, 3)) -> 1), sj.read[Map[Option[List[Int]], Int]](js3)) - } - - test("Option of Map") { - val inst: Option[Map[String, Boolean]] = - Some(Map("hey" -> true, "you" -> false)) - val js = sj.render(inst) - assertEquals("""{"hey":true,"you":false}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[Option[Map[String, Boolean]]](js)) - - val inst2: Option[Map[String, Boolean]] = None - val js2 = sj.render(inst2) - assertEquals("".asInstanceOf[JSON],js2) - // Can't read nothing into something - - val inst3: Map[Option[Map[String, Boolean]], Int] = - Map(None -> 2, Some(Map("hey" -> true, "you" -> false)) -> 1) - val js3 = sj.render(inst3) - assertEquals("""{"{\"hey\":true,\"you\":false}":1}""".asInstanceOf[JSON],js3) - assertEquals(Map(Some(Map("hey" -> true, "you" -> false)) -> 1), sj.read[Map[Option[Map[String, Boolean]], Int]](js3)) - } - - test("Option of Tuple") { - val inst: Option[(String, Boolean)] = Some(("a", true)) - val js = sj.render(inst) - assertEquals("""["a",true]""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[Option[(String, Boolean)]](js)) - - val inst2: Option[(String, Boolean)] = None - val js2 = sj.render(inst2) - assertEquals("".asInstanceOf[JSON],js2) - // Can't read nothing into something - - val inst3: Map[Option[(String, Boolean)], Int] = - Map(None -> 2, Some(("a", true)) -> 1) - val js3 = sj.render(inst3) - assertEquals("""{"[\"a\",true]":1}""".asInstanceOf[JSON],js3) - assertEquals(Map(Some(("a", true)) -> 1), sj.read[Map[Option[(String, Boolean)], Int]](js3)) - } - - test("Option of Case Class") { - val inst: Option[SomeClass] = Some(SomeClass("Mike", 2)) - val js = sj.render(inst) - assertEquals("""{"name":"Mike","age":2}""".asInstanceOf[JSON],js) - assertEquals(inst,sj.read[Option[SomeClass]](js)) - - val inst2: Option[SomeClass] = None - val js2 = sj.render(inst2) - assertEquals("".asInstanceOf[JSON],js2) - // Can't read nothing into something - - val inst3: Map[Option[SomeClass], Int] = Map(None -> 2, Some(SomeClass("Mike", 2)) -> 1) - val js3 = sj.render(inst3) - assertEquals("""{"{\"name\":\"Mike\",\"age\":2}":1}""".asInstanceOf[JSON],js3) - assertEquals(Map(Some(SomeClass("Mike", 2)) -> 1),sj.read[Map[Option[SomeClass], Int]](js3)) - } - - test("Option of Parameterized Class") { - val inst: Option[AThing[Int, String]] = Some(AThing("wow", 5)) - val js = sj.render(inst) - assertEquals("""{"a":"wow","b":5}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[Option[AThing[Int, String]]](js)) - - val inst2: Option[AThing[Int, String]] = None - val js2 = sj.render(inst2) - assertEquals("".asInstanceOf[JSON],js2) - - val inst3: Map[Option[AThing[Int, String]], Int] = Map(None -> 2, Some(AThing("wow", 5)) -> 1) - val js3 = sj.render(inst3) - assertEquals("""{"{\"a\":\"wow\",\"b\":5}":1}""".asInstanceOf[JSON],js3) - assertEquals(Map(Some(AThing("wow", 5)) -> 1), sj.read[Map[Option[AThing[Int, String]], Int]](js3)) - } - - test("Option of Trait") { - val inst: Option[Person] = Some(SomeClass("Mike", 2)) - val js = sj.render(inst) - assertEquals( - """{"_hint":"co.blocke.scalajack.json.collections.SomeClass","name":"Mike","age":2}""".asInstanceOf[JSON],js) - assertEquals(inst,sj.read[Option[Person]](js)) - - val inst2: Option[Person] = None - val js2 = sj.render(inst2) - assertEquals("".asInstanceOf[JSON],js2) - // Can't read nothing into something - - val inst3: Map[Option[Person], Int] = - Map(None -> 2, Some(SomeClass("Mike", 2)) -> 1) - val js3 = sj.render(inst3) - assertEquals( - """{"{\"_hint\":\"co.blocke.scalajack.json.collections.SomeClass\",\"name\":\"Mike\",\"age\":2}":1}""".asInstanceOf[JSON],js3) - assertEquals(Map(Some(SomeClass("Mike", 2)) -> 1),sj.read[Map[Option[Person], Int]](js3)) - } - - test("Option of Parameterized Trait") { - val inst: Option[Thing[String, Int]] = Some(AThing("wow", 5)) - val js = sj.render(inst) - assertEquals( - """{"_hint":"co.blocke.scalajack.json.collections.AThing","a":"wow","b":5}""".asInstanceOf[JSON],js) - assertEquals(inst,sj.read[Option[Thing[String, Int]]](js)) - - val inst2: Option[Thing[String, Int]] = None - val js2 = sj.render(inst2) - assertEquals("".asInstanceOf[JSON],js2) - - val inst3: Map[Option[Thing[String, Int]], Int] = - Map(None -> 2, Some(AThing("wow", 5)) -> 1) - val js3 = sj.render(inst3) - assertEquals( - """{"{\"_hint\":\"co.blocke.scalajack.json.collections.AThing\",\"a\":\"wow\",\"b\":5}":1}""".asInstanceOf[JSON],js3) - assertEquals(Map(Some(AThing("wow", 5)) -> 1),sj.read[Map[Option[Thing[String, Int]], Int]](js3)) - } - - //------------------- None tests - test("Option is None (in class)") { - describe("+++ Scala None/null tests") - - val inst = OptionClass("Mike", None) - val js = sj.render(inst) - assertEquals("""{"name":"Mike"}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[OptionClass](js)) - } - - test("Option is None (in List)") { - val inst: List[Option[Int]] = List(Some(1), None, Some(2)) - val js = sj.render(inst) - assertEquals("""[1,2]""".asInstanceOf[JSON],js) - assertEquals(List(Some(1), Some(2)).asInstanceOf[List[Option[Int]]], sj.read[List[Option[Int]]](js)) // None gets erased here - } - - test("Option is None (value in Map)") { - val inst: Map[Int, Option[String]] = Map(1 -> Some("one"), 2 -> None, 3 -> Some("three")) - val js = sj.render(inst) - assertEquals("""{"1":"one","3":"three"}""".asInstanceOf[JSON],js) - assertEquals(Map(1 -> Some("one"), 3 -> Some("three")).asInstanceOf[Map[Int,Option[String]]], sj.read[Map[Int, Option[String]]](js)) // None gets erased here - } - - test("Option is None (key in Map)") { - val inst: Map[Option[String], Int] = - Map(Some("one") -> 1, None -> 2, Some("three") -> 3) - val js = sj.render(inst) - assertEquals("""{"one":1,"three":3}""".asInstanceOf[JSON],js) - assertEquals(Map(Some("one") -> 1, Some("three") -> 3),sj.read[Map[Option[String], Int]](js)) - } - - test("Option is None (in Tuple)") { - val inst = List( - OptionTuple(1, (true, Some("ok"), 2)), - OptionTuple(5, (false, None, 3)) - ) - val js = sj.render(inst) - assertEquals( - """[{"foo":1,"t":[true,"ok",2]},{"foo":5,"t":[false,null,3]}]""".asInstanceOf[JSON],js) - assertEquals(inst,sj.read[List[OptionTuple]](js)) - } - - test("Reading null into optional (naked)") { - val js = "null".asInstanceOf[JSON] - assertEquals(null.asInstanceOf[Option[Int]], sj.read[Option[Int]](js)) - } - - test("Reading null into optional class field") { - val js = """{"name":"Mike","age":null}""".asInstanceOf[JSON] - assertEquals(OptionClass("Mike", null), sj.read[OptionClass](js)) - } - - test("Reading null into optional List item") { - val js = """[1,null,2]""".asInstanceOf[JSON] - assertEquals(List(Some(1), null, Some(2)).asInstanceOf[List[Option[Int]]],sj.read[List[Option[Int]]](js)) - } - - test("Reading null into optional Map item") { - val js = """{"1":"one","2":null,"3":"three"}""".asInstanceOf[JSON] - assertEquals(Map(1 -> Some("one"), 2 -> None, 3 -> Some("three")),sj.read[Map[Int, Option[String]]](js)) - } - - test("Reading null into optional Tuple item") { - val js = """[{"foo":1,"t":[true,"ok",2]},{"foo":5,"t":[false,null,3]}]""".asInstanceOf[JSON] - assertEquals( - List( - OptionTuple(1, (true, Some("ok"), 2)), - OptionTuple(5, (false, None, 3)) - ),sj.read[List[OptionTuple]](js)) - } - - //-------------- Java - - test("Option of primitive (naked)") { - describe("++++ Java Optional") - - val inst: Optional[BigInt] = Optional.of(BigInt(5)) - val js = sj.render(inst) - assertEquals("5".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[Optional[BigInt]](js)) - } - - test("Java class with Optional - empty JSON") { - val js = """{}""".asInstanceOf[JSON] - val inst = sj.read[Maybe](js) - assertEquals(inst.getOne, Optional.empty) - assertEquals(inst.getTwo, Optional.of("stuff")) - assertEquals( """{"two":"stuff"}""".asInstanceOf[JSON], sj.render(inst)) - - val msg = """Class co.blocke.scalajack.Maybe2 missing required fields: one - |{} - |-^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[Maybe2](js) - } - } - - test("Java class with Optional - some args specified") { - val js = """{"one":"meh"}""".asInstanceOf[JSON] - val inst = sj.read[Maybe](js) - assertEquals(inst.getOne, Optional.of("meh")) - assertEquals(inst.getTwo, Optional.of("stuff")) - assert(JsonMatcher.jsonMatches("""{"one":"meh","two":"stuff"}""".asInstanceOf[JSON], sj.render(inst))) - } - - test("Optional of primitive (in class)") { - val inst = OptionalBigInt(Optional.of(BigInt(5))) - val js = sj.render(inst) - assertEquals("""{"o":5}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[OptionalBigInt](js)) - } - - test("Optional of List") { - val inst: Optional[List[Int]] = Optional.of(List(1, 2, 3)) - val js = sj.render(inst) - assertEquals("""[1,2,3]""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[Optional[List[Int]]](js)) - - val inst2: Optional[List[Int]] = Optional.empty[List[Int]] - val js2 = sj.render(inst2) - assertEquals("".asInstanceOf[JSON],js2) - // Can't read nothing into something - - val inst3 = Map(Optional.empty[List[Int]] -> 2, Optional.of(List(1, 2, 3)) -> 1) - val js3 = sj.render(inst3) - assertEquals("""{"[1,2,3]":1}""".asInstanceOf[JSON],js3) - assert(Map(Optional.of(List(1, 2, 3)) -> 1) == sj.read[Map[Optional[List[Int]], Int]](js3)) - } - - test("Optional of Map") { - val inst: Optional[Map[String, Boolean]] = - Optional.of(Map("hey" -> true, "you" -> false)) - val js = sj.render(inst) - assertEquals("""{"hey":true,"you":false}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[Optional[Map[String, Boolean]]](js)) - - val inst2: Option[Map[String, Boolean]] = None - val js2 = sj.render(inst2) - assertEquals("".asInstanceOf[JSON],js2) - // Can't read nothing into something - - val inst3: Map[Option[Map[String, Boolean]], Int] = - Map(None -> 2, Some(Map("hey" -> true, "you" -> false)) -> 1) - val js3 = sj.render(inst3) - assertEquals("""{"{\"hey\":true,\"you\":false}":1}""".asInstanceOf[JSON],js3) - assertEquals(Map(Some(Map("hey" -> true, "you" -> false)) -> 1), sj.read[Map[Option[Map[String, Boolean]], Int]](js3)) - } - - test("Optional of Tuple") { - val inst: Optional[(String, Boolean)] = Optional.of(("a", true)) - val js = sj.render(inst) - assertEquals("""["a",true]""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[Optional[(String, Boolean)]](js)) - - val inst2: Optional[(String, Boolean)] = Optional.empty[(String,Boolean)] - val js2 = sj.render(inst2) - assertEquals("".asInstanceOf[JSON],js2) - // Can't read nothing into something - - val inst3: Map[Optional[(String, Boolean)], Int] = - Map(Optional.empty[(String, Boolean)] -> 2, Optional.of(("a", true)) -> 1) - val js3 = sj.render(inst3) - assertEquals("""{"[\"a\",true]":1}""".asInstanceOf[JSON],js3) - assertEquals(Map(Optional.of(("a", true)) -> 1), sj.read[Map[Optional[(String, Boolean)], Int]](js3)) - } - - test("Optional of Case Class") { - val inst: Optional[SomeClass] = Optional.of(SomeClass("Mike", 2)) - val js = sj.render(inst) - assertEquals("""{"name":"Mike","age":2}""".asInstanceOf[JSON],js) - assertEquals(inst,sj.read[Optional[SomeClass]](js)) - - val inst2: Optional[SomeClass] = Optional.empty[SomeClass] - val js2 = sj.render(inst2) - assertEquals("".asInstanceOf[JSON],js2) - // Can't read nothing into something - - val inst3: Map[Optional[SomeClass], Int] = Map(Optional.empty[SomeClass] -> 2, Optional.of(SomeClass("Mike", 2)) -> 1) - val js3 = sj.render(inst3) - assertEquals("""{"{\"name\":\"Mike\",\"age\":2}":1}""".asInstanceOf[JSON],js3) - assertEquals(Map(Optional.of(SomeClass("Mike", 2)) -> 1),sj.read[Map[Optional[SomeClass], Int]](js3)) - } - - test("Optional of Parameterized Class") { - val inst: Optional[AThing[Int, String]] = Optional.of(AThing("wow", 5)) - val js = sj.render(inst) - assertEquals("""{"a":"wow","b":5}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[Optional[AThing[Int, String]]](js)) - - val inst2: Optional[AThing[Int, String]] = Optional.empty[AThing[Int, String]] - val js2 = sj.render(inst2) - assertEquals("".asInstanceOf[JSON],js2) - - val inst3: Map[Optional[AThing[Int, String]], Int] = - Map(Optional.empty[AThing[Int, String]] -> 2, Optional.of(AThing("wow", 5)) -> 1) - val js3 = sj.render(inst3) - assertEquals("""{"{\"a\":\"wow\",\"b\":5}":1}""".asInstanceOf[JSON],js3) - assertEquals(Map(Optional.of(AThing("wow", 5)) -> 1), sj.read[Map[Optional[AThing[Int, String]], Int]](js3)) - } - - test("Optional is None (in class)") { - describe("+++ Java Empty/null tests") - - val inst = OptionalClass("Mike", Optional.empty[Int]) - val js = sj.render(inst) - assertEquals("""{"name":"Mike"}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[OptionalClass](js)) - } - - test("Optional is None (in List)") { - val inst: List[Optional[Int]] = List(Optional.of(1), Optional.empty[Int], Optional.of(2)) - val js = sj.render(inst) - assertEquals("""[1,2]""".asInstanceOf[JSON],js) - assertEquals(List(Optional.of(1), Optional.of(2)).asInstanceOf[List[Optional[Int]]], sj.read[List[Optional[Int]]](js)) // None gets erased here - } - - test("Optional is None (value in Map)") { - val inst: Map[Int, Optional[String]] = Map(1 -> Optional.of("one"), 2 -> Optional.empty[String], 3 -> Optional.of("three")) - val js = sj.render(inst) - assertEquals("""{"1":"one","3":"three"}""".asInstanceOf[JSON],js) - assert(Map(1 -> Optional.of("one"), 3 -> Optional.of("three")).asInstanceOf[Map[Int,Option[String]]] == sj.read[Map[Int, Optional[String]]](js)) // None gets erased here - } - - test("Optional is None (key in Map)") { - val inst: Map[Optional[String], Int] = - Map(Optional.of("one") -> 1, Optional.empty[String] -> 2, Optional.of("three") -> 3) - val js = sj.render(inst) - assertEquals("""{"one":1,"three":3}""".asInstanceOf[JSON],js) - assertEquals(Map(Optional.of("one") -> 1, Optional.of("three") -> 3),sj.read[Map[Optional[String], Int]](js)) - } - - test("Optional is Empty (in Tuple)") { - val inst = List( - OptionalTuple(1, (true, Optional.of("ok"), 2)), - OptionalTuple(5, (false, Optional.empty[String], 3)) - ) - val js = sj.render(inst) - assertEquals( - """[{"foo":1,"t":[true,"ok",2]},{"foo":5,"t":[false,null,3]}]""".asInstanceOf[JSON],js) - assertEquals(inst,sj.read[List[OptionalTuple]](js)) - } - - test("Reading null into Optional (naked)") { - val js = "null".asInstanceOf[JSON] - assertEquals(null.asInstanceOf[Optional[Int]], sj.read[Optional[Int]](js)) - } - - test("Reading null into Optional class field") { - val js = """{"name":"Mike","age":null}""".asInstanceOf[JSON] - assertEquals(OptionalClass("Mike", null), sj.read[OptionalClass](js)) - } - - test("Reading null into Optional List item") { - val js = """[1,null,2]""".asInstanceOf[JSON] - assertEquals(List(Optional.of(1), null, Optional.of(2)).asInstanceOf[List[Optional[Int]]],sj.read[List[Optional[Int]]](js)) - } - - test("Reading null into Optional Map item") { - val js = """{"1":"one","2":null,"3":"three"}""".asInstanceOf[JSON] - assertEquals(Map(1 -> Optional.of("one"), 2 -> Optional.empty[String], 3 -> Optional.of("three")),sj.read[Map[Int, Optional[String]]](js)) - } - - test("Reading null into Optional Tuple item") { - val js = """[{"foo":1,"t":[true,"ok",2]},{"foo":5,"t":[false,null,3]}]""".asInstanceOf[JSON] - assertEquals( - List( - OptionalTuple(1, (true, Optional.of("ok"), 2)), - OptionalTuple(5, (false, Optional.empty[String], 3)) - ),sj.read[List[OptionalTuple]](js)) - } diff --git a/src_old/test/scala/co.blocke.scalajack/json/collections/Seqs.scala b/src_old/test/scala/co.blocke.scalajack/json/collections/Seqs.scala deleted file mode 100644 index 68e1fb6d..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json/collections/Seqs.scala +++ /dev/null @@ -1,272 +0,0 @@ -package co.blocke.scalajack -package json.collections - -import co.blocke.scala_reflection._ -import scala.math._ -import java.util.UUID -import TestUtil._ -import munit._ -import munit.internal.console -import co.blocke.scalajack.json.JSON -import scala.collection.immutable._ -import scala.jdk.CollectionConverters._ -import scala.language.implicitConversions._ - -class Seqs() extends FunSuite: - - val sj = co.blocke.scalajack.ScalaJack() - - test("BigDecimal must work") { - describe("---------------------\n: Scala Seq Tests :\n---------------------", Console.BLUE) - describe("+++ Primitive Types +++") - - val inst = BigDecimalSeq(null, Seq(BigDecimal(123.456),BigDecimal(78.91))) - val js = sj.render(inst) - assertEquals( - """{"a1":null,"a2":[123.456,78.91]}""".asInstanceOf[JSON], - js - ) - assertEquals(inst, sj.read[BigDecimalSeq](js)) - } - - test("BigInt must work") { - val inst = BigIntSeq(null, List(BigInt(123),BigInt(78))) - val js = sj.render(inst) - assertEquals( - """{"a1":null,"a2":[123,78]}""".asInstanceOf[JSON], - js - ) - assertEquals(inst, sj.read[BigIntSeq](js)) - } - - test("Boolean must work") { - val inst = BooleanSeq(null, scala.collection.mutable.ListBuffer(true,false)) - val js = sj.render(inst) - assertEquals( - """{"a1":null,"a2":[true,false]}""".asInstanceOf[JSON], - js - ) - assertEquals(inst, sj.read[BooleanSeq](js)) - } - - test("Byte must work") { - val inst = ByteSeq(null, List(123.toByte,200.toByte)) - val js = sj.render(inst) - assertEquals( - """{"a1":null,"a2":[123,-56]}""".asInstanceOf[JSON], - js - ) - assertEquals(inst, sj.read[ByteSeq](js)) - } - - test("Char must work") { - val inst = CharSeq(null, Queue('a','b','c')) - val js = sj.render(inst) - assertEquals( - """{"a1":null,"a2":["a","b","c"]}""".asInstanceOf[JSON], - js - ) - assertEquals(inst, sj.read[CharSeq](js)) - } - - test("Double must work") { - val inst = DoubleSeq(null, scala.collection.mutable.ArrayBuffer(12.34,56.78)) - val js = sj.render(inst) - assertEquals( - """{"a1":null,"a2":[12.34,56.78]}""".asInstanceOf[JSON], - js - ) - assertEquals(inst, sj.read[DoubleSeq](js)) - } - - test("Float must work") { - val inst = FloatSeq(null, LinearSeq(12.34F,56.78F)) - val js = sj.render(inst) - assertEquals( - """{"a1":null,"a2":[12.34,56.78]}""".asInstanceOf[JSON], - js - ) - assertEquals(inst, sj.read[FloatSeq](js)) - } - - test("Int must work") { - val inst = IntSeq(null, scala.collection.mutable.IndexedSeq(1,2,3)) - val js = sj.render(inst) - assertEquals( - """{"a1":null,"a2":[1,2,3]}""".asInstanceOf[JSON], - js - ) - assertEquals(inst, sj.read[IntSeq](js)) - } - - test("Long must work") { - val inst = LongSeq(null, List(1L,2L,3L)) - val js = sj.render(inst) - assertEquals( - """{"a1":null,"a2":[1,2,3]}""".asInstanceOf[JSON], - js - ) - assertEquals(inst, sj.read[LongSeq](js)) - } - - test("Short must work") { - val inst = ShortSeq(null, List(1.toShort,2.toShort,3.toShort)) - val js = sj.render(inst) - assertEquals( - """{"a1":null,"a2":[1,2,3]}""".asInstanceOf[JSON], - js - ) - assertEquals(inst, sj.read[ShortSeq](js)) - } - - test("String must work") { - val inst = StringSeq(null, List("a","b","c")) - val js = sj.render(inst) - assertEquals( - """{"a1":null,"a2":["a","b","c"]}""".asInstanceOf[JSON], - js - ) - assertEquals(inst, sj.read[StringSeq](js)) - } - - test("Lists must work") { - describe("+++ Collection Types +++") - val inst = SeqSeq(List( List(1,2,3), List(4,5,6) )) - val js = sj.render(inst) - assertEquals( - """{"a1":[[1,2,3],[4,5,6]]}""".asInstanceOf[JSON], - js - ) - assertEquals(inst, sj.read[SeqSeq](js)) - } - - test("Maps must work") { - val inst = MapSeq(List( Map("a"->1,"b"->2), Map("c"->3,"d"->4) )) - val js = sj.render(inst) - assertEquals( - """{"a1":[{"a":1,"b":2},{"c":3,"d":4}]}""".asInstanceOf[JSON], - js - ) - assertEquals(inst, sj.read[MapSeq](js)) - } - - test("Classes must work") { - describe("+++ Class Types +++") - val inst = ClassSeq(List(IntArr(null,Array(1,2)), IntArr(null,Array(1,2)))) - val js = sj.render(inst) - assertEquals( - """{"a1":[{"a1":null,"a2":[1,2]},{"a1":null,"a2":[1,2]}]}""".asInstanceOf[JSON], - js - ) - val i2 = sj.read[ClassSeq](js) - assertEquals(i2.a1.toList(0).a1,null) - assert(inst.a1.toList(0).a2.sameElements(i2.a1.toList(0).a2)) - assertEquals(i2.a1.toList(1).a1,null) - assert(inst.a1.toList(1).a2.sameElements(i2.a1.toList(1).a2)) - } - - test("Multidimensional arrays must work") { - describe("+++ Complex Types +++") - val inst = MultiSeq(null, Seq(Seq( Seq(1L,2L), Seq(3L,4L) ), Seq(Seq(5L,6L), Seq(7L,8L)) ), - Seq(Seq(BigInt(12),BigInt(13)), Seq(BigInt(14),BigInt(15)))) - val js = sj.render(inst) - assertEquals( - """{"a0":null,"a1":[[[1,2],[3,4]],[[5,6],[7,8]]],"a2":[[12,13],[14,15]]}""".asInstanceOf[JSON], - js - ) - assertEquals(inst, sj.read[MultiSeq](js)) - } - - - test("BigDecimal (LinkedList) must work") { - describe("--------------------\n: Java Seq Tests :\n--------------------", Console.BLUE) - describe("+++ Primitive Types (Mix of List/Set/Queue +++") - - val inst = JBigDecimalSeq(null, new java.util.LinkedList(List(BigDecimal(123.456),BigDecimal(78.91)).asJava)) - val js = sj.render(inst) - assertEquals( - """{"a1":null,"a2":[123.456,78.91]}""".asInstanceOf[JSON], - js - ) - val readIn = sj.read[JBigDecimalSeq](js) - assertEquals(inst.a1, readIn.a1) - assertEquals(inst.a2, readIn.a2) - } - - test("BigInt (Vector) must work") { - val inst = JBigIntSeq(null, new java.util.Vector(List(BigInt(123),BigInt(78)).asJava)) - val js = sj.render(inst) - assertEquals( - """{"a1":null,"a2":[123,78]}""".asInstanceOf[JSON], - js - ) - val readIn = sj.read[JBigIntSeq](js) - assertEquals(inst.a1, readIn.a1) - assertEquals(inst.a2, readIn.a2) - } - - test("Boolean (ArrayList) must work") { - val inst = JBooleanSeq(null, new java.util.ArrayList(List(true,false).asJava)) - val js = sj.render(inst) - assertEquals( - """{"a1":null,"a2":[true,false]}""".asInstanceOf[JSON], - js - ) - val readIn = sj.read[JBooleanSeq](js) - assertEquals(inst.a1, readIn.a1) - assertEquals(inst.a2, readIn.a2) - } - - test("Char (PriorityQueue) must work") { - val inst = JCharSeq(null, new java.util.PriorityQueue(List('a','b','c').asJava)) - val js = sj.render(inst) - assertEquals( - """{"a1":null,"a2":["a","b","c"]}""".asInstanceOf[JSON], - js - ) - val readIn = sj.read[JCharSeq](js) - assertEquals(inst.a1, readIn.a1) - assertEquals(inst.a2.toString, readIn.a2.toString) - } - //case class JIntSeq( a1: java.util.Stack[Int], a2: java.util.Stack[Int] ) - - test("Int (Stack) must work") { - val stack = new java.util.Stack[Int]() - stack.push(1) - stack.push(2) - stack.push(3) - val inst = JIntSeq(null, stack) - val js = sj.render(inst) - assertEquals( - """{"a1":null,"a2":[1,2,3]}""".asInstanceOf[JSON], - js - ) - val readIn = sj.read[JIntSeq](js) - assertEquals(inst.a1, readIn.a1) - assertEquals(inst.a2, readIn.a2) - } - - test("Maps must work") { - val inst = JMapSeq(java.util.ArrayList( List(Map("a"->1,"b"->2), Map("c"->3,"d"->4)).asJava )) - val js = sj.render(inst) - assertEquals( - """{"a1":[{"a":1,"b":2},{"c":3,"d":4}]}""".asInstanceOf[JSON], - js - ) - assertEquals(inst.a1.asScala, sj.read[JMapSeq](js).a1.asScala) - } - - test("Classes must work") { - describe("+++ Class Types +++") - val inst = JClassSeq( new java.util.LinkedList(List(IntArr(null,Array(1,2)), IntArr(null,Array(1,2))).asJava)) - val js = sj.render(inst) - assertEquals( - """{"a1":[{"a1":null,"a2":[1,2]},{"a1":null,"a2":[1,2]}]}""".asInstanceOf[JSON], - js - ) - val i2 = sj.read[JClassSeq](js) - assertEquals(i2.a1.get(0).a1, null) - assert(inst.a1.get(0).a2.sameElements(i2.a1.get(0).a2)) - assertEquals(i2.a1.get(1).a1, null) - assert(inst.a1.get(1).a2.sameElements(i2.a1.get(1).a2)) - } diff --git a/src_old/test/scala/co.blocke.scalajack/json/collections/Tuples.scala b/src_old/test/scala/co.blocke.scalajack/json/collections/Tuples.scala deleted file mode 100644 index 45fa0e11..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json/collections/Tuples.scala +++ /dev/null @@ -1,52 +0,0 @@ -package co.blocke.scalajack -package json.collections - -import TestUtil._ -import munit._ -import munit.internal.console -import co.blocke.scalajack.json.JSON -import scala.collection.immutable._ - -class Tuples() extends FunSuite: - - val sj = co.blocke.scalajack.ScalaJack() - - test("null tuples work") { - describe("-----------------\n: Tuple Tests :\n-----------------", Console.BLUE) - val jsNull = "null".asInstanceOf[JSON] - assert(sj.read[(Int, Boolean)](jsNull) == null) - assert(sj.render[(Int,Boolean)](null) == jsNull) - } - - test("missing start bracken") { - val js = """12,5""".asInstanceOf[JSON] - val msg = - """Expected start of tuple here - |12,5 - |^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[(Int, Int)](js) - } - } - - test("missing comma") { - val js = """[12""".asInstanceOf[JSON] - val msg = - """Expected comma here - |[12 - |---^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[(Int, Int)](js) - } - } - - test("no closing bracket") { - val js = """[12,5""".asInstanceOf[JSON] - val msg = - """Expected end of tuple here - |[12,5 - |-----^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[(Int, Int)](js) - } - } diff --git a/src_old/test/scala/co.blocke.scalajack/json/custom/CustomAdapter.scala b/src_old/test/scala/co.blocke.scalajack/json/custom/CustomAdapter.scala deleted file mode 100644 index c13c0426..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json/custom/CustomAdapter.scala +++ /dev/null @@ -1,20 +0,0 @@ -package co.blocke.scalajack -package json.custom - -import co.blocke.scala_reflection._ -import TestUtil._ -import munit._ -import munit.internal.console -import co.blocke.scalajack.json.JSON - -class CustomAdapter() extends FunSuite: - - test("Overrides type adapter for specific (given) type") { - describe("--------------------------\n: Custom Adapter Tests :\n--------------------------", Console.BLUE) - - val sj = co.blocke.scalajack.ScalaJack().withAdapters(PhoneAdapter) - val inst = Person("Bartholomew", "5555555555".asInstanceOf[Phone]) - val js = sj.render(inst) - assertEquals("""{"name":"Bartholomew","phone":"555-555-5555"}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[Person](js)) - } \ No newline at end of file diff --git a/src_old/test/scala/co.blocke.scalajack/json/custom/CustomTypeHints.scala b/src_old/test/scala/co.blocke.scalajack/json/custom/CustomTypeHints.scala deleted file mode 100644 index f897ddd6..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json/custom/CustomTypeHints.scala +++ /dev/null @@ -1,134 +0,0 @@ -package co.blocke.scalajack -package json.custom - -import co.blocke.scala_reflection._ -import TestUtil._ -import munit._ -import munit.internal.console -import co.blocke.scalajack.json.JSON -import co.blocke.scalajack.model._ - -class CustomTypeHints() extends FunSuite: - - test("Override default trait/polymorphic type hint") { - describe("-----------------------------\n: Custom Type Hints Tests :\n-----------------------------", Console.BLUE) - describe("+++ Positive Tests +++") - - val sj = co.blocke.scalajack.ScalaJack().withDefaultHint("which") - val inst: Address = USAddress("123 Main", "New York", "NY", "39822") - val js = sj.render(inst) - assertEquals( - """{"which":"co.blocke.scalajack.json.custom.USAddress","street":"123 Main","city":"New York","state":"NY","postalCode":"39822"}""".asInstanceOf[JSON], - js) - assertEquals(inst, sj.read[Address](js)) - } - - test("Override type-specific trait/polymorphic type hint") { - val sj = co.blocke.scalajack.ScalaJack().withHints(RType.of[Address] -> "addr_kind") - val inst: Demographic = - USDemographic(50, USAddress("123 Main", "New York", "NY", "39822")) - val js = sj.render(inst) - assertEquals( - """{"_hint":"co.blocke.scalajack.json.custom.USDemographic","age":50,"address":{"addr_kind":"co.blocke.scalajack.json.custom.USAddress","street":"123 Main","city":"New York","state":"NY","postalCode":"39822"}}""".asInstanceOf[JSON], - js) - assertEquals(inst, sj.read[Demographic](js)) - } - - test( - "Use ClassNameHintModifier to modify trait/polymorphic type hint value" - ) { - val prependHintMod = ClassNameHintModifier( - (hint: String) => "co.blocke.scalajack.json.custom." + hint, - (cname: String) => cname.split('.').last - ) - val sj = - co.blocke.scalajack.ScalaJack().withHintModifiers((RType.of[Address], prependHintMod)) - val inst: Demographic = - USDemographic(50, USAddress("123 Main", "New York", "NY", "39822")) - val js = sj.render(inst) - assertEquals( - """{"_hint":"co.blocke.scalajack.json.custom.USDemographic","age":50,"address":{"_hint":"USAddress","street":"123 Main","city":"New York","state":"NY","postalCode":"39822"}}""".asInstanceOf[JSON], - js) - assertEquals(inst, sj.read[Demographic](js)) - } - - test( - "Use StringMatchHintModifier to modify trait/polymorphic type hint value" - ) { - val strMatchHintMod = - StringMatchHintModifier(Map("US" -> classOf[USAddress].getName)) - val sj = - co.blocke.scalajack.ScalaJack().withHintModifiers((RType.of[Address], strMatchHintMod)) - val inst: Demographic = - USDemographic(50, USAddress("123 Main", "New York", "NY", "39822")) - val js = sj.render(inst) - assertEquals( - """{"_hint":"co.blocke.scalajack.json.custom.USDemographic","age":50,"address":{"_hint":"US","street":"123 Main","city":"New York","state":"NY","postalCode":"39822"}}""".asInstanceOf[JSON], - js) - assertEquals(inst, sj.read[Demographic](js)) - } - - test("Use unspecified type hint") { - describe("--- Negative Tests ---") - val sj = co.blocke.scalajack.ScalaJack().withDefaultHint("which") - val js = - """{"bogus":"co.blocke.scalajack.json.custom.USAddress","street":"123 Main","city":"New York","state":"NY","postalCode":"39822"}""".asInstanceOf[JSON] - val msg = """Type hint 'which' not found - |...ity":"New York","state":"NY","postalCode":"39822"} - |----------------------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[Address](js) - } - } - - test("Hint value after modification doesn't resolve to known class name") { - val prependHintMod = ClassNameHintModifier( - (hint: String) => "co.blocke.scalajack.bogus." + hint, - (cname: String) => cname.split('.').last - ) - val sj = - co.blocke.scalajack.ScalaJack().withHintModifiers((RType.of[Address], prependHintMod)) - val js = - """{"_hint":"co.blocke.scalajack.json.custom.USDemographic","age":50,"address":{"_hint":"USAddress","street":"123 Main","city":"New York","state":"NY","postalCode":"39822"}}""".asInstanceOf[JSON] - val msg = - """Couldn't marshal class for USAddress - |...mographic","age":50,"address":{"_hint":"USAddress","street":"123 Main","city... - |----------------------------------------------------^""".stripMargin - interceptMessage[java.lang.ClassNotFoundException]("""co.blocke.scalajack.bogus.USAddress"""){ - sj.read[Demographic](js) - } - } - - test( - "Unknown string given as type hint value (no cooresponding match to class in mapping)" - ) { - val strMatchHintMod = - StringMatchHintModifier(Map("US" -> "co.blocke.scalajack.json.custom.USDemographic")) - val sj = - co.blocke.scalajack.ScalaJack().withHintModifiers((RType.of[Address], strMatchHintMod)) - val js = - """{"_hint":"co.blocke.scalajack.json.custom.USDemographic","age":50,"address":{"_hint":"Bogus","street":"123 Main","city":"New York","state":"NY","postalCode":"39822"}}""".asInstanceOf[JSON] - val msg = - """Couldn't marshal class for Bogus - |...USDemographic","age":50,"address":{"_hint":"Bogus","street":"123 Main","city... - |----------------------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[Demographic](js) - } - } - - test("Serialize object with unmapped hint class") { - val strMatchHintMod = - StringMatchHintModifier(Map("US" -> "co.blocke.scalajack.json.custom.USDemographic")) - val sj = - co.blocke.scalajack.ScalaJack().withHintModifiers((RType.of[Address], strMatchHintMod)) - val inst: Demographic = USDemographic( - 50, - CanadaAddress("123 Main", "New York", "NY", "39822") - ) - val msg = - """key not found: co.blocke.scalajack.json.custom.CanadaAddress""" - interceptMessage[java.util.NoSuchElementException](msg){ - sj.render(inst) - } - } diff --git a/src_old/test/scala/co.blocke.scalajack/json/custom/Model.scala b/src_old/test/scala/co.blocke.scalajack/json/custom/Model.scala deleted file mode 100644 index adfd8d61..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json/custom/Model.scala +++ /dev/null @@ -1,59 +0,0 @@ -package co.blocke.scalajack -package json.custom - -import co.blocke.scala_reflection.impl.Clazzes._ -import co.blocke.scala_reflection.info.AliasInfo -import co.blocke.scala_reflection._ -import co.blocke.scalajack.model._ - -opaque type Phone >: Null = String - -import scala.collection.mutable - -// Override just Phone -object PhoneAdapter extends TypeAdapterFactory with TypeAdapter[Phone]: - def matches(concrete: RType): Boolean = - concrete match { - case a: AliasInfo if a.name == "Phone" => true - case _ => false - } - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[Phone] = this - val info = RType.of[Phone] - override def isStringish: Boolean = true - - def read(parser: Parser): Phone = - parser.expectString() match { - case null => null.asInstanceOf[Phone] - case s: String => s.replaceAll("-", "").asInstanceOf[Phone] - } - - def write[WIRE]( - t: Phone, - writer: Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit = t match { - case null => writer.writeNull(out) - case _ => - writer.writeString( - "%s-%s-%s".format(t.toString.substring(0, 3), t.toString.substring(3, 6), t.toString.substring(6)), - out - ) - } - -case class Person(name: String, phone: Phone) - -trait Address { val postalCode: String } -case class USAddress( - street: String, - city: String, - state: String, - postalCode: String) - extends Address -case class CanadaAddress( - street: String, - city: String, - province: String, - postalCode: String) - extends Address -case class DefaultAddress(postalCode: String) extends Address -trait Demographic { val address: Address } -case class USDemographic(age: Int, address: Address) extends Demographic diff --git a/src_old/test/scala/co.blocke.scalajack/json/custom/ParseOrElse.scala b/src_old/test/scala/co.blocke.scalajack/json/custom/ParseOrElse.scala deleted file mode 100644 index 10a31998..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json/custom/ParseOrElse.scala +++ /dev/null @@ -1,19 +0,0 @@ -package co.blocke.scalajack -package json.custom - -import co.blocke.scala_reflection._ -import TestUtil._ -import munit._ -import munit.internal.console -import co.blocke.scalajack.json.JSON - -class ParseOrElse() extends FunSuite: - - test("Provide a default object if the object specified in the type hint is unknown") { - describe("-----------------------\n: ParseOrElse Tests :\n-----------------------", Console.BLUE) - - val sj = co.blocke.scalajack.ScalaJack().parseOrElse( (RType.of[Address] -> RType.of[DefaultAddress]) ) - val js = - """{"_hint":"co.blocke.scalajack.json.custom.USDemographic","age":50,"address":{"_hint":"co.blocke.scalajack.json.custom.UnknownAddress","street":"123 Main","city":"New York","state":"NY","postalCode":"39822"}}""".asInstanceOf[JSON] - assert(USDemographic(50, DefaultAddress("39822")) == sj.read[Demographic](js)) - } \ No newline at end of file diff --git a/src_old/test/scala/co.blocke.scalajack/json/mapkeys/ClassPrimKeys.scala b/src_old/test/scala/co.blocke.scalajack/json/mapkeys/ClassPrimKeys.scala deleted file mode 100644 index c83cb248..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json/mapkeys/ClassPrimKeys.scala +++ /dev/null @@ -1,319 +0,0 @@ -package co.blocke.scalajack -package json.mapkeys - -import co.blocke.scala_reflection._ -import TestUtil._ -import munit._ -import munit.internal.console -import co.blocke.scalajack.json.JSON -import java.util.UUID -import co.blocke.scalajack.model._ - -class ClassPrimKeys() extends FunSuite: - - val sj = co.blocke.scalajack.ScalaJack() - - test("Simple (flat) class as key") { - describe( - "-------------------------\n: Class Map Key Tests :\n-------------------------", Console.BLUE - ) - describe("+++ Positive Tests +++") - - val a = SimpleClass("Larry", 32, isOk = true, "golf") - val b = SimpleClass("Mike", 27, isOk = false, 125) - val inst = SampleSimple(Map(a -> b)) - val js = sj.render(inst) - assertEquals( - """{"m":{"{\"name\":\"Larry\",\"age\":32,\"isOk\":true,\"favorite\":\"golf\"}":{"name":"Mike","age":27,"isOk":false,"favorite":125}}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SampleSimple](js)) - } - - test("Complex class (having members that are classes) as key") { - val a = SimpleClass("Larry", 32, isOk = true, "golf") - val b = SimpleClass("Mike", 27, isOk = false, 125) - val c1 = ComplexClass( - UUID.fromString("1e6c2b31-4dfe-4bf6-a0a0-882caaff0e9c"), - a, - allDone = true - ) - val c2 = ComplexClass( - UUID.fromString("1e6c2b31-4dfe-4bf6-a0a0-882caaff0e9d"), - b, - allDone = false - ) - val inst = SampleComplex(Map(c1 -> c2)) - val js = sj.render(inst) - assertEquals( - """{"m":{"{\"id\":\"1e6c2b31-4dfe-4bf6-a0a0-882caaff0e9c\",\"simple\":{\"name\":\"Larry\",\"age\":32,\"isOk\":true,\"favorite\":\"golf\"},\"allDone\":true}":{"id":"1e6c2b31-4dfe-4bf6-a0a0-882caaff0e9d","simple":{"name":"Mike","age":27,"isOk":false,"favorite":125},"allDone":false}}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SampleComplex](js)) - } - - test("Simple (flat) trait as key") { - val a: Pet = FishPet("Flipper", Food.Veggies, 74.33) - val b: Pet = DogPet("Fido", Food.Meat, 3) - val inst = SamplePet(Map(a -> b)) - val js = sj.render(inst) - assertEquals( - """{"m":{"{\"_hint\":\"co.blocke.scalajack.json.mapkeys.FishPet\",\"name\":\"Flipper\",\"food\":\"Veggies\",\"waterTemp\":74.33}":{"_hint":"co.blocke.scalajack.json.mapkeys.DogPet","name":"Fido","food":"Meat","numLegs":3}}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SamplePet](js)) - } - - test("Complex trait (having members that are traits) as key") { - val a: Pet = FishPet("Flipper", Food.Veggies, 74.33) - val b: Pet = DogPet("Fido", Food.Meat, 3) - val c: Pet = CompoundPet("Legion", Food.Pellets, b) - val inst = SamplePet(Map(c -> a)) - val js = sj.render(inst) - assertEquals( - """{"m":{"{\"_hint\":\"co.blocke.scalajack.json.mapkeys.CompoundPet\",\"name\":\"Legion\",\"food\":\"Pellets\",\"pet\":{\"_hint\":\"co.blocke.scalajack.json.mapkeys.DogPet\",\"name\":\"Fido\",\"food\":\"Meat\",\"numLegs\":3}}":{"_hint":"co.blocke.scalajack.json.mapkeys.FishPet","name":"Flipper","food":"Veggies","waterTemp":74.33}}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SamplePet](js)) - } - - test( - "Complex trait (having members that are traits) as key where trait member is null" - ) { - val a: Pet = FishPet("Flipper", Food.Veggies, 74.33) - val b: Pet = null.asInstanceOf[Pet] // DogPet("Fido", Food.Meat, 3) - val c: Pet = CompoundPet("Legion", Food.Pellets, b) - val inst = SamplePet(Map(c -> a)) - val js = sj.render(inst) - assertEquals( - """{"m":{"{\"_hint\":\"co.blocke.scalajack.json.mapkeys.CompoundPet\",\"name\":\"Legion\",\"food\":\"Pellets\",\"pet\":null}":{"_hint":"co.blocke.scalajack.json.mapkeys.FishPet","name":"Flipper","food":"Veggies","waterTemp":74.33}}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SamplePet](js)) - } - - test("Class having collections as members") { - val a = PolyClass(Map("a" -> 1, "b" -> 2), List("one", "two")) - val b = PolyClass(Map("x" -> 9, "y" -> 10), List("aye", "you")) - val inst = SamplePolyClass(Map(a -> b)) - val js = sj.render(inst) - assertEquals( - """{"m":{"{\"lookup\":{\"a\":1,\"b\":2},\"favs\":[\"one\",\"two\"]}":{"lookup":{"x":9,"y":10},"favs":["aye","you"]}}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SamplePolyClass](js)) - } - - test("Class having collections as members (empty collections") { - val a = PolyClass(Map.empty[String, Int], List.empty[String]) - val b = PolyClass(Map.empty[String, Int], List.empty[String]) - val inst = SamplePolyClass(Map(a -> b)) - val js = sj.render(inst) - assertEquals( - """{"m":{"{\"lookup\":{},\"favs\":[]}":{"lookup":{},"favs":[]}}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SamplePolyClass](js)) - } - - test("Custom trait hint field and value for key trait") { - val petHintMod = StringMatchHintModifier( - Map("BreathsWater" -> "co.blocke.scalajack.json.mapkeys.FishPet", "BreathsAir" -> "co.blocke.scalajack.json.mapkeys.DogPet") - ) - val sj2 = co.blocke.scalajack.ScalaJack() - .withHints((RType.of[Pet] -> "kind")) - .withHintModifiers((RType.of[Pet] -> petHintMod)) - - val a: Pet = FishPet("Flipper", Food.Veggies, 74.33) - val b: Pet = DogPet("Fido", Food.Meat, 3) - val inst = SamplePet(Map(a -> b)) - val js = sj2.render(inst) - assertEquals( - """{"m":{"{\"kind\":\"BreathsWater\",\"name\":\"Flipper\",\"food\":\"Veggies\",\"waterTemp\":74.33}":{"kind":"BreathsAir","name":"Fido","food":"Meat","numLegs":3}}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj2.read[SamplePet](js)) - } - - test("Custom trait hint field and value for key member's trait") { - val petHintMod = StringMatchHintModifier( - Map("BreathsWater" -> "co.blocke.scalajack.json.mapkeys.FishPet", "BreathsAir" -> "co.blocke.scalajack.json.mapkeys.DogPet") - ) - val sj2 = co.blocke.scalajack.ScalaJack() - .withHints((RType.of[Pet] -> "kind")) - .withHintModifiers((RType.of[Pet] -> petHintMod)) - - val a: PetHolder = - ShinyPetHolder("123 Main", FishPet("Flipper", Food.Veggies, 74.33)) - val b: PetHolder = - ShinyPetHolder("210 North", DogPet("Fido", Food.Meat, 3)) - val inst = SampleShiny(Map(a -> b)) - val js = sj2.render(inst) - assertEquals( - """{"m":{"{\"_hint\":\"co.blocke.scalajack.json.mapkeys.ShinyPetHolder\",\"address\":\"123 Main\",\"pet\":{\"kind\":\"BreathsWater\",\"name\":\"Flipper\",\"food\":\"Veggies\",\"waterTemp\":74.33}}":{"_hint":"co.blocke.scalajack.json.mapkeys.ShinyPetHolder","address":"210 North","pet":{"kind":"BreathsAir","name":"Fido","food":"Meat","numLegs":3}}}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj2.read[SampleShiny](js)) - } - - test("Key value is a class having a noncanoncial map") { - val a = NCKey(Map(0 -> false, 1 -> true), "truth") - val b = NCKey(Map(1 -> false, 0 -> true), "lie") - val inst = SampleNCKey(Map(a -> b)) - val js = sj.render(inst) - assertEquals( - """{"m":{"{\"nc\":{\"0\":false,\"1\":true},\"name\":\"truth\"}":{"nc":{"1":false,"0":true},"name":"lie"}}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SampleNCKey](js)) - } - - test("Extra/unneeded fields in key's JSON harmlessly ignored") { - val js = - """{"m":{"{\"name\":\"Larry\",\"bogus\":false,\"age\":32,\"isOk\":true,\"favorite\":\"golf\"}":{"name":"Mike","age":27,"isOk":false,"favorite":125}}}""".asInstanceOf[JSON] - val a = SimpleClass("Larry", 32, isOk = true, "golf") - val b = SimpleClass("Mike", 27, isOk = false, 125) - val inst = SampleSimple(Map(a -> b)) - assertEquals(inst, sj.read[SampleSimple](js)) - } - - test("Bad (invalid--missing field) class json as map key") { - describe("--- Negative Tests ---") - - val js = - """{"m":{"{\"age\":32,\"favorite\":\"golf\"}":{"name":"Mike","age":27,"isOk":false,"favorite":125}}}""".asInstanceOf[JSON] - val msg = """Class co.blocke.scalajack.json.mapkeys.SimpleClass missing required fields: isOk, name - |{"age":32,"favorite":"golf"} - |---------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleSimple](js) - } - } - - test("Bad class json as map key (valid json, but wrong for given class)") { - val js = - """{"m":{"{\"name\":\"Larry\",\"age\":32,\"favorite\":\"golf\"}":{"name":"Mike","age":27,"isOk":false,"favorite":125}}}""".asInstanceOf[JSON] - val msg = """Class co.blocke.scalajack.json.mapkeys.SimpleClass missing required fields: isOk - |{"name":"Larry","age":32,"favorite":"golf"} - |------------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleSimple](js) - } - } - - test("Bad json for member class") { - val js = - """{"m":{"{\"id\":\"1e6c2b31-4dfe-4bf6-a0a0-882caaff0e9c\",\"simple\":{\"name\":\"Larry\",\"isOk\":true,\"favorite\":\"golf\"},\"allDone\":true}":{"id":"1e6c2b31-4dfe-4bf6-a0a0-882caaff0e9d","simple":{"name":"Mike","age":27,"isOk":false,"favorite":125},"allDone":false}}}""".asInstanceOf[JSON] - val msg = - """Class co.blocke.scalajack.json.mapkeys.SimpleClass missing required fields: age - |...le":{"name":"Larry","isOk":true,"favorite":"golf"},"allDone":true} - |----------------------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleComplex](js) - } - } - - test("Bad (invalid) trait json as map key") { - val js = - """{"m":{"{\"_hint\":\"co.blocke.scalajack.json.mapkeys.FishPet\":\"Flipper\" \"food\":\"Veggies\",\"waterTemp\":74.33}":{"_hint":"co.blocke.scalajack.mapkeys.DogPet","name":"Fido","food":"Meat","numLegs":3}}}""".asInstanceOf[JSON] - val msg = - """Expected comma here - |..._hint":"co.blocke.scalajack.json.mapkeys.FishPet":"Flipper" "food":"Veggies"... - |----------------------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SamplePet](js) - } - } - - test("Bad trait json (missing hint) as map key") { - val js = - """{"m":{"{\"name\":\"Flipper\",\"food\":\"Veggies\",\"waterTemp\":74.33}":{"_hint":"co.blocke.scalajack.json.mapkeys.DogPet","name":"Fido","food":"Meat","numLegs":3}}}""".asInstanceOf[JSON] - val msg = """Type hint '_hint' not found - |...ame":"Flipper","food":"Veggies","waterTemp":74.33} - |----------------------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SamplePet](js) - } - } - - test("Bad trait json (hint to unknown class) as map key") { - val js = - """{"m":{"{\"_hint\":\"co.blocke.scalajack.json.mapkeys.Bogus\",\"name\":\"Flipper\",\"food\":\"Veggies\",\"waterTemp\":74.33}":{"_hint":"co.blocke.scalajack.json.mapkeys.DogPet","name":"Fido","food":"Meat","numLegs":3}}}""".asInstanceOf[JSON] - // val msg = - // """Couldn't marshal class for co.blocke.scalajack.json.mapkeys.Bogus - // |{"_hint":"co.blocke.scalajack.json.mapkeys.Bogus","name":"Flipper","food":"Ve... - // |------------------------------------------------^""".stripMargin - interceptMessage[java.lang.ClassNotFoundException]("co.blocke.scalajack.json.mapkeys.Bogus"){ - sj.read[SamplePet](js) - } - } - - test("Bad (invalid) trait json for member trait") { - val js = - """{"m":{"{\"_hint\":\"co.blocke.scalajack.json.mapkeys.CompoundPet\",\"name\":\"Legion\",\"food\":\"Pellets\",\"pet\":{\"_hint\":\"co.blocke.scalajack.json.mapkeys.DogPet\",\"name\":\"Fido\",\"food\":\"Meat\",\"numLegs\":3}}":{"_hint":"co.blocke.scalajack.json.mapkeys.FishPet","name"}"Flipper","food":"Veggies","waterTemp":74.33}}}""".asInstanceOf[JSON] - val msg = - """Expected colon here - |..."co.blocke.scalajack.json.mapkeys.FishPet","name"}"Flipper","food":"Veggies"... - |----------------------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SamplePet](js) - } - } - - test("Bad trait json (missing hint) for member trait") { - val js = - """{"m":{"{\"_hint\":\"co.blocke.scalajack.json.mapkeys.CompoundPet\",\"name\":\"Legion\",\"food\":\"Pellets\",\"pet\":{\"_hint\":\"co.blocke.scalajack.json.mapkeys.DogPet\",\"name\":\"Fido\",\"food\":\"Meat\",\"numLegs\":3}}":{"name":"Flipper","food":"Veggies","waterTemp":74.33}}}""".asInstanceOf[JSON] - val msg = """Type hint '_hint' not found - |...ame":"Flipper","food":"Veggies","waterTemp":74.33}}} - |----------------------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SamplePet](js) - } - } - - test("Bad trait json (hint to unknown classs) for member trait") { - val js = - """{"m":{"{\"_hint\":\"co.blocke.scalajack.json.mapkeys.CompoundPet\",\"name\":\"Legion\",\"food\":\"Pellets\",\"pet\":{\"_hint\":\"co.blocke.scalajack.json.mapkeys.DogPet\",\"name\":\"Fido\",\"food\":\"Meat\",\"numLegs\":3}}":{"_hint":"co.blocke.scalajack.json.mapkeys.Bogus","name":"Flipper","food":"Veggies","waterTemp":74.33}}}""".asInstanceOf[JSON] - interceptMessage[java.lang.ClassNotFoundException]("co.blocke.scalajack.json.mapkeys.Bogus"){ - sj.read[SamplePet](js) - } - } - - test("Bad collection value in map key class having collections") { - val js = - """{"m":{"{\"lookup\":{\"a\":true,\"b\":2},\"favs\":[\"one\",\"two\"]}":{"lookup":{"x":9,"y":10},"favs":["aye","you"]}}}""".asInstanceOf[JSON] - val msg = """Expected a Number here - |{"lookup":{"a":true,"b":2},"favs":["one","two"]} - |---------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SamplePolyClass](js) - } - } - - test("Bad custom hint value for map key trait (sort instead of kind") { - val petHintMod = StringMatchHintModifier( - Map("BreathsWater" -> "co.blocke.scalajack.json.mapkeys.FishPet", "BreathsAir" -> "co.blocke.scalajack.json.mapkeys.DogPet") - ) - val sj2 = co.blocke.scalajack.ScalaJack() - .withHints(RType.of[Pet] -> "kind") - .withHintModifiers(RType.of[Pet] -> petHintMod) - val js = - """{"m":{"{\"_hint\":\"co.blocke.scalajack.json.mapkeys.ShinyPetHolder\",\"address\":\"123 Main\",\"pet\":{\"sort\":\"BreathsWater\",\"name\":\"Flipper\",\"food\":\"Veggies\",\"waterTemp\":74.33}}":{"_hint":"co.blocke.scalajack.json.mapkeys.ShinyPetHolder","address":"210 North","pet":{"kind":"BreathsAir","name":"Fido","food":"Meat","numLegs":3}}}}""".asInstanceOf[JSON] - val msg = """Type hint 'kind' not found - |...ame":"Flipper","food":"Veggies","waterTemp":74.33}} - |----------------------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj2.read[SampleShiny](js) - } - } - - test("Bad class for hint in Map key (trait)") { - val petHintMod = StringMatchHintModifier( - Map("BreathsWater" -> "co.blocke.scalajack.json.mapkeys.FishPet", "BreathsAir" -> "co.blocke.scalajack.json.mapkeys.DogPet") - ) - val sj2 = co.blocke.scalajack.ScalaJack() - .withHints(RType.of[Pet] -> "kind") - .withHintModifiers(RType.of[Pet] -> petHintMod) - val js = - """{"m":{"{\"_hint\":\"co.blocke.scalajack.mapkeys.Bogus\",\"address\":\"123 Main\",\"pet\":{\"kind\":\"BreathsLava\",\"name\":\"Flipper\",\"food\":\"Veggies\",\"waterTemp\":74.33}}":{"_hint":"co.blocke.scalajack.json.mapkeys.ShinyPetHolder","address":"210 North","pet":{"kind":"BreathsAir","name":"Fido","food":"Meat","numLegs":3}}}}""".asInstanceOf[JSON] - interceptMessage[java.lang.ClassNotFoundException]("co.blocke.scalajack.mapkeys.Bogus"){ - sj2.read[SampleShiny](js) - } - } - - test("Bad custom hint value for Map key member's trait") { - val petHintMod = StringMatchHintModifier( - Map("BreathsWater" -> "co.blocke.scalajack.json.mapkeys.FishPet", "BreathsAir" -> "co.blocke.scalajack.json.mapkeys.DogPet") - ) - val sj2 = co.blocke.scalajack.ScalaJack() - .withHints(RType.of[Pet] -> "kind") - .withHintModifiers((RType.of[Pet] -> petHintMod)) - val js = - """{"m":{"{\"_hint\":\"co.blocke.scalajack.json.mapkeys.ShinyPetHolder\",\"address\":\"123 Main\",\"pet\":{\"kind\":\"BreathsLava\",\"name\":\"Flipper\",\"food\":\"Veggies\",\"waterTemp\":74.33}}":{"_hint":"co.blocke.scalajack.json.mapkeys.ShinyPetHolder","address":"210 North","pet":{"kind":"BreathsAir","name":"Fido","food":"Meat","numLegs":3}}}}""".asInstanceOf[JSON] - val msg = """Couldn't marshal class for BreathsLava - |...","address":"123 Main","pet":{"kind":"BreathsLava","name":"Flipper","food":"... - |----------------------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj2.read[SampleShiny](js) - } - } \ No newline at end of file diff --git a/src_old/test/scala/co.blocke.scalajack/json/mapkeys/JavaPrimKeys.scala b/src_old/test/scala/co.blocke.scalajack/json/mapkeys/JavaPrimKeys.scala deleted file mode 100644 index b28689e5..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json/mapkeys/JavaPrimKeys.scala +++ /dev/null @@ -1,500 +0,0 @@ -package co.blocke.scalajack -package json.mapkeys - -import co.blocke.scala_reflection._ -import TestUtil._ -import munit._ -import munit.internal.console -import co.blocke.scalajack.json.JSON -import java.lang.{ - Boolean => JBoolean, - Byte => JByte, - Character => JChar, - Double => JDouble, - Float => JFloat, - Integer => JInteger, - Long => JLong, - Short => JShort -} -import java.math.{ BigDecimal => JBigDecimal, BigInteger => JBigInteger } -import java.time._ - -import co.blocke.scalajack.json.JsonMatcher - -class JavaPrimKeys() extends FunSuite: - - val sj = co.blocke.scalajack.ScalaJack() - - test("With BigDecimal Key") { - describe( - "----------------------------------\n: Java Primitive Map Key Tests :\n----------------------------------", Console.BLUE - ) - describe("+++ Positive Tests +++") - describe("Simple DelimSpec:") - - val inst = SampleJBigDecimal( - Map( - new JBigDecimal("123.456") -> new JBigDecimal("1"), - new JBigDecimal("789.123") -> new JBigDecimal("2") - ) - ) - val js = sj.render(inst) - assertEquals("""{"m":{"123.456":1,"789.123":2}}""".asInstanceOf[JSON], js) - assertEquals(inst, sj.read[SampleJBigDecimal](js)) - } - - test("With BigInteger Key") { - val inst = SampleJBigInteger( - Map( - new JBigInteger("123") -> new JBigInteger("1"), - new JBigInteger("789") -> new JBigInteger("2") - ) - ) - val js = sj.render(inst) - assertEquals("""{"m":{"123":1,"789":2}}""".asInstanceOf[JSON], js) - assertEquals(inst, sj.read[SampleJBigInteger](js)) - } - - test("With Boolean Key") { - val inst = SampleJBoolean( - Map( - true.asInstanceOf[JBoolean] -> false.asInstanceOf[JBoolean], - false.asInstanceOf[JBoolean] -> true.asInstanceOf[JBoolean] - ) - ) - val js = sj.render(inst) - assertEquals("""{"m":{"true":false,"false":true}}""".asInstanceOf[JSON], js) - assertEquals(inst, sj.read[SampleJBoolean](js)) - } - - test("With Byte Key") { - val inst = SampleJByte( - Map( - 16.toByte.asInstanceOf[JByte] -> 2.toByte.asInstanceOf[JByte], - 48.toByte.asInstanceOf[JByte] -> 9.toByte.asInstanceOf[JByte] - ) - ) - val js = sj.render(inst) - assertEquals("""{"m":{"16":2,"48":9}}""".asInstanceOf[JSON], js) - assertEquals(inst, sj.read[SampleJByte](js)) - } - - test("With Char Key") { - val inst = SampleJChar( - Map( - 'a'.asInstanceOf[JChar] -> 'A'.asInstanceOf[JChar], - 'z'.asInstanceOf[JChar] -> 'Z'.asInstanceOf[JChar] - ) - ) - val js = sj.render(inst) - assertEquals("""{"m":{"a":"A","z":"Z"}}""".asInstanceOf[JSON], js) - assertEquals(inst, sj.read[SampleJChar](js)) - } - - test("With Double Key") { - val inst = SampleJDouble( - Map( - 12.34.asInstanceOf[JDouble] -> 56.78.asInstanceOf[JDouble], - 90.12.asInstanceOf[JDouble] -> 34.56.asInstanceOf[JDouble] - ) - ) - val js = sj.render(inst) - assertEquals("""{"m":{"12.34":56.78,"90.12":34.56}}""".asInstanceOf[JSON], js) - assertEquals(inst, sj.read[SampleJDouble](js)) - } - - test("With Float Key") { - val inst = SampleJFloat( - Map( - 12.34F.asInstanceOf[JFloat] -> 56.78F.asInstanceOf[JFloat], - 90.12F.asInstanceOf[JFloat] -> 34.56F.asInstanceOf[JFloat] - ) - ) - val js = sj.render(inst) - assertEquals("""{"m":{"12.34":56.78,"90.12":34.56}}""".asInstanceOf[JSON], js) - assertEquals(inst, sj.read[SampleJFloat](js)) - } - - test("With Integer Key") { - val inst = SampleJInteger( - Map( - 12.asInstanceOf[JInteger] -> 56.asInstanceOf[JInteger], - 90.asInstanceOf[JInteger] -> 34.asInstanceOf[JInteger] - ) - ) - val js = sj.render(inst) - assertEquals("""{"m":{"12":56,"90":34}}""".asInstanceOf[JSON], js) - assertEquals(inst, sj.read[SampleJInteger](js)) - } - - test("With Long Key") { - val inst = SampleJLong( - Map( - 12L.asInstanceOf[JLong] -> 56L.asInstanceOf[JLong], - 90L.asInstanceOf[JLong] -> 34L.asInstanceOf[JLong] - ) - ) - val js = sj.render(inst) - assertEquals("""{"m":{"12":56,"90":34}}""".asInstanceOf[JSON], js) - assertEquals(inst, sj.read[SampleJLong](js)) - } - - - test("With Number Key") { - val inst = SampleJNumber( - Map( - JByte.valueOf("-128") -> JByte.valueOf("127"), - JShort.valueOf("-32768") -> JShort.valueOf("32767"), - JInteger.valueOf("-2147483648") -> JInteger.valueOf("2147483647"), - JLong.valueOf("-9223372036854775808") -> JLong - .valueOf("9223372036854755807"), - JByte.valueOf("0") -> new JBigInteger("9923372036854755810"), - JFloat.valueOf("3.4e-038") -> JFloat.valueOf("3.4e+038"), - JDouble.valueOf("1.7e-308") -> JDouble.valueOf("1.7e+308"), - new JBigDecimal("1.8e+308") -> JFloat.valueOf("0.0") - ) - ) - val result = SampleJNumber( - Map( - JByte.valueOf("0") -> new JBigDecimal("9923372036854755810"), - JInteger.valueOf("-2147483648") -> JInteger.valueOf("2147483647"), - JLong.valueOf("-9223372036854775808") -> JLong - .valueOf("9223372036854755807"), - JByte.valueOf("-128") -> JByte.valueOf("127"), - JFloat.valueOf("3.4E-38") -> JFloat.valueOf("3.4E38"), - JShort.valueOf("-32768") -> JShort.valueOf("32767"), - new JBigDecimal("1.8E+308") -> JByte.valueOf("0"), - JDouble.valueOf("1.7E-308") -> JDouble.valueOf("1.7E308") - ) - ) - val js = sj.render(inst) - assert( JsonMatcher.jsonMatches(js, - """{"m":{"0":9923372036854755810,"-2147483648":2147483647,"-9223372036854775808":9223372036854755807,"-128":127,"3.4E-38":3.4E38,"-32768":32767,"1.8E+308":0.0,"1.7E-308":1.7E308}}""".asInstanceOf[JSON])) - val read = sj.read[SampleJNumber](js) - assertEquals(result, read) - } - - test("With Short Key") { - val inst = SampleJShort( - Map( - 12.toShort.asInstanceOf[JShort] -> 56.toShort - .asInstanceOf[JShort], - 90.toShort.asInstanceOf[JShort] -> 34.toShort.asInstanceOf[JShort] - ) - ) - val js = sj.render(inst) - assertEquals("""{"m":{"12":56,"90":34}}""".asInstanceOf[JSON], js) - assertEquals(inst, sj.read[SampleJShort](js)) - } - - - test("With Duration Key") { - describe("Time DelimSpec:") - - val inst = - SampleDuration(Map(Duration.ZERO -> Duration.parse("P2DT3H4M"))) - val js = sj.render(inst) - assertEquals("""{"m":{"PT0S":"PT51H4M"}}""".asInstanceOf[JSON], js) - assertEquals(inst, sj.read[SampleDuration](js)) - } - - test("With Instant Key") { - val inst = SampleInstant( - Map( - Instant.EPOCH -> Instant.MAX, - Instant.MIN -> Instant.parse("2007-12-03T10:15:30.00Z") - ) - ) - val js = sj.render(inst) - assertEquals( - """{"m":{"1970-01-01T00:00:00Z":"+1000000000-12-31T23:59:59.999999999Z","-1000000000-01-01T00:00:00Z":"2007-12-03T10:15:30Z"}}""".asInstanceOf[JSON], js) - assertEquals(inst, sj.read[SampleInstant](js)) - } - - test("With LocalDateTime Key") { - val inst = SampleLocalDateTime( - Map( - LocalDateTime.MAX -> LocalDateTime.MIN, - LocalDateTime.parse("2007-12-03T10:15:30") -> null - ) - ) - val js = sj.render(inst) - assertEquals( - """{"m":{"+999999999-12-31T23:59:59.999999999":"-999999999-01-01T00:00:00","2007-12-03T10:15:30":null}}""".asInstanceOf[JSON], js) - assertEquals(inst, sj.read[SampleLocalDateTime](js)) - } - - test("With LocalDate Key") { - val inst = SampleLocalDate( - Map( - LocalDate.MAX -> LocalDate.MIN, - LocalDate.parse("2007-12-03") -> null - ) - ) - val js = sj.render(inst) - assertEquals( - """{"m":{"+999999999-12-31":"-999999999-01-01","2007-12-03":null}}""".asInstanceOf[JSON], js) - assertEquals(inst, sj.read[SampleLocalDate](js)) - } - - test("With LocalTime Key") { - val inst = SampleLocalTime( - Map( - LocalTime.MAX -> LocalTime.MIN, - LocalTime.MIDNIGHT -> LocalTime.NOON, - LocalTime.parse("10:15:30") -> null - ) - ) - val js = sj.render(inst) - assertEquals( - """{"m":{"23:59:59.999999999":"00:00:00","00:00:00":"12:00:00","10:15:30":null}}""".asInstanceOf[JSON], js) - assertEquals(inst, sj.read[SampleLocalTime](js)) - } - - test("With OffsetDateTime Key") { - val inst = SampleOffsetDateTime( - Map( - OffsetDateTime.MAX -> OffsetDateTime.MIN, - OffsetDateTime.parse("2007-12-03T10:15:30+01:00") -> null - ) - ) - val js = sj.render(inst) - assertEquals( - """{"m":{"+999999999-12-31T23:59:59.999999999-18:00":"-999999999-01-01T00:00:00+18:00","2007-12-03T10:15:30+01:00":null}}""".asInstanceOf[JSON], js) - assertEquals(inst, sj.read[SampleOffsetDateTime](js)) - } - - test("With OffsetTime Key") { - val inst = SampleOffsetTime( - Map( - OffsetTime.MAX -> OffsetTime.MIN, - OffsetTime.parse("10:15:30+01:00") -> null - ) - ) - val js = sj.render(inst) - assertEquals( - """{"m":{"23:59:59.999999999-18:00":"00:00:00+18:00","10:15:30+01:00":null}}""".asInstanceOf[JSON], js) - assertEquals(inst, sj.read[SampleOffsetTime](js)) - } - - test("With Period Key") { - val inst = SamplePeriod(Map(Period.ZERO -> Period.parse("P1Y2M3D"))) - val js = sj.render(inst) - assertEquals("""{"m":{"P0D":"P1Y2M3D"}}""".asInstanceOf[JSON], js) - assertEquals(inst, sj.read[SamplePeriod](js)) - } - - test("With ZonedDateTime Key") { - val inst = SampleZonedDateTime( - Map( - ZonedDateTime - .parse("2007-12-03T10:15:30+01:00[Europe/Paris]") -> null - ) - ) - val js = sj.render(inst) - assertEquals( - """{"m":{"2007-12-03T10:15:30+01:00[Europe/Paris]":null}}""" - .asInstanceOf[JSON], js) - assertEquals(inst, sj.read[SampleZonedDateTime](js)) - } - - test("Bad BigDecimal Key") { - describe("--- Negative Tests ---") - describe("Simple DelimSpec:") - - val js = """{"m":{"fred":1,"789.123":2}}""".asInstanceOf[JSON] - val msg = """Expected a Number here - |fred - |^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleJBigDecimal](js) - } - } - - test("Bad BigInt Key") { - val js = """{"m":{"fred":1,"789":2}}""".asInstanceOf[JSON] - val msg = """Expected a Number here - |fred - |^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleJBigInteger](js) - } - } - - test("Bad Boolean Key") { - val js = """{"m":{"true":false,"123":true}}""".asInstanceOf[JSON] - val msg = """Expected a Boolean here - |123 - |^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleJBoolean](js) - } - } - - test("Bad Byte Key") { - val js = """{"m":{"16":2,"4x8":9}}""".asInstanceOf[JSON] - val msg = """Expected a Number here - |4x8 - |-^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleJByte](js) - } - } - - test("Bad Char Key") { // NOTE: This comprehensively tests for any null keyed Map - val js = """{"m":{null:"A","z":"Z"}}""".asInstanceOf[JSON] - val msg = """Map keys cannot be null - |{"m":{null:"A","z":"Z"}} - |----------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleJChar](js) - } - } - - test("Bad Double Key") { - val js = """{"m":{"12.34":56.78,"true":34.56}}""".asInstanceOf[JSON] - val msg = """Expected a Number here - |true - |^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleJDouble](js) - } - } - - test("Bad Float Key") { - val js = """{"m":{"12.34":56.78,"90.12.3":34.56}}""".asInstanceOf[JSON] - interceptMessage[java.lang.NumberFormatException]("multiple points"){ - sj.read[SampleJFloat](js) - } - } - - test("Bad Int Key") { - val js = """{"m":{"12.0":56,"90":34}}""".asInstanceOf[JSON] - interceptMessage[java.lang.NumberFormatException]("For input string: \"12.0\""){ - sj.read[SampleJInteger](js) - } - } - - test("Bad Long Key") { - val js = """{"m":{"12":56,"hey":34}}""".asInstanceOf[JSON] - val msg = """Expected a Number here - |hey - |^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleJLong](js) - } - } - - test("Bad Number Key") { - val js = - """{"m":{"flume":9923372036854755810,"-2147483648":2147483647,"-9223372036854775808":9223372036854755807,"-128":127,"3.4E-38":3.4E38,"-32768":32767,"1.8E+308":0.0,"1.7E-308":1.7E308}}""".asInstanceOf[JSON] - val msg = """Expected a Number here - |flume - |^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleJNumber](js) - } - } - - test("Bad Duration Key") { - describe("Time DelimSpec:") - - val js = """{"m":{"PT0SXXX":"PT51H4M"}}""".asInstanceOf[JSON] - val msg = """Failed to parse Duration from input 'PT0SXXX' - |{"m":{"PT0SXXX":"PT51H4M"}} - |--------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleDuration](js) - } - } - - test("Bad Instant Key") { - val js = - """{"m":{"1970-01-01T00:00:00Z":"+1000000000-12-31T23:59:59.999999999Z","bogus":"2007-12-03T10:15:30Z"}}""".asInstanceOf[JSON] - val msg = - """Failed to parse Instant from input 'bogus' - |...Z":"+1000000000-12-31T23:59:59.999999999Z","bogus":"2007-12-03T10:15:30Z"}} - |----------------------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleInstant](js) - } - } - - test("Bad LocalDateTime Key") { - val js = - """{"m":{"+999999999-12-31T23:59:59.999999999":"-999999999-01-01T00:00:00","bogus":null}}""".asInstanceOf[JSON] - val msg = - """Failed to parse LocalDateTime from input 'bogus' - |...:59.999999999":"-999999999-01-01T00:00:00","bogus":null}} - |----------------------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleLocalDateTime](js) - } - } - - test("Bad LocalDate Key") { - val js = """{"m":{"bogus":"-999999999-01-01","2007-12-03":null}}""".asInstanceOf[JSON] - val msg = """Failed to parse LocalDate from input 'bogus' - |{"m":{"bogus":"-999999999-01-01","2007-12-03":null}} - |------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleLocalDate](js) - } - } - - test("Bad LocalTime Key") { - val js = - """{"m":{"23:59:59.999999999":"00:00:00","nada":"12:00:00","10:15:30":null}}""".asInstanceOf[JSON] - val msg = - """Failed to parse LocalTime from input 'nada' - |{"m":{"23:59:59.999999999":"00:00:00","nada":"12:00:00","10:15:30":null}} - |-------------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleLocalTime](js) - } - } - - test("Bad OffsetDateTime Key") { - val js = - """{"m":{"false":"-999999999-01-01T00:00:00+18:00","2007-12-03T10:15:30+01:00":null}}""".asInstanceOf[JSON] - val msg = - """Failed to parse OffsetDateTime from input 'false' - |{"m":{"false":"-999999999-01-01T00:00:00+18:00","2007-12-03T10:15:30+01:00":n... - |------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleOffsetDateTime](js) - } - } - - test("Bad OffsetTime Key") { - val js = """{"m":{"2007-12-03T10:15:30+01:00[Europe\/Bogus]":null}}""".asInstanceOf[JSON] - val msg = - """Failed to parse OffsetTime from input '2007-12-03T10:15:30+01:00[Europe/Bogus]' - |{"m":{"2007-12-03T10:15:30+01:00[Europe\/Bogus]":null}} - |-----------------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleOffsetTime](js) - } - } - - test("Bad Period Key") { - val js = """{"m":{"P0D???":"P1Y2M3D"}}""".asInstanceOf[JSON] - val msg = """Failed to parse Period from input 'P0D???' - |{"m":{"P0D???":"P1Y2M3D"}} - |-------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SamplePeriod](js) - } - } - - test("Bad ZonedDateTime Key") { - val js = - """{"m":{"FRED23:59:59.999999999-18:00":"00:00:00+18:00","10:15:30+01:00":null}}""".asInstanceOf[JSON] - val msg = - """Failed to parse ZonedDateTime from input 'FRED23:59:59.999999999-18:00' - |{"m":{"FRED23:59:59.999999999-18:00":"00:00:00+18:00","10:15:30+01:00":null}} - |-----------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleZonedDateTime](js) - } - } \ No newline at end of file diff --git a/src_old/test/scala/co.blocke.scalajack/json/mapkeys/ListCollKeys.scala b/src_old/test/scala/co.blocke.scalajack/json/mapkeys/ListCollKeys.scala deleted file mode 100644 index 599a3e8d..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json/mapkeys/ListCollKeys.scala +++ /dev/null @@ -1,125 +0,0 @@ -package co.blocke.scalajack -package json.mapkeys - -import co.blocke.scala_reflection._ -import TestUtil._ -import munit._ -import munit.internal.console -import co.blocke.scalajack.json.JSON - -class ListCollKeys() extends FunSuite: - - val sj = co.blocke.scalajack.ScalaJack() - - test("List as key") { - describe( - "------------------------\n: List Map Key Tests :\n------------------------", Console.BLUE - ) - - val l1 = List(1, 2, 3) - val l2 = List(4, 5, 6) - val inst = Map(l1 -> l2) - val js = sj.render(inst) - assertEquals("""{"[1,2,3]":[4,5,6]}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[Map[List[Int], List[Int]]](js)) - } - - test("List of Lists as key") { - val l1 = List(List(1, 2, 3), List(9, 8, 7)) - val l2 = List(List(4, 5, 6), List(1, 3, 5)) - val inst = Map(l1 -> l2) - val js = sj.render(inst) - assertEquals("""{"[[1,2,3],[9,8,7]]":[[4,5,6],[1,3,5]]}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[Map[List[List[Int]], List[List[Int]]]](js)) - } - - test("List of Tuples as key") { - val l1: List[(String, String)] = List(("A", "a"), ("B", "b"), (null, "c")) - val l2: List[(String, String)] = List(("X", "x"), ("Y", "y"), (null, "z")) - val inst = Map(l1 -> l2) - val js = sj.render(inst) - assertEquals( - """{"[[\"A\",\"a\"],[\"B\",\"b\"],[null,\"c\"]]":[["X","x"],["Y","y"],[null,"z"]]}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[Map[List[(String, String)], List[(String, String)]]](js)) - } - - test("List of Maps as key") { - val l1 = List(Map("wow" -> true), Map("ya" -> false)) - val l2 = List(Map("zing" -> false), Map("bling" -> true)) - val inst = Map(l1 -> l2) - val js = sj.render(inst) - assertEquals( - """{"[{\"wow\":true},{\"ya\":false}]":[{"zing":false},{"bling":true}]}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[Map[List[Map[String, Boolean]], List[Map[String, Boolean]]]](js)) - } - - test("List of Case Class as key") { - val fish = FishPet("Flipper", Food.Meat, 68.9) - val inst = Map(List(fish, fish) -> List(fish, fish)) - val js = sj.render(inst) - assertEquals( - """{"[{\"name\":\"Flipper\",\"food\":\"Meat\",\"waterTemp\":68.9},{\"name\":\"Flipper\",\"food\":\"Meat\",\"waterTemp\":68.9}]":[{"name":"Flipper","food":"Meat","waterTemp":68.9},{"name":"Flipper","food":"Meat","waterTemp":68.9}]}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[Map[List[FishPet], List[FishPet]]](js)) - } - - test("List of Trait as key") { - val fish: Pet = FishPet("Flipper", Food.Meat, 68.9) - val inst = Map(List(fish, fish) -> List(fish, fish)) - val js = sj.render(inst) - assertEquals( - """{"[{\"_hint\":\"co.blocke.scalajack.json.mapkeys.FishPet\",\"name\":\"Flipper\",\"food\":\"Meat\",\"waterTemp\":68.9},{\"_hint\":\"co.blocke.scalajack.json.mapkeys.FishPet\",\"name\":\"Flipper\",\"food\":\"Meat\",\"waterTemp\":68.9}]":[{"_hint":"co.blocke.scalajack.json.mapkeys.FishPet","name":"Flipper","food":"Meat","waterTemp":68.9},{"_hint":"co.blocke.scalajack.json.mapkeys.FishPet","name":"Flipper","food":"Meat","waterTemp":68.9}]}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[Map[List[Pet], List[Pet]]](js)) - } - - test("List of Any as key") { - val inst: Map[List[Any], List[Any]] = - Map(List(23L, "wow", true) -> List(12.2, 0)) - val js = sj.render(inst) - assertEquals("""{"[23,\"wow\",true]":[12.2,0.0]}""".asInstanceOf[JSON],js) - assertEquals(true, sj.read[Map[List[Any], List[Any]]](js).isInstanceOf[Map[List[Any], List[Any]]]) - } - - test("List of parameterized class as key") { - val inst = Map( - List(AThing(true, "True"), AThing(false, "False")) -> List( - AThing(true, "Yes"), - AThing(false, "No") - ) - ) - val js = sj.render(inst) - assertEquals( - """{"[{\"a\":true,\"b\":\"True\"},{\"a\":false,\"b\":\"False\"}]":[{"a":true,"b":"Yes"},{"a":false,"b":"No"}]}""".asInstanceOf[JSON],js) - assertEquals(true, sj.read[Map[List[AThing[String, Boolean]], List[AThing[String, Boolean]]]](js) - .isInstanceOf[Map[List[AThing[String, Boolean]], List[AThing[String, Boolean]]]]) - } - - test("List of parameterized trait as key") { - val inst: Map[List[Thing[Boolean, String]], List[Thing[Boolean, String]]] = - Map( - List(AThing(true, "True"), AThing(false, "False")) -> List( - AThing(true, "Yes"), - AThing(false, "No") - ) - ) - val js = sj.render(inst) - assertEquals( - """{"[{\"_hint\":\"co.blocke.scalajack.json.mapkeys.AThing\",\"a\":true,\"b\":\"True\"},{\"_hint\":\"co.blocke.scalajack.json.mapkeys.AThing\",\"a\":false,\"b\":\"False\"}]":[{"_hint":"co.blocke.scalajack.json.mapkeys.AThing","a":true,"b":"Yes"},{"_hint":"co.blocke.scalajack.json.mapkeys.AThing","a":false,"b":"No"}]}""".asInstanceOf[JSON],js) - assertEquals(true, sj.read[Map[List[Thing[Boolean, String]], List[Thing[Boolean, String]]]](js) - .isInstanceOf[Map[List[Thing[Boolean, String]], List[Thing[Boolean, String]]]]) - } - - test("List of Optional as key") { - val inst: Map[List[Option[String]], List[Option[String]]] = - Map(List(Some("hey"), Some("you")) -> List(Some("stop"), Some("go"))) - val js = sj.render(inst) - assertEquals("""{"[\"hey\",\"you\"]":["stop","go"]}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[Map[List[Option[String]], List[Option[String]]]](js)) - } - - test("List of ValueClass as key") { - val inst = - Map(List(VCChar('A'), VCChar('a')) -> List(VCChar('B'), VCChar('b'))) - val js = sj.render(inst) - assertEquals("""{"[\"A\",\"a\"]":["B","b"]}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[Map[List[VCChar], List[VCChar]]](js)) - } diff --git a/src_old/test/scala/co.blocke.scalajack/json/mapkeys/MapCollKeys.scala b/src_old/test/scala/co.blocke.scalajack/json/mapkeys/MapCollKeys.scala deleted file mode 100644 index e618172c..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json/mapkeys/MapCollKeys.scala +++ /dev/null @@ -1,166 +0,0 @@ -package co.blocke.scalajack -package json.mapkeys - -import co.blocke.scala_reflection._ -import TestUtil._ -import munit._ -import munit.internal.console -import co.blocke.scalajack.json.JSON - -class MapCollKeys() extends FunSuite: - - val sj = co.blocke.scalajack.ScalaJack() - - test("Map as key") { - describe( - "-----------------------\n: Map Map Key Tests :\n-----------------------", Console.BLUE - ) - - val m1 = Map(1 -> 2) - val m2 = Map(3 -> 4) - val inst = Map(m1 -> m2) - val js = sj.render(inst) - assertEquals("""{"{\"1\":2}":{"3":4}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[Map[Map[Int, Int], Map[Int, Int]]](js)) - } - - test("Map as key, map value is null") { - val m1 = Map(1 -> 2) - val m2: Map[Int, Int] = null - val inst = Map(m1 -> m2) - val js = sj.render(inst) - assertEquals("""{"{\"1\":2}":null}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[Map[Map[Int, Int], Map[Int, Int]]](js)) - } - - test("Map of Lists as key") { - val m1 = List(Food.Meat, Food.Veggies) - val m2 = List(Food.Seeds, Food.Pellets) - val inst = Map(Map(m1 -> m2) -> Map(m2 -> m1)) - val js = sj.render(inst) - assertEquals( - """{"{\"[\\\"Meat\\\",\\\"Veggies\\\"]\":[\"Seeds\",\"Pellets\"]}":{"[\"Seeds\",\"Pellets\"]":["Meat","Veggies"]}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[Map[Map[List[Food.Value], List[Food.Value]], Map[List[Food.Value], List[Food.Value]]]](js)) - } - - test("Map of Maps as key") { - val m1 = Map(Food.Meat -> Food.Veggies) - val m2 = Map(Food.Seeds -> Food.Pellets) - val inst = Map(Map(m1 -> m2) -> Map(m2 -> m1)) - val js = sj.render(inst) - assertEquals( - """{"{\"{\\\"Meat\\\":\\\"Veggies\\\"}\":{\"Seeds\":\"Pellets\"}}":{"{\"Seeds\":\"Pellets\"}":{"Meat":"Veggies"}}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[Map[Map[Map[Food.Value, Food.Value], Map[Food.Value, Food.Value]], Map[Map[Food.Value, Food.Value], Map[Food.Value, Food.Value]]]](js)) - } - - test("Map of Tuples as key") { - val m1 = (Food.Meat, Food.Veggies) - val m2 = (Food.Seeds, Food.Pellets) - val inst = Map(Map(m1 -> m2) -> Map(m2 -> m1)) - val js = sj.render(inst) - assertEquals( - """{"{\"[\\\"Meat\\\",\\\"Veggies\\\"]\":[\"Seeds\",\"Pellets\"]}":{"[\"Seeds\",\"Pellets\"]":["Meat","Veggies"]}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[Map[Map[(Food.Value, Food.Value), (Food.Value, Food.Value)], Map[(Food.Value, Food.Value), (Food.Value, Food.Value)]]](js)) - } - - test("Map of Case Class as key") { - val m1 = - Map(DogPet("Fido", Food.Meat, 4) -> FishPet("Flipper", Food.Meat, 87.3)) - val m2 = - Map(FishPet("Flipper", Food.Meat, 87.3) -> DogPet("Fido", Food.Meat, 4)) - val inst = Map(Map(m1 -> m2) -> Map(m2 -> m1)) - val js = sj.render(inst) - assertEquals( - """{"{\"{\\\"{\\\\\\\"name\\\\\\\":\\\\\\\"Fido\\\\\\\",\\\\\\\"food\\\\\\\":\\\\\\\"Meat\\\\\\\",\\\\\\\"numLegs\\\\\\\":4}\\\":{\\\"name\\\":\\\"Flipper\\\",\\\"food\\\":\\\"Meat\\\",\\\"waterTemp\\\":87.3}}\":{\"{\\\"name\\\":\\\"Flipper\\\",\\\"food\\\":\\\"Meat\\\",\\\"waterTemp\\\":87.3}\":{\"name\":\"Fido\",\"food\":\"Meat\",\"numLegs\":4}}}":{"{\"{\\\"name\\\":\\\"Flipper\\\",\\\"food\\\":\\\"Meat\\\",\\\"waterTemp\\\":87.3}\":{\"name\":\"Fido\",\"food\":\"Meat\",\"numLegs\":4}}":{"{\"name\":\"Fido\",\"food\":\"Meat\",\"numLegs\":4}":{"name":"Flipper","food":"Meat","waterTemp":87.3}}}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[Map[Map[Map[DogPet, FishPet], Map[FishPet, DogPet]], Map[Map[FishPet, DogPet], Map[DogPet, FishPet]]]](js)) - } - - test("Map of Trait as key") { - val m1: Map[Pet, Pet] = - Map(DogPet("Fido", Food.Meat, 4) -> FishPet("Flipper", Food.Meat, 87.3)) - val m2: Map[Pet, Pet] = - Map(FishPet("Flipper", Food.Meat, 87.3) -> DogPet("Fido", Food.Meat, 4)) - val inst = Map(Map(m1 -> m2) -> Map(m2 -> m1)) - val js = sj.render(inst) - assertEquals( - """{"{\"{\\\"{\\\\\\\"_hint\\\\\\\":\\\\\\\"co.blocke.scalajack.json.mapkeys.DogPet\\\\\\\",\\\\\\\"name\\\\\\\":\\\\\\\"Fido\\\\\\\",\\\\\\\"food\\\\\\\":\\\\\\\"Meat\\\\\\\",\\\\\\\"numLegs\\\\\\\":4}\\\":{\\\"_hint\\\":\\\"co.blocke.scalajack.json.mapkeys.FishPet\\\",\\\"name\\\":\\\"Flipper\\\",\\\"food\\\":\\\"Meat\\\",\\\"waterTemp\\\":87.3}}\":{\"{\\\"_hint\\\":\\\"co.blocke.scalajack.json.mapkeys.FishPet\\\",\\\"name\\\":\\\"Flipper\\\",\\\"food\\\":\\\"Meat\\\",\\\"waterTemp\\\":87.3}\":{\"_hint\":\"co.blocke.scalajack.json.mapkeys.DogPet\",\"name\":\"Fido\",\"food\":\"Meat\",\"numLegs\":4}}}":{"{\"{\\\"_hint\\\":\\\"co.blocke.scalajack.json.mapkeys.FishPet\\\",\\\"name\\\":\\\"Flipper\\\",\\\"food\\\":\\\"Meat\\\",\\\"waterTemp\\\":87.3}\":{\"_hint\":\"co.blocke.scalajack.json.mapkeys.DogPet\",\"name\":\"Fido\",\"food\":\"Meat\",\"numLegs\":4}}":{"{\"_hint\":\"co.blocke.scalajack.json.mapkeys.DogPet\",\"name\":\"Fido\",\"food\":\"Meat\",\"numLegs\":4}":{"_hint":"co.blocke.scalajack.json.mapkeys.FishPet","name":"Flipper","food":"Meat","waterTemp":87.3}}}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[Map[Map[Map[Pet, Pet], Map[Pet, Pet]], Map[Map[Pet, Pet], Map[Pet, Pet]]]](js)) - } - - test("Map of Any as key") { - val m1: Map[Any, Any] = Map(123.45 -> 2) - val m2: Map[Any, Any] = Map(398328372 -> 0) - val inst = Map(Map(m1 -> m2) -> Map(m2 -> m1)) - val js = sj.render(inst) - assertEquals( - """{"{\"{\\\"123.45\\\":2}\":{\"398328372\":0}}":{"{\"398328372\":0}":{"123.45":2}}}""".asInstanceOf[JSON],js) - assertEquals(true, - sj.read[Map[Map[Map[Any, Any], Map[Any, Any]], Map[Map[Any, Any], Map[Any, Any]]]](js) - .isInstanceOf[Map[Map[Map[Any, Any], Map[Any, Any]], Map[Map[Any, Any], Map[Any, Any]]]]) - } - - test("Map of parameterized class as key") { - val m1: Map[AThing[Int, String], AThing[Int, String]] = - Map(AThing("one", 1) -> AThing("two", 2)) - val m2: Map[AThing[Int, String], AThing[Int, String]] = - Map(AThing("four", 4) -> AThing("three", 3)) - val inst = Map(Map(m1 -> m2) -> Map(m2 -> m1)) - val js = sj.render(inst) - assertEquals( - """{"{\"{\\\"{\\\\\\\"a\\\\\\\":\\\\\\\"one\\\\\\\",\\\\\\\"b\\\\\\\":1}\\\":{\\\"a\\\":\\\"two\\\",\\\"b\\\":2}}\":{\"{\\\"a\\\":\\\"four\\\",\\\"b\\\":4}\":{\"a\":\"three\",\"b\":3}}}":{"{\"{\\\"a\\\":\\\"four\\\",\\\"b\\\":4}\":{\"a\":\"three\",\"b\":3}}":{"{\"a\":\"one\",\"b\":1}":{"a":"two","b":2}}}}""".asInstanceOf[JSON],js) - assertEquals(true, - sj.read[Map[Map[Map[AThing[Int, String], AThing[Int, String]], Map[AThing[Int, String], AThing[Int, String]]], Map[Map[AThing[Int, String], AThing[Int, String]], Map[AThing[Int, String], AThing[Int, String]]]]](js) - .isInstanceOf[Map[Map[Map[AThing[Int, String], AThing[Int, String]], Map[AThing[Int, String], AThing[Int, String]]], Map[Map[AThing[Int, String], AThing[Int, String]], Map[AThing[Int, String], AThing[Int, String]]]]]) - } - - test("Map of parameterized trait as key") { - val m1: Map[Thing[String, Int], Thing[String, Int]] = - Map(AThing("one", 1) -> AThing("two", 2)) - val m2: Map[Thing[String, Int], Thing[String, Int]] = - Map(AThing("four", 4) -> AThing("three", 3)) - val inst = Map(Map(m1 -> m2) -> Map(m2 -> m1)) - val js = sj.render(inst) - assertEquals( - """{"{\"{\\\"{\\\\\\\"_hint\\\\\\\":\\\\\\\"co.blocke.scalajack.json.mapkeys.AThing\\\\\\\",\\\\\\\"a\\\\\\\":\\\\\\\"one\\\\\\\",\\\\\\\"b\\\\\\\":1}\\\":{\\\"_hint\\\":\\\"co.blocke.scalajack.json.mapkeys.AThing\\\",\\\"a\\\":\\\"two\\\",\\\"b\\\":2}}\":{\"{\\\"_hint\\\":\\\"co.blocke.scalajack.json.mapkeys.AThing\\\",\\\"a\\\":\\\"four\\\",\\\"b\\\":4}\":{\"_hint\":\"co.blocke.scalajack.json.mapkeys.AThing\",\"a\":\"three\",\"b\":3}}}":{"{\"{\\\"_hint\\\":\\\"co.blocke.scalajack.json.mapkeys.AThing\\\",\\\"a\\\":\\\"four\\\",\\\"b\\\":4}\":{\"_hint\":\"co.blocke.scalajack.json.mapkeys.AThing\",\"a\":\"three\",\"b\":3}}":{"{\"_hint\":\"co.blocke.scalajack.json.mapkeys.AThing\",\"a\":\"one\",\"b\":1}":{"_hint":"co.blocke.scalajack.json.mapkeys.AThing","a":"two","b":2}}}}""".asInstanceOf[JSON],js) - assertEquals(true, - sj.read[Map[Map[Map[Thing[String, Int], Thing[String, Int]], Map[Thing[String, Int], Thing[String, Int]]], Map[Map[Thing[String, Int], Thing[String, Int]], Map[Thing[String, Int], Thing[String, Int]]]]](js) - .isInstanceOf[Map[Map[Map[Thing[String, Int], Thing[String, Int]], Map[Thing[String, Int], Thing[String, Int]]], Map[Map[Thing[String, Int], Thing[String, Int]], Map[Thing[String, Int], Thing[String, Int]]]]] - ) - } - - test("Map of Optional as key") { - val m1: Map[Option[Int], Option[Int]] = Map(Some(3) -> None) - val m2: Map[Option[Int], Option[Int]] = - Map(None -> Some(2), Some(5) -> null) - val inst = Map(Map(m1 -> m2) -> Map(m2 -> m1)) - val js = sj.render(inst) - assertEquals("""{"{\"{}\":{\"5\":null}}":{"{\"5\":null}":{}}}""".asInstanceOf[JSON],js) - assert( - Map( - Map(Map() -> Map(Some(5) -> None)) -> Map( - Map(Some(5) -> None) -> Map() - ) - ) == sj.read[Map[Map[Map[Option[Int], Option[Int]], Map[Option[Int], Option[Int]]], Map[Map[Option[Int], Option[Int]], Map[Option[Int], Option[Int]]]]](js)) - } - - test("Map of Option as key where Option is null must fail") { - val m1: Map[Option[Int], Option[Int]] = Map(Some(3) -> None) - val m0 = Map.empty[Option[Int], Option[Int]] - val bad: Option[Int] = null - val m2 = m0 + (bad -> Some(99)) - val inst = Map(Map(m1 -> m2) -> Map(m2 -> m1)) - val msg = "Map keys cannot be null." - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.render(inst) - } - } - - test("Map of ValueClass as key") { - val m1: Map[VCChar, VCChar] = Map(VCChar('Z') -> VCChar('z')) - val m2: Map[VCChar, VCChar] = Map(VCChar('A') -> VCChar('a')) - val inst = Map(Map(m1 -> m2) -> Map(m2 -> m1)) - val js = sj.render(inst) - assertEquals( - """{"{\"{\\\"Z\\\":\\\"z\\\"}\":{\"A\":\"a\"}}":{"{\"A\":\"a\"}":{"Z":"z"}}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[Map[Map[Map[VCChar, VCChar], Map[VCChar, VCChar]], Map[Map[VCChar, VCChar], Map[VCChar, VCChar]]]](js)) - } \ No newline at end of file diff --git a/src_old/test/scala/co.blocke.scalajack/json/mapkeys/Model.scala b/src_old/test/scala/co.blocke.scalajack/json/mapkeys/Model.scala deleted file mode 100644 index b470e48b..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json/mapkeys/Model.scala +++ /dev/null @@ -1,176 +0,0 @@ -package co.blocke.scalajack -package json.mapkeys - -import java.util.UUID -import java.lang.{ - Boolean => JBoolean, - Byte => JByte, - Character => JChar, - Double => JDouble, - Float => JFloat, - Integer => JInteger, - Long => JLong, - Number => JNumber, - Short => JShort -} -import java.math.{ BigDecimal => JBigDecimal, BigInteger => JBigInteger } -import java.time._ - -object Size extends Enumeration { - val Small, Medium, Large = Value -} - -trait Thing[A, B] { val a: A; val b: B } -case class AThing[Y, X](a: X, b: Y) extends Thing[X, Y] -trait Part[A] { val p: A } -case class APart[A](p: A) extends Part[A] - -// === Scala Primitive Keys -case class SampleBigDecimal(m: Map[BigDecimal, BigDecimal]) -case class SampleBigInt(m: Map[BigInt, BigInt]) -case class SampleBoolean(m: Map[Boolean, Boolean]) -case class SampleByte(m: Map[Byte, Byte]) -case class SampleChar(m: Map[Char, Char]) -case class SampleDouble(m: Map[Double, Double]) -case class SampleEnumeration(m: Map[Size.Value, Size.Value]) -case class SampleFloat(m: Map[Float, Float]) -case class SampleInt(m: Map[Int, Int]) -case class SampleLong(m: Map[Long, Long]) -case class SampleShort(m: Map[Short, Short]) - -// === Java Primitive Keys -case class SampleJBigDecimal(m: Map[JBigDecimal, JBigDecimal]) -case class SampleJBigInteger(m: Map[JBigInteger, JBigInteger]) -case class SampleJBoolean(m: Map[JBoolean, JBoolean]) -case class SampleJByte(m: Map[JByte, JByte]) -case class SampleJChar(m: Map[JChar, JChar]) -case class SampleJDouble(m: Map[JDouble, JDouble]) -case class SampleJFloat(m: Map[JFloat, JFloat]) -case class SampleJInteger(m: Map[JInteger, JInteger]) -case class SampleJLong(m: Map[JLong, JLong]) -case class SampleJNumber(m: Map[JNumber, JNumber]) -case class SampleJShort(m: Map[JShort, JShort]) - -// === Java Time Keys -case class SampleDuration(m: Map[Duration, Duration]) -case class SampleInstant(m: Map[Instant, Instant]) -case class SampleLocalDateTime(m: Map[LocalDateTime, LocalDateTime]) -case class SampleLocalDate(m: Map[LocalDate, LocalDate]) -case class SampleLocalTime(m: Map[LocalTime, LocalTime]) -case class SampleOffsetDateTime(m: Map[OffsetDateTime, OffsetDateTime]) -case class SampleOffsetTime(m: Map[OffsetTime, OffsetTime]) -case class SamplePeriod(m: Map[Period, Period]) -case class SampleZonedDateTime(m: Map[ZonedDateTime, ZonedDateTime]) - -// === Any primitives -case class AnyShell(m: Map[Any, Any]) - -// === Class Keys -case class SimpleClass(name: String, age: Int, isOk: Boolean, favorite: Any) -case class SampleSimple(m: Map[SimpleClass, SimpleClass]) -case class ComplexClass(id: UUID, simple: SimpleClass, allDone: Boolean) -case class SampleComplex(m: Map[ComplexClass, ComplexClass]) - -object Food extends Enumeration { - val Seeds, Meat, Pellets, Veggies = Value -} -trait Pet { - val name: String - val food: Food.Value -} -case class FishPet(name: String, food: Food.Value, waterTemp: Double) - extends Pet -case class DogPet(name: String, food: Food.Value, numLegs: Int) extends Pet -case class CompoundPet(name: String, food: Food.Value, pet: Pet) extends Pet -trait PetHolder { - val address: String - val pet: Pet -} -case class ShinyPetHolder(address: String, pet: Pet) extends PetHolder -case class SamplePet(m: Map[Pet, Pet]) -case class PolyClass(lookup: Map[String, Int], favs: List[String]) -case class SamplePolyClass(m: Map[PolyClass, PolyClass]) -case class SampleShiny(m: Map[PetHolder, PetHolder]) -case class NCKey(nc: Map[Int, Boolean], name: String) -case class SampleNCKey(m: Map[NCKey, NCKey]) - -// === Collections - Tuple -case class SampleTuple(m: Map[(Int, String, Char), (String, Boolean)]) -case class SampleTupleList( - m: Map[(List[String], List[Int]), (List[String], List[Int])] -) -case class SampleTupleMap( - m: Map[(Map[String, Int], Map[Int, String]), (Map[String, Int], Map[Int, String])] -) -case class SampleTupleTuple( - m: Map[((String, Boolean), (Int, Double)), ((String, Boolean), (Int, Double))] -) -case class SampleTupleClass( - m: Map[(SampleChar, SampleInt), (SampleChar, SampleInt)] -) -case class SampleTupleTrait(m: Map[(Pet, Pet), (Pet, Pet)]) -case class SampleTupleAny(m: Map[(Any, Any), (Any, Any)]) -case class SampleTupleOptional( - m: Map[(Option[Int], Option[String]), (Option[Boolean], Option[Food.Value])] -) -case class SampleTupleVC(m: Map[(VCChar, VCChar), (VCChar, VCChar)]) -case class SampleTupleComplex( - m: Map[(ComplexClass, ComplexClass), (ComplexClass, ComplexClass)] -) -case class SampleTuplePolyClass( - m: Map[(PolyClass, PolyClass), (PolyClass, PolyClass)] -) - -// === Value Classes -case class VCBigDecimal(vc: BigDecimal) extends AnyVal -case class SampleVCBigDecimal(m: Map[VCBigDecimal, VCBigDecimal]) -case class VCBigInt(vc: BigInt) extends AnyVal -case class SampleVCBigInt(m: Map[VCBigInt, VCBigInt]) -case class VCBoolean(vc: Boolean) extends AnyVal -case class SampleVCBoolean(m: Map[VCBoolean, VCBoolean]) -case class VCByte(vc: Byte) extends AnyVal -case class SampleVCByte(m: Map[VCByte, VCByte]) -case class VCChar(vc: Char) extends AnyVal -case class SampleVCChar(m: Map[VCChar, VCChar]) -case class VCDouble(vc: Double) extends AnyVal -case class SampleVCDouble(m: Map[VCDouble, VCDouble]) -case class VCEnumeration(vc: Food.Value) extends AnyVal -case class SampleVCEnumeration(m: Map[VCEnumeration, VCEnumeration]) -case class VCFloat(vc: Float) extends AnyVal -case class SampleVCFloat(m: Map[VCFloat, VCFloat]) -case class VCInt(vc: Int) extends AnyVal -case class SampleVCInt(m: Map[VCInt, VCInt]) -case class VCLong(vc: Long) extends AnyVal -case class SampleVCLong(m: Map[VCLong, VCLong]) -case class VCShort(vc: Short) extends AnyVal -case class SampleVCShort(m: Map[VCShort, VCShort]) -case class VCString(vc: String) extends AnyVal -case class SampleVCString(m: Map[VCString, VCString]) -case class VCUUID(vc: UUID) extends AnyVal -case class SampleVCUUID(m: Map[VCUUID, VCUUID]) -case class VCNumber(vc: Number) extends AnyVal -case class SampleVCNumber(m: Map[VCNumber, VCNumber]) -case class VCList(vc: List[Int]) extends AnyVal -case class SampleVCList(m: Map[VCList, VCList]) -case class VCMap(vc: Map[Int, Int]) extends AnyVal -case class SampleVCMap(m: Map[VCMap, VCMap]) -case class VCTuple(vc: Tuple3[Int, String, Boolean]) extends AnyVal -case class SampleVCTuple(m: Map[VCTuple, VCTuple]) - -case class VCClass(vc: ComplexClass) extends AnyVal -case class SampleVCClass(m: Map[VCClass, VCClass]) -case class VCTrait(vc: Pet) extends AnyVal -case class SampleVCTrait(m: Map[VCTrait, VCTrait]) -case class VCOption(vc: Option[String]) extends AnyVal -case class SampleVCOption(m: Map[VCOption, VCOption]) -case class VCNested(vc: List[Map[String, String]]) extends AnyVal -case class SampleVCNested(m: Map[VCNested, VCNested]) - -case class VCParamClass[A, B](vc: AThing[A, B]) extends AnyVal -case class SampleVCParamClass[A, B]( - m: Map[VCParamClass[A, B], VCParamClass[A, B]] -) -case class VCParamTrait[A, B](vc: Thing[A, B]) extends AnyVal -case class SampleVCParamTrait[A, B]( - m: Map[VCParamTrait[A, B], VCParamTrait[A, B]] -) diff --git a/src_old/test/scala/co.blocke.scalajack/json/mapkeys/ScalaPrimKeys.scala b/src_old/test/scala/co.blocke.scalajack/json/mapkeys/ScalaPrimKeys.scala deleted file mode 100644 index 7b0eeeea..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json/mapkeys/ScalaPrimKeys.scala +++ /dev/null @@ -1,248 +0,0 @@ -package co.blocke.scalajack -package json.mapkeys - -import co.blocke.scala_reflection._ -import TestUtil._ -import munit._ -import munit.internal.console -import co.blocke.scalajack.json.JSON - -class ScalaPrimKeys() extends FunSuite: - - val sj = co.blocke.scalajack.ScalaJack() - - test("With Any Key") { - describe( - "-----------------------------------\n: Scala Primitive Map Key Tests :\n-----------------------------------", Console.BLUE - ) - describe("+++ Positive Tests +++") - - val inst = AnyShell( - Map( - List(1, 2, 3) -> List("a", "b", "c"), - DogPet("Fido", Food.Meat, 4) -> DogPet("Fifi", Food.Meat, 4), - Size.Small -> "ok", - 123.456 -> true, - 293845 -> "Greg", - false -> "16", - "Fred" -> "Wilma", - 16.toByte -> null - ) - ) - val js = sj.render(inst) - assert( json.JsonMatcher.jsonMatches(js, - """{"m":{"false":"16","Small":"ok","123.456":true,"{\"_hint\":\"co.blocke.scalajack.json.mapkeys.DogPet\",\"name\":\"Fido\",\"food\":\"Meat\",\"numLegs\":4}":{"_hint":"co.blocke.scalajack.json.mapkeys.DogPet","name":"Fifi","food":"Meat","numLegs":4},"Fred":"Wilma","[1,2,3]":["a","b","c"],"293845":"Greg","16":null}}""".asInstanceOf[JSON] - ) ) - val read = sj.read[AnyShell](js).m.keySet.map(z => (z, z.getClass.getName)) - assert(read.contains((16, "java.lang.Integer"))) - assert(read.contains((293845, "java.lang.Integer"))) - assert(read.contains((123.456, "java.lang.Double"))) - assert(read.contains(("Small", "java.lang.String"))) - assert(read.contains(("Fred", "java.lang.String"))) - assert(read.contains((false, "java.lang.Boolean"))) - assert(read.contains( - ( - DogPet("Fido", Food.Meat, 4), - "co.blocke.scalajack.json.mapkeys.DogPet" - ) - )) - } - - test("With BigDecimal Key") { - val inst = SampleBigDecimal( - Map( - BigDecimal(123.456) -> BigDecimal(1), - BigDecimal(789.123) -> BigDecimal(2) - ) - ) - val js = sj.render(inst) - assertEquals("""{"m":{"123.456":1,"789.123":2}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SampleBigDecimal](js)) - } - - test("With BigInt Key") { - val inst = - SampleBigInt(Map(BigInt(123) -> BigInt(1), BigInt(789) -> BigInt(2))) - val js = sj.render(inst) - assertEquals("""{"m":{"123":1,"789":2}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SampleBigInt](js)) - } - - test("With Boolean Key") { - val inst = SampleBoolean(Map(true -> false, false -> true)) - val js = sj.render(inst) - assertEquals("""{"m":{"true":false,"false":true}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SampleBoolean](js)) - } - - test("With Byte Key") { - val inst = SampleByte(Map(16.toByte -> 2.toByte, 48.toByte -> 9.toByte)) - val js = sj.render(inst) - assertEquals("""{"m":{"16":2,"48":9}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SampleByte](js)) - } - - test("With Char Key") { - val inst = SampleChar(Map('a' -> 'A', 'z' -> 'Z')) - val js = sj.render(inst) - assertEquals("""{"m":{"a":"A","z":"Z"}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SampleChar](js)) - } - - test("With Double Key") { - val inst = SampleDouble(Map(12.34 -> 56.78, 90.12 -> 34.56)) - val js = sj.render(inst) - assertEquals("""{"m":{"12.34":56.78,"90.12":34.56}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SampleDouble](js)) - } - - test("With Enumeration Key") { - val inst = SampleEnumeration( - Map(Size.Small -> Size.Large, Size.Large -> Size.Medium) - ) - val js = sj.render(inst) - assertEquals("""{"m":{"Small":"Large","Large":"Medium"}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SampleEnumeration](js)) - } - - test("With Float Key") { - val inst = SampleFloat(Map(12.34F -> 56.78F, 90.12F -> 34.56F)) - val js = sj.render(inst) - assertEquals("""{"m":{"12.34":56.78,"90.12":34.56}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SampleFloat](js)) - } - - test("With Int Key") { - val inst = SampleInt(Map(12 -> 56, 90 -> 34)) - val js = sj.render(inst) - assertEquals("""{"m":{"12":56,"90":34}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SampleInt](js)) - } - - test("With Long Key") { - val inst = SampleLong(Map(12L -> 56L, 90L -> 34L)) - val js = sj.render(inst) - assertEquals("""{"m":{"12":56,"90":34}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SampleLong](js)) - } - - test("With Short Key") { - val inst = - SampleShort(Map(12.toShort -> 56.toShort, 90.toShort -> 34.toShort)) - val js = sj.render(inst) - assertEquals("""{"m":{"12":56,"90":34}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SampleShort](js)) - } - - test("Bad BigDecimal Key") { - describe("--- Negative Tests ---") - - val js = """{"m":{"789.123":1,"fred":2}}""".asInstanceOf[JSON] - val msg = """Expected a Number here - |fred - |^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleBigDecimal](js) - } - } - - test("Bad BigInt Key") { - val js = """{"m":{"fred":1,"789":2}}""".asInstanceOf[JSON] - val msg = """Expected a Number here - |fred - |^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleBigInt](js) - } - } - - test("Bad Boolean Key") { - val js = """{"m":{"true":false,"123":true}}""".asInstanceOf[JSON] - val msg = """Expected a Boolean here - |123 - |^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleBoolean](js) - } - } - - test("Bad Byte Key") { - val js = """{"m":{"16":2,"x48":9}}""".asInstanceOf[JSON] - val msg = """Expected a Number here - |x48 - |^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleByte](js) - } - } - - test("Bad Char Key") { // NOTE: This comprehensively tests for any null keyed Map - val js = """{"m":{null:"A","z":"Z"}}""".asInstanceOf[JSON] - val msg = """A Char typed value cannot be null - |{"m":{null:"A","z":"Z"}} - |---------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleChar](js) - } - } - - test("Bad Double Key") { - val js = """{"m":{"12.34":56.78,"true":34.56}}""".asInstanceOf[JSON] - val msg = """Expected a Number here - |true - |^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleDouble](js) - } - } - - test("Bad Enumeration Key") { - val js = """{"m":{"Small":"Large","Bogus":"Medium"}}""".asInstanceOf[JSON] - val msg = - """No value found in enumeration co.blocke.scalajack.json.mapkeys.Size$ for Bogus - |{"m":{"Small":"Large","Bogus":"Medium"}} - |----------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleEnumeration](js) - } - } - - test("Bad Float Key") { - val js = """{"m":{"12.34":56.78,"90.12.3":34.56}}""".asInstanceOf[JSON] - val msg = """Cannot parse an Float from value - |90.12.3 - |------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleFloat](js) - } - } - - test("Bad Int Key") { - val js = """{"m":{"12.0":56,"90":34}}""".asInstanceOf[JSON] - val msg = """Cannot parse an Int from value - |12.0 - |---^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleInt](js) - } - } - - test("Bad Long Key") { - val js = """{"m":{"12":56,"hey":34}}""".asInstanceOf[JSON] - val msg = """Expected a Number here - |hey - |^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleLong](js) - } - } - - test("Bad Short Key") { - val js = """{"m":{"p99999":56,"90":34}}""".asInstanceOf[JSON] - val msg = """Expected a Number here - |p99999 - |^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleShort](js) - } - } diff --git a/src_old/test/scala/co.blocke.scalajack/json/mapkeys/TupleCollKeys.scala b/src_old/test/scala/co.blocke.scalajack/json/mapkeys/TupleCollKeys.scala deleted file mode 100644 index f3205beb..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json/mapkeys/TupleCollKeys.scala +++ /dev/null @@ -1,236 +0,0 @@ -package co.blocke.scalajack -package json.mapkeys - -import co.blocke.scala_reflection._ -import TestUtil._ -import munit._ -import munit.internal.console -import co.blocke.scalajack.json.JSON -import java.util.UUID -import co.blocke.scalajack.model._ - -class TupleCollKeys() extends FunSuite: - - val sj = co.blocke.scalajack.ScalaJack() - - test("Tuple as key") { - describe( - "-------------------------\n: Tuple Map Key Tests :\n-------------------------", Console.BLUE - ) - - val a = (2, "Blather", 'Q') - val b = ("Foo", true) - val inst = SampleTuple(Map(a -> b)) - val js = sj.render(inst) - assertEquals("""{"m":{"[2,\"Blather\",\"Q\"]":["Foo",true]}}""".asInstanceOf[JSON],js) - assertEquals(inst,sj.read[SampleTuple](js)) - } - - test("Tuple as key, null tuple as value") { - val a = (2, "Blather", 'Q') - val b: (String, Boolean) = null - val inst = SampleTuple(Map(a -> b)) - val js = sj.render(inst) - assertEquals("""{"m":{"[2,\"Blather\",\"Q\"]":null}}""".asInstanceOf[JSON],js) - assertEquals(inst,sj.read[SampleTuple](js)) - } - - test("Tuple of Lists as key") { - val a = (List("one", "two", "three"), List(1, 2, 3)) - val b = (List("four", "five", "six"), List(4, 5, 6)) - val inst = SampleTupleList(Map(a -> b)) - val js = sj.render(inst) - assertEquals( - """{"m":{"[[\"one\",\"two\",\"three\"],[1,2,3]]":[["four","five","six"],[4,5,6]]}}""".asInstanceOf[JSON],js) - assertEquals(inst,sj.read[SampleTupleList](js)) - } - - test("Tuple of Maps as key") { - val a = (Map("one" -> 1), Map(2 -> "two")) - val b = (Map("three" -> 3), Map(4 -> "four")) - val inst = SampleTupleMap(Map(a -> b)) - val js = sj.render(inst) - assertEquals( - """{"m":{"[{\"one\":1},{\"2\":\"two\"}]":[{"three":3},{"4":"four"}]}}""".asInstanceOf[JSON],js) - assertEquals(inst,sj.read[SampleTupleMap](js)) - } - - test("Tuple of Tuples as key") { - val a = (("yes", true), (1, 0.25)) - val b = (("no", false), (2, 0.5)) - val inst = SampleTupleTuple(Map(a -> b)) - val js = sj.render(inst) - assertEquals( - """{"m":{"[[\"yes\",true],[1,0.25]]":[["no",false],[2,0.5]]}}""".asInstanceOf[JSON],js) - assertEquals(inst,sj.read[SampleTupleTuple](js)) - } - - test("Tuple of Case Class as key") { - val a = (SampleChar(Map('a' -> 'A')), SampleInt(Map(1 -> 2))) - val b = (SampleChar(Map('z' -> 'Z')), SampleInt(Map(99 -> 100))) - val inst = SampleTupleClass(Map(a -> b)) - val js = sj.render(inst) - assertEquals( - """{"m":{"[{\"m\":{\"a\":\"A\"}},{\"m\":{\"1\":2}}]":[{"m":{"z":"Z"}},{"m":{"99":100}}]}}""".asInstanceOf[JSON],js) - assertEquals(inst,sj.read[SampleTupleClass](js)) - } - - test("Tuple of Trait as key") { - val a = (DogPet("Fido", Food.Meat, 4), FishPet("Jaws", Food.Meat, 69.8)) - val b = - (DogPet("Chey", Food.Meat, 3), FishPet("Flipper", Food.Seeds, 80.1)) - val inst = SampleTupleTrait(Map(a -> b)) - val js = sj.render(inst) - assertEquals( - """{"m":{"[{\"_hint\":\"co.blocke.scalajack.json.mapkeys.DogPet\",\"name\":\"Fido\",\"food\":\"Meat\",\"numLegs\":4},{\"_hint\":\"co.blocke.scalajack.json.mapkeys.FishPet\",\"name\":\"Jaws\",\"food\":\"Meat\",\"waterTemp\":69.8}]":[{"_hint":"co.blocke.scalajack.json.mapkeys.DogPet","name":"Chey","food":"Meat","numLegs":3},{"_hint":"co.blocke.scalajack.json.mapkeys.FishPet","name":"Flipper","food":"Seeds","waterTemp":80.1}]}}""".asInstanceOf[JSON],js) - assertEquals(inst,sj.read[SampleTupleTrait](js)) - } - - test("Tuple of Any as key") { - val a = (DogPet("Fido", Food.Meat, 4), FishPet("Jaws", Food.Meat, 69.8)) - val b = - (DogPet("Chey", Food.Meat, 3), FishPet("Flipper", Food.Seeds, 80.1)) - val inst = SampleTupleTrait(Map(a -> b)) - val js = sj.render(inst) - assertEquals( - """{"m":{"[{\"_hint\":\"co.blocke.scalajack.json.mapkeys.DogPet\",\"name\":\"Fido\",\"food\":\"Meat\",\"numLegs\":4},{\"_hint\":\"co.blocke.scalajack.json.mapkeys.FishPet\",\"name\":\"Jaws\",\"food\":\"Meat\",\"waterTemp\":69.8}]":[{"_hint":"co.blocke.scalajack.json.mapkeys.DogPet","name":"Chey","food":"Meat","numLegs":3},{"_hint":"co.blocke.scalajack.json.mapkeys.FishPet","name":"Flipper","food":"Seeds","waterTemp":80.1}]}}""".asInstanceOf[JSON],js) - assertEquals(inst,sj.read[SampleTupleTrait](js)) - } - - test("Tuple of Optional as key") { - val a = (Some(5), Some("Fred")) - val b = (None, Some(Food.Meat)) - val inst = SampleTupleOptional(Map(a -> b)) - val js = sj.render(inst) - assertEquals("""{"m":{"[5,\"Fred\"]":[null,"Meat"]}}""".asInstanceOf[JSON],js) - assertEquals(inst,sj.read[SampleTupleOptional](js)) - } - - test("Tuple of ValueClass as key") { - val a = (VCChar('a'), VCChar('A')) - val b = (VCChar('z'), VCChar('Z')) - val inst = SampleTupleVC(Map(a -> b)) - val js = sj.render(inst) - assertEquals("""{"m":{"[\"a\",\"A\"]":["z","Z"]}}""".asInstanceOf[JSON],js) - assertEquals(inst,sj.read[SampleTupleVC](js)) - } - - test("Complex class (having members that are classes) as key") { - val a = SimpleClass("Larry", 32, isOk = true, "golf") - val b = SimpleClass("Mike", 27, isOk = false, 125) - val c1 = ComplexClass( - UUID.fromString("1e6c2b31-4dfe-4bf6-a0a0-882caaff0e9c"), - a, - allDone = true - ) - val c2 = ComplexClass( - UUID.fromString("1e6c2b31-4dfe-4bf6-a0a0-882caaff0e9d"), - b, - allDone = false - ) - val t1 = (c1, c2) - val t2 = (c2, c1) - val inst = SampleTupleComplex(Map(t1 -> t2)) - val js = sj.render(inst) - assertEquals( - """{"m":{"[{\"id\":\"1e6c2b31-4dfe-4bf6-a0a0-882caaff0e9c\",\"simple\":{\"name\":\"Larry\",\"age\":32,\"isOk\":true,\"favorite\":\"golf\"},\"allDone\":true},{\"id\":\"1e6c2b31-4dfe-4bf6-a0a0-882caaff0e9d\",\"simple\":{\"name\":\"Mike\",\"age\":27,\"isOk\":false,\"favorite\":125},\"allDone\":false}]":[{"id":"1e6c2b31-4dfe-4bf6-a0a0-882caaff0e9d","simple":{"name":"Mike","age":27,"isOk":false,"favorite":125},"allDone":false},{"id":"1e6c2b31-4dfe-4bf6-a0a0-882caaff0e9c","simple":{"name":"Larry","age":32,"isOk":true,"favorite":"golf"},"allDone":true}]}}""".asInstanceOf[JSON],js) - assertEquals(inst,sj.read[SampleTupleComplex](js)) - } - - test("Class having collections as members") { - val a = PolyClass(Map("a" -> 1, "b" -> 2), List("one", "two")) - val b = PolyClass(Map("x" -> 9, "y" -> 10), List("aye", "you")) - val t1 = (a, b) - val t2 = (b, a) - val inst = SampleTuplePolyClass(Map(t1 -> t2)) - val js = sj.render(inst) - assertEquals( - """{"m":{"[{\"lookup\":{\"a\":1,\"b\":2},\"favs\":[\"one\",\"two\"]},{\"lookup\":{\"x\":9,\"y\":10},\"favs\":[\"aye\",\"you\"]}]":[{"lookup":{"x":9,"y":10},"favs":["aye","you"]},{"lookup":{"a":1,"b":2},"favs":["one","two"]}]}}""".asInstanceOf[JSON],js) - assertEquals(inst,sj.read[SampleTuplePolyClass](js)) - } - - test("Class having collections as members (empty collections") { - val a = PolyClass(Map.empty[String, Int], List.empty[String]) - val b = PolyClass(Map.empty[String, Int], List.empty[String]) - val t1 = (a, b) - val t2 = (b, a) - val inst = Map(t1 -> t2) - val js = sj.render(inst) - assertEquals( - """{"[{\"lookup\":{},\"favs\":[]},{\"lookup\":{},\"favs\":[]}]":[{"lookup":{},"favs":[]},{"lookup":{},"favs":[]}]}""".asInstanceOf[JSON],js) - assertEquals(inst,sj.read[Map[(PolyClass, PolyClass), (PolyClass, PolyClass)]](js)) - } - - test("Custom trait hint field and value for key trait") { - val petHintMod = StringMatchHintModifier( - Map("BreathsWater" -> "co.blocke.scalajack.json.mapkeys.FishPet", "BreathsAir" -> "co.blocke.scalajack.json.mapkeys.DogPet") - ) - val sj2 = co.blocke.scalajack.ScalaJack() - .withHints(RType.of[Pet] -> "kind") - .withHintModifiers(RType.of[Pet] -> petHintMod) - val a: Pet = FishPet("Flipper", Food.Veggies, 74.33) - val b: Pet = DogPet("Fido", Food.Meat, 3) - val t1 = (a, b) - val t2 = (b, a) - val inst = Map(t1 -> t2) - val js = sj2.render(inst) - assertEquals( - """{"[{\"kind\":\"BreathsWater\",\"name\":\"Flipper\",\"food\":\"Veggies\",\"waterTemp\":74.33},{\"kind\":\"BreathsAir\",\"name\":\"Fido\",\"food\":\"Meat\",\"numLegs\":3}]":[{"kind":"BreathsAir","name":"Fido","food":"Meat","numLegs":3},{"kind":"BreathsWater","name":"Flipper","food":"Veggies","waterTemp":74.33}]}""".asInstanceOf[JSON],js) - assertEquals(inst,sj2.read[Map[(Pet, Pet), (Pet, Pet)]](js)) - } - - test("Custom trait hint field and value for key member's trait") { - val petHintMod = StringMatchHintModifier( - Map("BreathsWater" -> "co.blocke.scalajack.json.mapkeys.FishPet", "BreathsAir" -> "co.blocke.scalajack.json.mapkeys.DogPet") - ) - val sj2 = co.blocke.scalajack.ScalaJack() - .withHints(RType.of[Pet] -> "kind") - .withHintModifiers(RType.of[Pet] -> petHintMod) - - val a: PetHolder = - ShinyPetHolder("123 Main", FishPet("Flipper", Food.Veggies, 74.33)) - val b: PetHolder = - ShinyPetHolder("210 North", DogPet("Fido", Food.Meat, 3)) - val t1 = (a, b) - val t2 = (b, a) - val inst = Map(t1 -> t2) - val js = sj2.render(inst) - assertEquals( - """{"[{\"_hint\":\"co.blocke.scalajack.json.mapkeys.ShinyPetHolder\",\"address\":\"123 Main\",\"pet\":{\"kind\":\"BreathsWater\",\"name\":\"Flipper\",\"food\":\"Veggies\",\"waterTemp\":74.33}},{\"_hint\":\"co.blocke.scalajack.json.mapkeys.ShinyPetHolder\",\"address\":\"210 North\",\"pet\":{\"kind\":\"BreathsAir\",\"name\":\"Fido\",\"food\":\"Meat\",\"numLegs\":3}}]":[{"_hint":"co.blocke.scalajack.json.mapkeys.ShinyPetHolder","address":"210 North","pet":{"kind":"BreathsAir","name":"Fido","food":"Meat","numLegs":3}},{"_hint":"co.blocke.scalajack.json.mapkeys.ShinyPetHolder","address":"123 Main","pet":{"kind":"BreathsWater","name":"Flipper","food":"Veggies","waterTemp":74.33}}]}""".asInstanceOf[JSON],js) - assertEquals(inst,sj2.read[Map[(PetHolder, PetHolder), (PetHolder, PetHolder)]](js)) - } - - test("Parameterized class") { - val t1 = (AThing("wow", 4), AThing("boom", 1)) - val t2 = (AThing("yep", 3), AThing("yikes", 11)) - val inst = Map(t1 -> t2) - val js = sj.render(inst) - assertEquals( - """{"[{\"a\":\"wow\",\"b\":4},{\"a\":\"boom\",\"b\":1}]":[{"a":"yep","b":3},{"a":"yikes","b":11}]}""".asInstanceOf[JSON],js) - assertEquals(inst,sj.read[Map[(AThing[Int, String], AThing[Int, String]), (AThing[Int, String], AThing[Int, String])]](js)) - } - - test("Parameterized trait") { - type T1 = Thing[String,Int] - val t1: (Thing[String,Int], Thing[String,Int]) = - (AThing("wow", 4), AThing("boom", 1)) - val t2: (Thing[String,Int], Thing[String,Int]) = - (AThing("yep", 3), AThing("yikes", 11)) - val inst = Map(t1 -> t2) - val js = sj.render(inst) - assertEquals( - """{"[{\"_hint\":\"co.blocke.scalajack.json.mapkeys.AThing\",\"a\":\"wow\",\"b\":4},{\"_hint\":\"co.blocke.scalajack.json.mapkeys.AThing\",\"a\":\"boom\",\"b\":1}]":[{"_hint":"co.blocke.scalajack.json.mapkeys.AThing","a":"yep","b":3},{"_hint":"co.blocke.scalajack.json.mapkeys.AThing","a":"yikes","b":11}]}""".asInstanceOf[JSON],js) - assert(inst == sj.read[Map[(Thing[String,Int],Thing[String,Int]),(Thing[String,Int],Thing[String,Int])]](js)) - } - - test("Parameterized trait having parameterized trait members") { - val t1: (Thing[String, Part[Double]], Thing[String, Part[Double]]) = - (AThing("wow", APart(1.2)), AThing("boom", APart(2.3))) - val t2: (Thing[String, Part[Double]], Thing[String, Part[Double]]) = - (AThing("yep", APart(4.5)), AThing("yikes", APart(6.7))) - val inst = Map(t1 -> t2) - val js = sj.render(inst) - assertEquals( - """{"[{\"_hint\":\"co.blocke.scalajack.json.mapkeys.AThing\",\"a\":\"wow\",\"b\":{\"_hint\":\"co.blocke.scalajack.json.mapkeys.APart\",\"p\":1.2}},{\"_hint\":\"co.blocke.scalajack.json.mapkeys.AThing\",\"a\":\"boom\",\"b\":{\"_hint\":\"co.blocke.scalajack.json.mapkeys.APart\",\"p\":2.3}}]":[{"_hint":"co.blocke.scalajack.json.mapkeys.AThing","a":"yep","b":{"_hint":"co.blocke.scalajack.json.mapkeys.APart","p":4.5}},{"_hint":"co.blocke.scalajack.json.mapkeys.AThing","a":"yikes","b":{"_hint":"co.blocke.scalajack.json.mapkeys.APart","p":6.7}}]}""".asInstanceOf[JSON],js) - assert(inst == sj.read[Map[(Thing[String, Part[Double]], Thing[String, Part[Double]]), (Thing[String, Part[Double]], Thing[String, Part[Double]])]](js)) - } \ No newline at end of file diff --git a/src_old/test/scala/co.blocke.scalajack/json/mapkeys/ValueClassKeys.scala b/src_old/test/scala/co.blocke.scalajack/json/mapkeys/ValueClassKeys.scala deleted file mode 100644 index c021b807..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json/mapkeys/ValueClassKeys.scala +++ /dev/null @@ -1,454 +0,0 @@ -package co.blocke.scalajack -package json.mapkeys - -import co.blocke.scala_reflection._ -import TestUtil._ -import munit._ -import munit.internal.console -import co.blocke.scalajack.json.JSON -import java.util.UUID -import co.blocke.scalajack.model._ - -class ValueClassKeys() extends FunSuite: - - val sj = co.blocke.scalajack.ScalaJack() - - test("Value class of BigDecimal") { - describe( - "------------------------------\n: ValueClass Map Key Tests :\n------------------------------", Console.BLUE - ) - describe("+++ Positive Primitive Tests +++") - - val inst = SampleVCBigDecimal( - Map( - VCBigDecimal(BigDecimal(12.34)) -> VCBigDecimal(BigDecimal(56.78)) - ) - ) - val js = sj.render(inst) - assertEquals("""{"m":{"12.34":56.78}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SampleVCBigDecimal](js)) - } - - test("Value class of BigInt") { - val inst = - SampleVCBigInt(Map(VCBigInt(BigInt(12)) -> VCBigInt(BigInt(56)))) - val js = sj.render(inst) - assertEquals("""{"m":{"12":56}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SampleVCBigInt](js)) - } - - test("Value class of Byte") { - val inst = SampleVCByte( - Map(VCByte(12.asInstanceOf[Byte]) -> VCByte(56.asInstanceOf[Byte])) - ) - val js = sj.render(inst) - assertEquals("""{"m":{"12":56}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SampleVCByte](js)) - } - - test("Value class of Boolean") { - val inst = SampleVCBoolean(Map(VCBoolean(true) -> VCBoolean(false))) - val js = sj.render(inst) - assertEquals("""{"m":{"true":false}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SampleVCBoolean](js)) - } - - test("Value class of Char") { - val inst = SampleVCChar(Map(VCChar('a') -> VCChar('A'))) - val js = sj.render(inst) - assertEquals("""{"m":{"a":"A"}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SampleVCChar](js)) - } - - test("Value class of Double") { - val inst = SampleVCDouble(Map(VCDouble(12.34) -> VCDouble(56.78))) - val js = sj.render(inst) - assertEquals("""{"m":{"12.34":56.78}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SampleVCDouble](js)) - } - - test("Value class of Enumeration") { - val inst = SampleVCEnumeration( - Map(VCEnumeration(Food.Veggies) -> VCEnumeration(Food.Meat)) - ) - val js = sj.render(inst) - assertEquals("""{"m":{"Veggies":"Meat"}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SampleVCEnumeration](js)) - } - - test("Value class of Float") { - val inst = SampleVCFloat(Map(VCFloat(12.34F) -> VCFloat(56.2F))) - val js = sj.render(inst) - assertEquals("""{"m":{"12.34":56.2}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SampleVCFloat](js)) - } - - test("Value class of Int") { - val inst = SampleVCInt(Map(VCInt(12) -> VCInt(56))) - val js = sj.render(inst) - assertEquals("""{"m":{"12":56}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SampleVCInt](js)) - } - - test("Value class of Long") { - val inst = SampleVCLong(Map(VCLong(12L) -> VCLong(56L))) - val js = sj.render(inst) - assertEquals("""{"m":{"12":56}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SampleVCLong](js)) - } - - test("Value class of Short") { - val inst = SampleVCShort( - Map( - VCShort(12.asInstanceOf[Short]) -> VCShort(56.asInstanceOf[Short]) - ) - ) - val js = sj.render(inst) - assertEquals("""{"m":{"12":56}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SampleVCShort](js)) - } - - test("Value class of String") { - val inst = SampleVCString(Map(VCString("A") -> VCString("B"))) - val js = sj.render(inst) - assertEquals("""{"m":{"A":"B"}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SampleVCString](js)) - } - - test("Value class of UUID") { - val inst = SampleVCUUID( - Map( - VCUUID(UUID.fromString("54cab778-7b9e-4b07-9d37-87b97a011e56")) -> VCUUID( - UUID.fromString("54cab778-7b9e-4b07-9d37-87b97a011e55") - ) - ) - ) - val js = sj.render(inst) - assertEquals( - """{"m":{"54cab778-7b9e-4b07-9d37-87b97a011e56":"54cab778-7b9e-4b07-9d37-87b97a011e55"}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SampleVCUUID](js)) - } - - test("Value class of List") { - describe("+++ Positive Collection Tests +++") - - val inst = - SampleVCList(Map(VCList(List(1, 2, 3)) -> VCList(List(4, 5, 6)))) - val js = sj.render(inst) - assertEquals("""{"m":{"[1,2,3]":[4,5,6]}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SampleVCList](js)) - } - - test("Value class of List (empty List)") { - val inst = - SampleVCList(Map(VCList(List.empty[Int]) -> VCList(List.empty[Int]))) - val js = sj.render(inst) - assertEquals("""{"m":{"[]":[]}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SampleVCList](js)) - } - - test("Value class of Map") { - val inst = SampleVCMap(Map(VCMap(Map(1 -> 2)) -> VCMap(Map(3 -> 4)))) - val js = sj.render(inst) - assertEquals("""{"m":{"{\"1\":2}":{"3":4}}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SampleVCMap](js)) - } - - test("Value class of Map (empty Map") { - val inst = SampleVCMap( - Map(VCMap(Map.empty[Int, Int]) -> VCMap(Map.empty[Int, Int])) - ) - val js = sj.render(inst) - assertEquals("""{"m":{"{}":{}}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SampleVCMap](js)) - } - - test("Value class of Tupple") { - val inst = SampleVCTuple( - Map(VCTuple((1, "one", true)) -> VCTuple((2, "two", false))) - ) - val js = sj.render(inst) - assertEquals("""{"m":{"[1,\"one\",true]":[2,"two",false]}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SampleVCTuple](js)) - } - - test("Value class of Case Class") { - describe("+++ Positive Complex Tests +++") - - val a = SimpleClass("Larry", 32, isOk = true, "golf") - val b = SimpleClass("Mike", 27, isOk = false, 125) - val c1 = ComplexClass( - UUID.fromString("1e6c2b31-4dfe-4bf6-a0a0-882caaff0e9c"), - a, - allDone = true - ) - val c2 = ComplexClass( - UUID.fromString("1e6c2b31-4dfe-4bf6-a0a0-882caaff0e9d"), - b, - allDone = false - ) - val inst = SampleVCClass(Map(VCClass(c1) -> VCClass(c2))) - val js = sj.render(inst) - assertEquals( - """{"m":{"{\"id\":\"1e6c2b31-4dfe-4bf6-a0a0-882caaff0e9c\",\"simple\":{\"name\":\"Larry\",\"age\":32,\"isOk\":true,\"favorite\":\"golf\"},\"allDone\":true}":{"id":"1e6c2b31-4dfe-4bf6-a0a0-882caaff0e9d","simple":{"name":"Mike","age":27,"isOk":false,"favorite":125},"allDone":false}}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SampleVCClass](js)) - } - - test("Value class of Trait") { - val a: Pet = FishPet("Flipper", Food.Veggies, 74.33) - val b: Pet = DogPet("Fido", Food.Meat, 3) - val inst = SampleVCTrait(Map(VCTrait(a) -> VCTrait(b))) - val js = sj.render(inst) - assertEquals( - """{"m":{"{\"_hint\":\"co.blocke.scalajack.json.mapkeys.FishPet\",\"name\":\"Flipper\",\"food\":\"Veggies\",\"waterTemp\":74.33}":{"_hint":"co.blocke.scalajack.json.mapkeys.DogPet","name":"Fido","food":"Meat","numLegs":3}}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SampleVCTrait](js)) - } - - test("Value class of Parameterized Case Class") { - val a = AThing(5, "wow") - val b = AThing(6, "zoom") - val inst = SampleVCParamClass(Map(VCParamClass(a) -> VCParamClass(b))) - val js = sj.render(inst) - assertEquals("""{"m":{"{\"a\":5,\"b\":\"wow\"}":{"a":6,"b":"zoom"}}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SampleVCParamClass[String, Int]](js)) - } - - test("Value class of Parameterized Trait") { - val a: Thing[Int, String] = AThing(5, "wow") - val b: Thing[Int, String] = AThing(6, "zoom") - val inst = SampleVCParamTrait(Map(VCParamTrait(a) -> VCParamTrait(b))) - val js = sj.render(inst) - assertEquals( - """{"m":{"{\"_hint\":\"co.blocke.scalajack.json.mapkeys.AThing\",\"a\":5,\"b\":\"wow\"}":{"_hint":"co.blocke.scalajack.json.mapkeys.AThing","a":6,"b":"zoom"}}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SampleVCParamTrait[Int, String]](js)) - } - - test("Value class of Option") { - val inst = - SampleVCOption(Map(VCOption(Some("here")) -> VCOption(Some("there")))) - val js = sj.render(inst) - assertEquals("""{"m":{"here":"there"}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SampleVCOption](js)) - } - - test("Value class of nested collection") { - val a = VCNested(List(Map("a" -> "b"), Map("c" -> "d"))) - val b = VCNested(List(Map("t" -> "u"), Map("x" -> "y"))) - val inst = SampleVCNested(Map(a -> b)) - val js = sj.render(inst) - assertEquals( - """{"m":{"[{\"a\":\"b\"},{\"c\":\"d\"}]":[{"t":"u"},{"x":"y"}]}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[SampleVCNested](js)) - } - - test("Bad BigDecimal Key") { - describe("--- Negative Primitive Tests ---") - - val js = """{"m":{"true":56.78}}""".asInstanceOf[JSON] - val msg = """Expected a Number here - |true - |^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleVCBigDecimal](js) - } - } - - test("Bad BigInt Key") { - val js = """{"m":{"12.5":56}}""".asInstanceOf[JSON] - interceptMessage[java.lang.NumberFormatException]("For input string: \"12.5\""){ - sj.read[SampleVCBigInt](js) - } - } - - test("Bad Boolean Key") { - val js = """{"m":{"1":false}}""".asInstanceOf[JSON] - val msg = """Expected a Boolean here - |1 - |^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleVCBoolean](js) - } - } - - test("Bad Char Key") { - val js = """{"m":{null:"A"}}""".asInstanceOf[JSON] - val msg = """A Char typed value cannot be null - |{"m":{null:"A"}} - |---------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleVCChar](js) - } - } - - test("Bad Double Key") { - val js = """{"m":{"false":56.78}}""".asInstanceOf[JSON] - val msg = """Expected a Number here - |false - |^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleVCDouble](js) - } - } - - test("Bad Enumeration Key") { - val js = """{"m":{"Bogus":"Meat"}}""".asInstanceOf[JSON] - val msg = - """No value found in enumeration co.blocke.scalajack.json.mapkeys.Food$ for Bogus - |{"m":{"Bogus":"Meat"}} - |------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleVCEnumeration](js) - } - } - - test("Bad Float Key") { - val js = """{"m":{"hey":56.2}}""".asInstanceOf[JSON] - val msg = """Expected a Number here - |hey - |^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleVCFloat](js) - } - } - - test("Bad Int Key") { - val js = """{"m":{"12.5":56}}""".asInstanceOf[JSON] - val msg = """Cannot parse an Int from value - |12.5 - |---^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleVCInt](js) - } - } - - test("Bad Long Key") { - val js = """{"m":{"12.5":56}}""".asInstanceOf[JSON] - val msg = """Cannot parse an Long from value - |12.5 - |---^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleVCLong](js) - } - } - - test("Bad Short Key") { - val js = """{"m":{"12.5":56}}""".asInstanceOf[JSON] - val msg = """Cannot parse an Short from value - |12.5 - |---^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleVCShort](js) - } - } - - test("Bad String Key") { - val js = """{"m":{true:"B"}}""".asInstanceOf[JSON] - val msg = """Expected a String here - |{"m":{true:"B"}} - |------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleVCString](js) - } - } - - test("Bad UUID Key") { - val js = """{"m":{"bogus":"54cab778-7b9e-4b07-9d37-87b97a011e55"}}""".asInstanceOf[JSON] - val msg = """Failed to create UUID value from parsed text bogus - |{"m":{"bogus":"54cab778-7b9e-4b07-9d37-87b97a011e55"}} - |------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleVCUUID](js) - } - } - - test("Bad List Key") { - describe("--- Negative Collection Tests ---") - - val js = """{"m":{"[1,2,\"a\"]":[4,5,6]}}""".asInstanceOf[JSON] - val msg = """Expected a Number here - |[1,2,"a"] - |-----^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleVCList](js) - } - } - - test("Bad Map Key") { - val js = """{"m":{"{[true]:2}":{"3":4}}}""".asInstanceOf[JSON] - val msg = """Expected a String here - |{[true]:2} - |-^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleVCMap](js) - } - } - - test("Bad Tuple Key") { - val js = """{"m":{"[1,\"one\",true,1]":[2,"two",false]}}""".asInstanceOf[JSON] - val msg = """Expected end of tuple here - |[1,"one",true,1] - |-------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleVCTuple](js) - } - } - - test("Bad Case Class Key") { - describe("--- Negative Complex Tests ---") - - val js = - """{"m":{"{\"id\":\"1e6c2b31-4dfe-4bf6-a0a0-882caaff0e9c\",\"simple\":{\"bogus\":\"Larry\",\"age\":32,\"isOk\":true,\"favorite\":\"golf\"},\"allDone\":true}":{"id":"1e6c2b31-4dfe-4bf6-a0a0-882caaff0e9d","simple":{"name":"Mike","age":27,"isOk":false,"favorite":125},"allDone":false}}}""".asInstanceOf[JSON] - val msg = - """Class co.blocke.scalajack.json.mapkeys.SimpleClass missing required fields: name - |...s":"Larry","age":32,"isOk":true,"favorite":"golf"},"allDone":true} - |----------------------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleVCClass](js) - } - } - - test("Bad Trait Key") { - val js = - """{"m":{"{\"_hint\":\"co.blocke.scalajack.json.mapkeys.Bogus\",\"name\":\"Flipper\",\"food\":\"Veggies\",\"waterTemp\":74.33}":{"_hint":"co.blocke.scalajack.json.mapkeys.DogPet","name":"Fido","food":"Meat","numLegs":3}}}""".asInstanceOf[JSON] - interceptMessage[java.lang.ClassNotFoundException]("co.blocke.scalajack.json.mapkeys.Bogus"){ - sj.read[SampleVCTrait](js) - } - } - - test("Bad Parameterized Case Class Key") { - val js = """{"m":{"{\"a\":5.5,\"b\":\"wow\"}":{"a":6,"b":"zoom"}}}""".asInstanceOf[JSON] - val msg = """Cannot parse an Int from value - |{"a":5.5,"b":"wow"} - |-------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleVCParamClass[String, Int]](js) - } - } - - test("Bad Parameterized Trait Key") { - val js = - """{"m":{"{\"_hint\":\"co.blocke.scalajack.json.mapkeys.ZThing\",\"a\":5,\"b\":\"wow\"}":{"_hint":"co.blocke.scalajack.mapkeys.AThing","a":6,"b":"zoom"}}}""".asInstanceOf[JSON] - interceptMessage[java.lang.ClassNotFoundException]("co.blocke.scalajack.json.mapkeys.ZThing"){ - sj.read[SampleVCParamTrait[Int, String]](js) - } - } - - test("Bad Option Key") { - val js = """{"m":{true:"there"}}""".asInstanceOf[JSON] - val msg = """Expected a String here - |{"m":{true:"there"}} - |------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleVCOption](js) - } - } - - test("Bad Nested Collection Key") { - val js = """{"m":{"[{\"a\":\"b\"},{\"c\":9}]":[{"t":"u"},{"x":"y"}]}}""".asInstanceOf[JSON] - val msg = """Expected a String here - |[{"a":"b"},{"c":9}] - |----------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleVCNested](js) - } - } \ No newline at end of file diff --git a/src_old/test/scala/co.blocke.scalajack/json/parameters/ClassParams.scala b/src_old/test/scala/co.blocke.scalajack/json/parameters/ClassParams.scala deleted file mode 100644 index a23d4b69..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json/parameters/ClassParams.scala +++ /dev/null @@ -1,103 +0,0 @@ -package co.blocke.scalajack -package json.parameters - -import TestUtil._ -import munit._ -import munit.internal.console -import co.blocke.scalajack.json.JSON -import co.blocke.scala_reflection._ - -class ClassParams() extends FunSuite: - - val sj = co.blocke.scalajack.ScalaJack() - - test("Simple parameters - Foo[A](x:A) where A -> simple type") { - describe("---------------------------------\n: Class Paramterization Tests :\n---------------------------------", Console.BLUE) - describe("Basic Parameterized Case Class") - - val inst = Foo1(false, 19) - val js = sj.render(inst) - assertEquals("""{"x":false,"b":19}""".asInstanceOf[JSON],js) - assertEquals(inst,sj.read[Foo1[Boolean]](js)) - } - - test("Non-parameter case class as a parameter - Foo[A](x:A) where A -> Bar (case clas)") { - val inst = Foo1(Bar1("Fred"), 19) - val js = sj.render(inst) - assertEquals("""{"x":{"name":"Fred"},"b":19}""".asInstanceOf[JSON],js) - assertEquals(inst,sj.read[Foo1[Bar1]](js)) - } - - test("Parameterized case class as parameter - Foo[A](x:A) where A -> Bar[Int]") { - describe("Advanced Parameterized Case Class") - - val inst = Foo1(Bar2(123L), 19) - val js = sj.render(inst) - assertEquals("""{"x":{"id":123},"b":19}""".asInstanceOf[JSON],js) - assertEquals(inst,sj.read[Foo1[Bar2[Long]]](js)) - } - - test("Value class as parameter - Foo[A](x:A) where A -> value class") { - val inst = Foo1(VC1("Wow"), 19) - val js = sj.render(inst) - assertEquals("""{"x":"Wow","b":19}""".asInstanceOf[JSON],js) - assertEquals(inst,sj.read[Foo1[VC1]](js)) - } - - test("Parameterized case class as a parameter - Foo[A](x:Bar[A])") { - val inst = Foo2(Bar2(123L), 19) - val js = sj.render(inst) - assertEquals("""{"x":{"id":123},"b":19}""".asInstanceOf[JSON],js) - assertEquals(inst,sj.read[Foo2[Long]](js)) - } - - test("Parameterized case class with parameterized another member - Foo[A](x:Bar[A], y:A)") { - val inst = Foo3(Bar2(List(1, 2, 3)), List(4, 5, 6)) - val js = sj.render(inst) - assertEquals("""{"x":{"id":[1,2,3]},"b":[4,5,6]}""".asInstanceOf[JSON],js) - assertEquals(inst,sj.read[Foo3[List[Int]]](js)) - } - - test("Class with two parameters, one given one not - Foo[A](x:List[Bar[A, Boolean]])") { - val inst = Foo4( - List( - Bar3(Map(4 -> "yes", 5 -> "no"), true), - Bar3(Map(8 -> "yellow", 9 -> "red"), false) - ), - Map(1 -> "wow", 2 -> "yup") - ) - val js = sj.render(inst) - assertEquals( - """{"x":[{"id":{"4":"yes","5":"no"},"isIt":true},{"id":{"8":"yellow","9":"red"},"isIt":false}],"b":{"1":"wow","2":"yup"}}""".asInstanceOf[JSON],js) - assertEquals(inst,sj.read[Foo4[Map[Int, String]]](js)) - } - - test("Multiple parameters, in order - Foo[A,B](x:Bar[A,B], y:A)") { - describe("Very Advanced Parameterized Case Class") - - val inst = Foo5( - List( - Bar3(Map(4 -> "yes", 5 -> "no"), true), - Bar3(Map(8 -> "yellow", 9 -> "red"), false) - ), - Map(1 -> "wow", 2 -> "yup") - ) - val js = sj.render(inst) - assertEquals( - """{"x":[{"id":{"4":"yes","5":"no"},"isIt":true},{"id":{"8":"yellow","9":"red"},"isIt":false}],"b":{"1":"wow","2":"yup"}}""".asInstanceOf[JSON],js) - assertEquals(inst,sj.read[Foo5[Map[Int, String], Boolean]](js)) - } - - test("Multiple parameters, out of order - Foo[A,B,C,D](x:Bar[C,D,A], y:B)") { - val inst = Foo6(Bar4(5, 2.5, 'H'), "wow") - val js = sj.render(inst) - assertEquals("""{"x":{"id":5,"thing1":2.5,"thing2":"H"},"y":"wow"}""".asInstanceOf[JSON],js) - assertEquals(inst,sj.read[Foo6[Double, String, Int, Char]](js)) - } - - test("Nested multiple parameters, out of order - Foo[A,B,C,D](x:Bar[C,Blah[D,A]], y:B)") { - val inst = Foo7(Bar5(5, Blah(2.5, 'H')), "wow") - val js = sj.render(inst) - assertEquals("""{"x":{"id":5,"blah":{"t":2.5,"u":"H"}},"y":"wow"}""".asInstanceOf[JSON],js) - assertEquals(inst,sj.read[Foo7[Double, String, Char, Int]](js)) - } \ No newline at end of file diff --git a/src_old/test/scala/co.blocke.scalajack/json/parameters/Model.scala b/src_old/test/scala/co.blocke.scalajack/json/parameters/Model.scala deleted file mode 100644 index e90fa9ad..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json/parameters/Model.scala +++ /dev/null @@ -1,60 +0,0 @@ -package co.blocke.scalajack -package json.parameters - -//--- Basic Parameterized Case Class -case class Foo1[A](x: A, b: Int) -case class Bar1(name: String) - -//--- Advanced Parameterized Case Class -case class Bar2[X](id: X) -case class VC1(s: String) extends AnyVal -case class Foo2[A](x: Bar2[A], b: Int) -case class Foo3[A](x: Bar2[A], b: A) -case class Bar3[X, Y](id: X, isIt: Y) -case class Foo4[A](x: List[Bar3[A, Boolean]], b: A) - -//--- Very Advanced Parameterized Case Class -case class Foo5[A, B](x: List[Bar3[A, B]], b: A) -case class Foo6[A, B, C, D](x: Bar4[C, D, A], y: B) -case class Bar4[X, Y, Z](id: X, thing1: Z, thing2: Y) -case class Blah[T, U](t: T, u: U) -case class Foo7[A, B, C, D](x: Bar5[C, D, A], y: B) -case class Bar5[X, Y, Z](id: Y, blah: Blah[Z, X]) - -//--- Basic Parameterized Trait -trait T1[X] { val x: X } -case class TFoo1[A](x: A, b: Int) extends T1[A] -trait T2 { val name: String } -case class TBar1(name: String) extends T2 - -//--- Advanced Parameterized Trait -trait T3[X] { val thing: X } -case class TBar2(thing: Boolean) extends T3[Boolean] -case class TBar3[T](thing: T) extends T3[T] -trait T4[X] { val x: TBar3[X] } -case class TFoo2[A](x: TBar3[A], b: A) extends T4[A] -trait T5[X, Y] { val thing1: X; val thing2: Y } -case class TBar4[T](thing1: T, thing2: String) extends T5[T, String] -trait T6[X] { val x: List[T5[X, String]] } -case class TFoo3[A](x: List[T5[A, String]]) extends T6[A] - -//--- Very Advanced Parameterized Trait -trait T7[X, Y] { val x: T5[X, Y]; val b: X } -case class TBar5[T, U](thing1: T, thing2: U) extends T5[T, U] -case class TFoo4[A, B](x: T5[A, B], b: A) extends T7[A, B] - -trait T8[W, X, Y, Z] { val x: T9[Y, Z, W]; val y: X } -trait T9[T, U, V] { val pi: T; val po: U; val pu: V } -case class TBar6[A, B, C](pi: A, po: B, pu: C) extends T9[A, B, C] -case class TFoo5[A, B, C, D](x: T9[C, D, A], y: B) extends T8[A, B, C, D] - -// Foo[A,B,C,D](x:Bar[C,Blah[D,A]], y:B) -trait T10[X, Y] { val x: X; val y: Y } -trait T11[W, Z] { val w: W; val z: Z } -case class TBlah1[A, B](w: A, z: B) extends T11[A, B] -case class TBar7[A, B](thing1: A, thing2: B) extends T5[A, B] -case class TFoo6[A, B, C, D](x: T11[C, T5[D, A]], y: B) - extends T10[T11[C, T5[D, A]], B] - -trait Pet[T]{ val name: String; val other: T } -case class Dog[X](name: String, numLegs: Int, other: X) extends Pet[X] \ No newline at end of file diff --git a/src_old/test/scala/co.blocke.scalajack/json/parameters/TraitParams.scala b/src_old/test/scala/co.blocke.scalajack/json/parameters/TraitParams.scala deleted file mode 100644 index a8889379..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json/parameters/TraitParams.scala +++ /dev/null @@ -1,143 +0,0 @@ -package co.blocke.scalajack -package json.parameters - -import TestUtil._ -import munit._ -import munit.internal.console -import co.blocke.scalajack.json.JSON -import co.blocke.scala_reflection._ - -class TraitParams() extends FunSuite: - - val sj = co.blocke.scalajack.ScalaJack() - - test("Simple parameters - Foo[A](x:A) where A -> simple type") { - describe("---------------------------------\n: Trait Paramterization Tests :\n---------------------------------", Console.BLUE) - describe("Basic Parameterized Trait") - - val inst: T1[Boolean] = TFoo1(false, 19) - val js = sj.render[T1[Boolean]](inst) - assertEquals("""{"_hint":"co.blocke.scalajack.json.parameters.TFoo1","x":false,"b":19}""".asInstanceOf[JSON],js) - assertEquals(inst,sj.read[T1[Boolean]](js)) - } - - test("Non-parameter trait as a parameter - Foo[A](x:A) where A -> Bar (case class)") { - val inst: T1[T2] = TFoo1(TBar1("Fred"), 19) - val js = sj.render[T1[T2]](inst) - assertEquals( - """{"_hint":"co.blocke.scalajack.json.parameters.TFoo1","x":{"_hint":"co.blocke.scalajack.json.parameters.TBar1","name":"Fred"},"b":19}""".asInstanceOf[JSON],js) - assert(inst == sj.read[TFoo1[TBar1]](js)) - } - - /* - test("Parameterized trait as parameter - Foo[A](x:A) where A -> Bar[Int]") { - describe("Advanced Parameterized trait") - - val inst: T1[TBar2] = TFoo1(TBar2(true), 19) - val js = sj.render(inst) - assertEquals( - """{"_hint":"co.blocke.scalajack.json.parameters.TFoo1","x":{"thing":true},"b":19}""".asInstanceOf[JSON],js) - assert(inst == sj.read[T1[TBar2]](js)) - } - - test("Value class as parameter - Foo[A](x:A) where A -> value class") { - val inst: T1[VC1] = TFoo1(VC1("Wow"), 19) - val js = sj.render(inst) - assertEquals("""{"_hint":"co.blocke.scalajack.json.parameters.TFoo1","x":"Wow","b":19}""".asInstanceOf[JSON],js) - assert(inst == sj.read[T1[VC1]](js)) - } - - test("Parameterized trait as a parameter - Foo[A](x:Bar[A])") { - val inst: T1[T3[Boolean]] = TFoo1(TBar3(false), 19) - val js = sj.render(inst) - assertEquals( - """{"_hint":"co.blocke.scalajack.json.parameters.TFoo1","x":{"_hint":"co.blocke.scalajack.json.parameters.TBar3","thing":false},"b":19}""".asInstanceOf[JSON],js) - assert(inst == sj.read[T1[T3[Boolean]]](js)) - } - - test("Parameterized trait with parameterized another member - Foo[A](x:Bar[A], y:A)") { - val inst: T4[Boolean] = TFoo2(TBar3(false), true) - val js = sj.render(inst) - assertEquals( - """{"_hint":"co.blocke.scalajack.json.parameters.TFoo2","x":{"thing":false},"b":true}""".asInstanceOf[JSON],js) - assert(inst == sj.read[T4[Boolean]](js)) - } - - test("Trait with two parameters, one given one not - Foo[A](x:List[Bar[A, Boolean]])") { - val inst: T6[Int] = TFoo3(List(TBar4(5, "five"), TBar4(6, "six"))) - val js = sj.render(inst) - assertEquals( - """{"_hint":"co.blocke.scalajack.json.parameters.TFoo3","x":[{"_hint":"co.blocke.scalajack.json.parameters.TBar4","thing1":5,"thing2":"five"},{"_hint":"co.blocke.scalajack.json.parameters.TBar4","thing1":6,"thing2":"six"}]}""".asInstanceOf[JSON],js) - assert(inst == sj.read[T6[Int]](js)) - } - - test("Multiple parameters, in order - Foo[A,B](x:Bar[A,B], y:A)") { - describe("Very Advanced Parameterized Trait") - - val inst: T7[Long, String] = TFoo4(TBar5(123L, "wow"), 456L) - val js = sj.render(inst) - assertEquals( - """{"_hint":"co.blocke.scalajack.json.parameters.TFoo4","x":{"_hint":"co.blocke.scalajack.json.parameters.TBar5","thing1":123,"thing2":"wow"},"b":456}""".asInstanceOf[JSON],js) - assert(inst == sj.read[T7[Long, String]](js)) - } - - test("Multiple parameters, out of order - Foo[A,B,C,D](x:Bar[C,D,A], y:B)") { - val inst: T8[Char, String, Int, Double] = - TFoo5(TBar6(5, 2.5, 'H'), "wow") - val js = sj.render(inst) - assertEquals( - """{"_hint":"co.blocke.scalajack.json.parameters.TFoo5","x":{"_hint":"co.blocke.scalajack.json.parameters.TBar6","pi":5,"po":2.5,"pu":"H"},"y":"wow"}""".asInstanceOf[JSON],js) - assert(inst == sj.read[T8[Char, String, Int, Double]](js)) - } - - test("Multiple parameters, out of order - Foo[A,B,C,D](x:Bar[C,D,A], y:B)") { - val inst: T8[Char, String, Int, Double] = - TFoo5(TBar6(5, 2.5, 'H'), "wow") - val js = sj.render(inst) - assertEquals( - """{"_hint":"co.blocke.scalajack.json.parameters.TFoo5","x":{"_hint":"co.blocke.scalajack.json.parameters.TBar6","pi":5,"po":2.5,"pu":"H"},"y":"wow"}""".asInstanceOf[JSON],js) - assert(inst == sj.read[T8[Char, String, Int, Double]](js)) - } - - test("Nested multiple parameters, out of order - Foo[A,B,C,D](x:Bar[C,Blah[D,A]], y:B)") { - val rt = RType.of[T10[T11[Int, T5[Double, Char]], String]] - val inst: T10[T11[Int, T5[Double, Char]], String] = TFoo6(TBlah1(5, TBar7(1.2, 'Z')), "wow") - val js = sj.render(inst) - assertEquals( - """{"_hint":"co.blocke.scalajack.json.parameters.TFoo6","x":{"_hint":"co.blocke.scalajack.json.parameters.TBlah1","w":5,"z":{"_hint":"co.blocke.scalajack.json.parameters.TBar7","thing1":1.2,"thing2":"Z"}},"y":"wow"}""".asInstanceOf[JSON],js) - val z = sj.read[T10[T11[Int, T5[Double, Char]], String]](js) - assertEquals(inst, sj.read[T10[T11[Int, T5[Double, Char]], String]](js)) - } - */ - - /* Performance tests - var rtx: RType = null - var jsx: JSON = null.asInstanceOf[JSON] - var zx: T10[T11[Int, T5[Double, Char]], String] = null - test("a") { - rtx = Reflector.reflectOn[T10[T11[Int, T5[Double, Char]], String]] - } - test("b") { - val inst: T10[T11[Int, T5[Double, Char]], String] = TFoo6(TBlah1(5, TBar7(1.2, 'Z')), "wow") - jsx = sj.render(inst) - } - test("c") { - zx = sj.read[T10[T11[Int, T5[Double, Char]], String]](jsx) - } - test("a.2") { - rtx = Reflector.reflectOn[T10[T11[Int, T5[Double, Char]], String]] - } - test("b.2") { - val inst: T10[T11[Int, T5[Double, Char]], String] = TFoo6(TBlah1(5, TBar7(1.2, 'Z')), "wow") - jsx = sj.render(inst) - } - test("c.2") { - zx = sj.read[T10[T11[Int, T5[Double, Char]], String]](jsx) - } - test("Z") { - val inst: Pet[Boolean] = Dog("Fido",4,true) - val js = sj.render(inst) - sj.read[Pet[Boolean]](js) - } - */ - diff --git a/src_old/test/scala/co.blocke.scalajack/json/plainclass/Inheritance.scala b/src_old/test/scala/co.blocke.scalajack/json/plainclass/Inheritance.scala deleted file mode 100644 index 5aacc68b..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json/plainclass/Inheritance.scala +++ /dev/null @@ -1,141 +0,0 @@ -package co.blocke.scalajack -package json -package plainclass - -import co.blocke.scalajack.model.ClassNameHintModifier -import co.blocke.scala_reflection._ -import TestUtil._ -import munit._ -import munit.internal.console -import co.blocke.scalajack.json.JSON -import JsonMatcher._ - - -class Inheritance() extends FunSuite: - - val sj = co.blocke.scalajack.ScalaJack() - - test("Simple class inheritance must work (all fields present)") { - describe( - "-------------------------------------\n: Inheritance Tests (Plain Class) :\n-------------------------------------", Console.BLUE - ) - describe("Scala Plain") - - val js = - """{"uno":"foo","foobar":99,"dontForget":12,"three":false,"quatro":12.34,"foo":25,"extra":"bar"}""".asInstanceOf[JSON] - val simple = sj.read[InheritSimpleChild](js) - assertEquals(simple.one, "foo") - assertEquals(simple.extra, "bar") - assertEquals(simple.foo, 25) - assertEquals(simple.two, 99) - assertEquals(simple.three, false) - assertEquals(simple.four, 12.34) - // Need this matching because JSON order is often different - assert(JsonMatcher.jsonMatches(js, sj.render(simple))) - } - - test("MapName, and Ignore annotations must be inherited properly") { - val adapter = sj.taCache - .typeAdapterOf[InheritSimpleChild] - .asInstanceOf[co.blocke.scalajack.typeadapter.classes.NonCaseClassTypeAdapter[_]] - assertEquals( adapter.dbKeys.map(f => (f.name, f.dbKeyIndex)), - List( - ("uno", Some(0)), - ("foobar", Some(1)), - ("quatro", Some(2)), - ("foo", Some(99)) - ) - ) - val inst = new InheritSimpleChild("thing1", "thing2") - val js = sj.render(inst) - // Need this matching because JSON order is often different - assert(JsonMatcher.jsonMatches(js, """{"extra":"thing1","uno":"thing2","foo":39,"dontForget":9,"quatro":0.1,"three":true,"foobar":5}""".asInstanceOf[JSON])) - } - - test("With type parameter") { - val js = """{"thing":5, "item": 15, "cosa": 99}""".asInstanceOf[JSON] - val inst = sj.read[ParamChild[Int]](js) - assertEquals(inst.thing, 5) - assertEquals(inst.item, 15) - assertEquals(inst.cosa, 99) - assert(JsonMatcher.jsonMatches(sj.render(inst), """{"thing":5,"cosa":99,"item":15}""".asInstanceOf[JSON])) - } - - test("With type member (as part of a trait)") { - val inst = new WrapTrait[TraitBase]() - val flower = new Flower(5, 6) - inst.rose = flower - val js = sj.render(inst) - assertEquals(js, - """{"flower":"co.blocke.scalajack.json.plainclass.Flower","rose":{"thing":5,"other":6}}""".asInstanceOf[JSON] - ) - val inst2 = sj.read[WrapTrait[TraitBase]](js) - assert(inst2.rose.thing == flower.thing && inst2.rose.other == flower.other) - } - - test("Must catch missing/required var") { - describe("Scala Plain Negative") - - val js = - """{"extra":"bar","foo":25,"dontForget":12,"uno":"something","quatro":12.34}""".asInstanceOf[JSON] - val msg = """Class co.blocke.scalajack.json.plainclass.InheritSimpleChild missing required fields: foobar - |...,"dontForget":12,"uno":"something","quatro":12.34} - |----------------------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[InheritSimpleChild](js) - } - } - - test("Must catch missing/required constructor field (with newline)") { - val js = - """{"extra":"bar","foo":25,"dontForget":12,"quatro":12.34,"foobar":99}""".asInstanceOf[JSON] - val msg = - """Class co.blocke.scalajack.json.plainclass.InheritSimpleChild missing required fields: uno - |...oo":25,"dontForget":12,"quatro":12.34,"foobar":99} - |----------------------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[InheritSimpleChild](js) - } - } - - test("Must catch missing/required getter/setter field") { - val js = - """{"extra":"bar","foo":25,"uno":"something","quatro":12.34,"foobar":99}""".asInstanceOf[JSON] - val msg = - """Class co.blocke.scalajack.json.plainclass.InheritSimpleChild missing required fields: dontForget - |...":25,"uno":"something","quatro":12.34,"foobar":99} - |----------------------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[InheritSimpleChild](js) - } - } - - // NOTE: We can't test this because the exception is tossed at compile-time. (It will indeed throw this exception, however.) - //---------- - // test("Must fail non-val constructor field") { - // val f = new Fail4(1, 2) - // interceptMessage[co.blocke.scala_reflection.ReflectException]("""Class [co.blocke.scalajack.json.plainclass.Fail4]: Non-case class constructor arguments must all be 'val'"""){ - // sj.render(f) - // } - // } - - test( - "Simple class inheritance must work (all fields present) including MapName and Ignore" - ) { - describe("Java Plain") - - val js = """{"three":3,"dos":1}""".asInstanceOf[JSON] - val simple = sj.read[JavaSimpleChild](js) - assertEquals(simple.getTwo, 1) - assertEquals(simple.getThree, 3) - assertEquals(simple.getBogus, -1) - assertEquals(sj.render(simple), js) - } - - test("Optional annotation must be inherited properly") { - val js = """{"dos":1}""".asInstanceOf[JSON] - val simple = sj.read[JavaSimpleChild](js) - assertEquals(simple.getTwo, 1) - assertEquals(simple.getThree, -10) - assertEquals(sj.render(simple), """{"three":-10,"dos":1}""".asInstanceOf[JSON]) - } \ No newline at end of file diff --git a/src_old/test/scala/co.blocke.scalajack/json/plainclass/Misc.scala b/src_old/test/scala/co.blocke.scalajack/json/plainclass/Misc.scala deleted file mode 100644 index d62aeb39..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json/plainclass/Misc.scala +++ /dev/null @@ -1,81 +0,0 @@ -package co.blocke.scalajack -package json.plainclass - -import co.blocke.scalajack.model.ClassNameHintModifier -import co.blocke.scala_reflection._ -import TestUtil._ -import munit._ -import munit.internal.console -import co.blocke.scalajack.json.JSON - - -trait Distance extends Any -case class Meter(val value: Double) extends AnyVal with Distance - - -class Misc() extends FunSuite: - - val sj = co.blocke.scalajack.ScalaJack() - - test("Read/write null into object") { - describe("------------------------\n: Misc Tests (Plain) :\n------------------------", Console.BLUE) - - assert(null == sj.read[PlayerMix]("null".asInstanceOf[JSON]) ) - assert("null".asInstanceOf[JSON] == sj.render[PlayerMix](null) ) - } - - test("Handles type members with modifier") { - val prependHintMod = ClassNameHintModifier( - (hint: String) => "co.blocke.scalajack.json.plainclass." + hint, - (cname: String) => cname.split('.').last - ) - val sj2 = co.blocke.scalajack.ScalaJack().withTypeValueModifier(prependHintMod) - val js = """{"flower":"Flower","rose":{"thing":5,"other":6}}""".asInstanceOf[JSON] - val inst = sj2.read[WrapTrait[TraitBase]](js) - assert(inst.rose.isInstanceOf[Flower]) - assertEquals(sj2.render(inst), js) - } - - test("Fails if no hint for type member") { - val js = """{"rose":{"thing":5,"other":6}}""".asInstanceOf[JSON] - val msg = - """Did not find required type member(s): flower - |{"rose":{"thing":5,"other":6}} - |^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[WrapTrait[TraitBase]](js) - } - } - - test("Must accept missing default constructor values") { - val js = """{"foobar":3, "quatro":4, "dontForget":1}""".asInstanceOf[JSON] - val inst = sj.read[InheritSimpleBase](js) - assertEquals(inst.one, "blather") - } - - test("Must accept missing optional constructor values") { - val js = """{"b":3}""".asInstanceOf[JSON] - val inst = sj.read[OptConst](js) - assertEquals(inst.a, None) - assertEquals(inst.b, Some(3)) - } - - test("Must ignore unneeded type members") { - val inst = new UnneededType[String]() - inst.a = 9 - assertEquals(sj.render(inst), """{"a":9}""".asInstanceOf[JSON]) - } - - test("Must require Java classes to have an empty constructor") { - val inst = new Unsupported("Foo") - interceptMessage[co.blocke.scalajack.ScalaJackError]("""ScalaJack does not support Java classes with a non-empty constructor."""){ - sj.render(inst) - } - } - - test("Must handle Change on Java setter") { - val js = """{"dos":9}""".asInstanceOf[JSON] - val inst = sj.read[OnSetter](js) - assertEquals(inst.getTwo, 9) - assertEquals(sj.render(inst), js) - } diff --git a/src_old/test/scala/co.blocke.scalajack/json/plainclass/Model.scala b/src_old/test/scala/co.blocke.scalajack/json/plainclass/Model.scala deleted file mode 100644 index 0300e78a..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json/plainclass/Model.scala +++ /dev/null @@ -1,132 +0,0 @@ -package co.blocke.scalajack -package json.plainclass - -import co.blocke.scalajack.SJCapture -import co.blocke.scala_reflection._ -import co.blocke.scalajack._ - -import scala.util._ - - -class InheritSimpleBase( - @DBKey(index = 50)@Change(name = "bogus") val one:String= "blather" -) { - // Public data member - @DBKey(index = 1) @Change(name = "foobar") var two: Int = 5 - @Optional var three: Boolean = true - - // Private var or val - val notOne: Int = 2 - - @Ignore var dontseeme: Int = 90 - - // Scala-style getter/setter - private var _four: Double = 0.1 - @DBKey(index = 2) def four: Double = _four - @Change(name = "quatro") def four_=(a: Double): Unit = _four = a - - private var _dontForget: Int = 9 - def dontForget: Int = _dontForget - def dontForget_=(a: Int): Unit = _dontForget = a - - private var _unused: Double = 0.1 - @Ignore def unused: Double = _unused - def unused_=(a: Double): Unit = _unused = a -} - -class InheritSimpleChild( - val extra: String, - @DBKey @Change(name = "uno") override val one:String) - extends InheritSimpleBase(one) { - @DBKey(index = 99) var foo: Int = 39 - @Ignore var bogus: String = "" - - private var _nada: Double = 0.1 - def nada: Double = _nada - @Ignore def nada_=(a: Double): Unit = _nada = a -} - -// --- - -class ParamBase[T](val thing: T) { - var item: T = null.asInstanceOf[T] - - private var _cosa: T = null.asInstanceOf[T] - def cosa: T = _cosa - def cosa_=(a: T): Unit = _cosa = a -} - -class ParamChild[T](override val thing: T) extends ParamBase[T](thing) - -// --- - -trait TraitBase { - val thing: Int - val other: Int -} - -class Flower(val thing: Int, val other: Int) extends TraitBase - -class WrapTrait[T <: TraitBase]() { - type flower = T - var rose: T = null.asInstanceOf[T] - // IMPORTANT! rose must be of type T, not "flower". flower is the label for the external type in JSON -} - -// --- - -// class Fail4(val a: Int, b: Int) - -// -- - -class OptConst(val a: Option[Int]) { - var b: Option[Int] = Some(3) -} - -class UnneededType[T]() { - type item = T - - val m: T = null.asInstanceOf[item] - var a: Int = 5 -} - -//------------------------------------------------------ -case class VCDouble(vc: Double) extends AnyVal -class PlayerMix() { - def someConfusingThing() = true - var name: String = "" // public var member - var maybe: Option[Int] = Some(1) // optional member - - @Ignore var bogus: String = "" - - private var _age: VCDouble = VCDouble(0.0) - def age: VCDouble = _age // getter/setter member - def age_=(a: VCDouble): Unit = _age = a -} - -class BigPlayer() extends PlayerMix { - var more: Int = 0 -} - -// class NotAllVals(val a: Int, b: Int, val c: Int) - -class Embed() { - var stuff: List[String] = List.empty[String] - var num: Int = 0 -} -class Boom() { - var name: String = "" - var other: Try[Embed] = Success(null) -} - -class Cap() extends SJCapture { - var name: String = "" -} - -case class CaseCap(name: String) extends SJCapture - - -case class One(vc: List[VCDouble]) -class Two(val vc: VCDouble) { - var vcx: VCDouble = VCDouble(54.32) -} \ No newline at end of file diff --git a/src_old/test/scala/co.blocke.scalajack/json/plainclass/TryAndCapture.scala b/src_old/test/scala/co.blocke.scalajack/json/plainclass/TryAndCapture.scala deleted file mode 100644 index 92a97c0c..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json/plainclass/TryAndCapture.scala +++ /dev/null @@ -1,80 +0,0 @@ -package co.blocke.scalajack -package json -package plainclass - -import co.blocke.scala_reflection._ -import TestUtil._ -import munit._ -import munit.internal.console -import co.blocke.scalajack.json.JSON -import scala.util.{Try, Success, Failure} -import JsonMatcher._ - - -class TryAndCapture() extends FunSuite: - - val sj = co.blocke.scalajack.ScalaJack() - - test("Try sucess") { - describe("-----------------------------------------\n: Try and Capture Tests (Plain class) :\n-----------------------------------------", Console.BLUE) - describe("Try:") - - val js = """{"name":"Greg","other":{"stuff":["a","b","c"],"num":2}}""".asInstanceOf[JSON] - val obj = sj.read[Boom](js) - assert( - obj.name == "Greg" && obj.other - .asInstanceOf[Success[Embed]] - .get - .num == 2 - ) - assert( - jsonMatches("""{"other":{"num":2,"stuff":["a","b","c"]},"name":"Greg"}""".asInstanceOf[JSON], sj.render(obj) )) - } - - test("Try failure") { - val js = """{"name":"Greg","other":[1,2,3]}""".asInstanceOf[JSON] - val obj = sj.read[Boom](js) - assertEquals("""Expected start of object here - |{"name":"Greg","other":[1,2,3]} - |-----------------------^""".stripMargin, - obj.other.asInstanceOf[Failure[_]].exception.getMessage) - assert(jsonMatches("""{"other":[1,2,3],"name":"Greg"}""".asInstanceOf[JSON], sj.render(obj) )) - } - - test("Try failure 2") { - val js = """{"name":"Greg","other": -12.45 ,"num":2}""".asInstanceOf[JSON] - val obj = sj.read[Boom](js) - assertEquals("""Expected start of object here - |{"name":"Greg","other": -12.45 ,"num":2} - |-------------------------^""".stripMargin, - obj.other.asInstanceOf[Failure[_]].exception.getMessage) - assert(jsonMatches("""{"other":-12.45,"name":"Greg"}""".asInstanceOf[JSON], sj.render(obj) )) - } - - test("Plain-class capture can write semantically equivalent JSON") { - describe("Capture:") - val js = - """{"name":"Greg", "foo":[1,2,"t" ], "zing": {"dot":{"age":25,"food":"Pizza"}}, "blather":"wow", "boo": -29384.34, "maybe": false }""".asInstanceOf[JSON] - val h = sj.read[Cap](js) - assertEquals(h.name,"Greg") - val js2 = sj.render(h) - assert( jsonMatches(js2,js) ) - } - - test("Case class capture can write semantically equivalent JSON") { - val js = - """{"name":"Greg", "foo":[1,2,"t" ], "zing" : {"_hint":"a.b.com.Hey", "dot":{"age":25,"food":"Pizza"}}, "blather":"wow", "boo": -29384.34, "maybe": false }""".asInstanceOf[JSON] - val h = sj.read[CaseCap](js) - assertEquals(h.name, "Greg") - val js2 = sj.render(h) - assert( jsonMatches(js2,js) ) - } - - test("Java class capture can write semantically equivalent JSON") { - val js = - """{"name":"Greg", "foo":[1,2,"t" ], "zing" : {"_hint":"a.b.com.Hey", "dot":{"age":25,"food":"Pizza"}}, "blather":"wow", "boo": -29384.34, "maybe": false }""".asInstanceOf[JSON] - val h = sj.read[JavaCap](js) - assertEquals(h.getName ,"Greg") - val js2 = sj.render(h) - assert( jsonMatches(js2,js) ) - } diff --git a/src_old/test/scala/co.blocke.scalajack/json/plainclass/ValueClass.scala b/src_old/test/scala/co.blocke.scalajack/json/plainclass/ValueClass.scala deleted file mode 100644 index f9e8ee7e..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json/plainclass/ValueClass.scala +++ /dev/null @@ -1,31 +0,0 @@ -package co.blocke.scalajack -package json -package plainclass - -import co.blocke.scalajack.model.ClassNameHintModifier -import co.blocke.scala_reflection._ -import TestUtil._ -import munit._ -import munit.internal.console -import co.blocke.scalajack.json.JSON -import JsonMatcher._ - - -class ValueClass() extends FunSuite: - - val sj = co.blocke.scalajack.ScalaJack() - - test("Value class of Double") { - describe( - "----------------------------------------------\n: ValueClass DelimSpec Tests (Plain Class) :\n----------------------------------------------", Console.BLUE - ) - - val p1 = new PlayerMix() - p1.name = "Mike" - p1.age = VCDouble(BigDecimal("1.23").toDouble) - val js = sj.render(p1) - val r = sj.read[PlayerMix](js) - assert(jsonMatches("""{"age":1.23,"maybe":1,"name":"Mike"}""".asInstanceOf[JSON], js )) - assertEquals(p1.name, r.name) - assertEquals(p1.age, r.age) - } diff --git a/src_old/test/scala/co.blocke.scalajack/json/primitives/AnyPrim.scala b/src_old/test/scala/co.blocke.scalajack/json/primitives/AnyPrim.scala deleted file mode 100644 index 467e2a32..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json/primitives/AnyPrim.scala +++ /dev/null @@ -1,159 +0,0 @@ -package co.blocke.scalajack -package json.primitives - -import TestUtil._ -import munit._ -import munit.internal.console -import co.blocke.scalajack.json.JSON -import co.blocke.scala_reflection._ - -class AnyPrim() extends FunSuite: - - val sj = co.blocke.scalajack.ScalaJack() - - test("null works") { - describe("-------------------------\n: Any Primitive Tests :\n-------------------------", Console.BLUE) - - val shell = AnyShell(null) - val js = sj.render(shell) - assertEquals("""{"a":null}""".asInstanceOf[JSON],js) - assert(sj.read[AnyShell](js).a == null) - } - - test("BigDecimal works") { - val payload = BigDecimal("12345678901234567890.12345678901234567890") - val js = sj.render(AnyShell(payload)) - assertEquals("""{"a":12345678901234567890.12345678901234567890}""".asInstanceOf[JSON], js) - assert({ - val parsed = sj.read[AnyShell](js).a - (parsed == payload) && (parsed.getClass == payload.getClass) - }) - } - - test("BigInt works") { - val payload = BigInt("12345678901234567890") - val js = sj.render(AnyShell(payload)) - assertEquals("""{"a":12345678901234567890}""".asInstanceOf[JSON], js) - assert({ - val parsed = sj.read[AnyShell](js).a - (parsed == payload) && parsed.isInstanceOf[BigInt] - }) - } - - test("Boolean works") { - val payload = true - val js = sj.render(AnyShell(payload)) - assertEquals("""{"a":true}""".asInstanceOf[JSON], js) - assert({ - val parsed = sj.read[AnyShell](js).a - (parsed == payload) && parsed.isInstanceOf[Boolean] - }) - } - - test("Byte works") { - val payload: Byte = 16 - val js = sj.render(AnyShell(payload)) - assertEquals("""{"a":16}""".asInstanceOf[JSON], js) - assert({ - val parsed = sj.read[AnyShell](js).a - (parsed == payload) && parsed.isInstanceOf[Integer] // byte becomes Integer - }) - } - - test("Char works") { - val payload: Char = 'Z' - val js = sj.render(AnyShell(payload)) - assertEquals("""{"a":"Z"}""".asInstanceOf[JSON], js) - assert({ - val parsed = sj.read[AnyShell](js).a - (parsed == payload.toString) && parsed.isInstanceOf[String] // Char becomes String - }) - } - - test("Double works") { - val payload: Double = 1234.5678 - val js = sj.render(AnyShell(payload)) - assertEquals("""{"a":1234.5678}""".asInstanceOf[JSON], js) - assert({ - val parsed = sj.read[AnyShell](js).a - (parsed == payload) && parsed.isInstanceOf[Double] - }) - } - - test("Enumeration works") { - val payload: Size.Value = Size.Small - val js = sj.render(AnyShell(payload)) - assertEquals("""{"a":"Small"}""".asInstanceOf[JSON], js) - assert({ - val parsed = sj.read[AnyShell](js).a - (parsed == payload.toString) && parsed.isInstanceOf[String] // enum value becomes String - }) - } - - test("Enum works") { - val payload = Color.Blue - val js = sj.render(AnyShell(payload)) - assertEquals("""{"a":"Blue"}""".asInstanceOf[JSON], js) - assert({ - val parsed = sj.read[AnyShell](js).a - (parsed == payload.toString) && parsed.isInstanceOf[String] // enum value becomes String - }) - } - - test("Float works") { - val payload: Float = 1234.5678F - val js = sj.render(AnyShell(payload)) - assertEquals("""{"a":1234.5677}""".asInstanceOf[JSON], js) - assert({ - val parsed = sj.read[AnyShell](js).a - (parsed.toString == payload.toString) && parsed.isInstanceOf[Double] // float becomes Double - }) - } - - test("Int works") { - val payload: Int = 1234567 - val js = sj.render(AnyShell(payload)) - assertEquals("""{"a":1234567}""".asInstanceOf[JSON], js) - assert({ - val parsed = sj.read[AnyShell](js).a - (parsed == payload) && parsed.isInstanceOf[Int] - }) - } - - test("Long works") { - val payload: Long = 123456789012345L - val js = sj.render(AnyShell(payload)) - assertEquals("""{"a":123456789012345}""".asInstanceOf[JSON], js) - assert({ - val parsed = sj.read[AnyShell](js).a - (parsed == payload) && parsed.isInstanceOf[Long] // (Note this could become Integer if smaller number parsed) - }) - - val payload2: Long = 123L - val js2 = sj.render(AnyShell(payload2)) - assertEquals("""{"a":123}""".asInstanceOf[JSON], js2) - assert({ - val parsed = sj.read[AnyShell](js2).a - (parsed == payload2) && parsed.isInstanceOf[Int] // Long became Int due to smaller size - }) - } - - test("Short works") { - val payload: Short = 16234 - val js = sj.render(AnyShell(payload)) - assertEquals("""{"a":16234}""".asInstanceOf[JSON], js) - assert({ - val parsed = sj.read[AnyShell](js).a - (parsed == payload) && parsed.isInstanceOf[Int] // short becomes Int - }) - } - - test("String works") { - val payload = "something" - val js = sj.render(AnyShell(payload)) - assertEquals("""{"a":"something"}""".asInstanceOf[JSON], js) - assert({ - val parsed = sj.read[AnyShell](js).a - (parsed == payload) && (parsed.getClass == payload.getClass) - }) - } \ No newline at end of file diff --git a/src_old/test/scala/co.blocke.scalajack/json/primitives/Enums.scala b/src_old/test/scala/co.blocke.scalajack/json/primitives/Enums.scala deleted file mode 100644 index 31e4cc05..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json/primitives/Enums.scala +++ /dev/null @@ -1,136 +0,0 @@ -package co.blocke.scalajack -package json.primitives - -import co.blocke.scala_reflection._ -import scala.math.BigDecimal -import java.util.UUID -import TestUtil._ -import munit._ -import munit.internal.console -import co.blocke.scalajack.json.JSON - -class Enums() extends FunSuite: - - val sj = co.blocke.scalajack.ScalaJack() - val sj2 = sj.enumsAsInts() - - test("Enumeration (Scala 2.x) must work (not nullable)") { - describe("-----------------\n: Scala Enums :\n-----------------", Console.BLUE) - - import SizeWithType._ - val inst = SampleEnum(Size.Small, Size.Medium, Size.Large, null, Size.Medium, Little) - val js = sj.render(inst) - assertEquals( - """{"e1":"Small","e2":"Medium","e3":"Large","e4":null,"e5":"Medium","e6":"Little"}""".asInstanceOf[JSON], - js) - // mutate e5 into an ordinal... - val js2 = js.asInstanceOf[String].replaceAll(""""e5":"Medium"""", """"e5":1""").asInstanceOf[JSON] - assertEquals(inst, sj.read[SampleEnum](js2)) - } - - test("Ordinal Enumeration (Scala 2.x) must work (not nullable)") { - import SizeWithType._ - val inst = SampleEnum(Size.Small, Size.Medium, Size.Large, null, Size.Medium, Little) - val js = sj2.render(inst) - assertEquals( - """{"e1":0,"e2":1,"e3":2,"e4":null,"e5":1,"e6":0}""".asInstanceOf[JSON], - js) - // mutate e5 into an ordinal... - val js2 = js.asInstanceOf[String].replaceAll(""""e5":"Medium"""", """"e5":1""").asInstanceOf[JSON] - assertEquals(inst, sj.read[SampleEnum](js2)) - } - - test("Enum (Scala 3.x) must work (not nullable)") { - val inst = TVColors(null, Color.Red) - val js = sj.render(inst) - assertEquals( - """{"color1":null,"color2":"Red"}""".asInstanceOf[JSON], - js) - val inst2 = sj.read[TVColors](js) - assertEquals(inst, inst2) - } - - test("Ordinal Enum (Scala 3.x) must work (not nullable)") { - val inst = TVColors(null, Color.Red) - val js = sj2.render(inst) - assertEquals( - """{"color1":null,"color2":0}""".asInstanceOf[JSON], - js) - assertEquals(inst, sj2.read[TVColors](js)) - } - - test("""Sealed trait "enums" must work""") { - val inst = Ride(Car(4,"Red")) - val js = sj.render(inst) - assertEquals("""{"wheels":{"numberOfWheels":4,"color":"Red"}}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[Ride](js)) - } - - test("""Case object "enums" must work""") { - val inst = Favorite(Chocolate) - val js = sj.render(inst) - assertEquals("""{"flavor":"Chocolate"}""".asInstanceOf[JSON],js) - assertEquals(inst, sj.read[Favorite](js)) - } - - test("Enumeration (Scala 2.x) must break") { - describe("--- Negative Tests ---") - val js = - """{"e1":"Small","e2":"Bogus","e3":"Large","e4":null,"e5":"Medium","e6":"Little"}""".asInstanceOf[JSON] - val msg = - """No value found in enumeration co.blocke.scalajack.json.primitives.Size$ for Bogus - |{"e1":"Small","e2":"Bogus","e3":"Large","e4":null,"e5":"Medium","e6":"Little"} - |-------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleEnum](js) - } - val js2 = - """{"e1":"Small","e2":"Medium","e3":"Large","e4":null,"e5":9,"e6":"Little}""".asInstanceOf[JSON] - val msg2 = - """No value found in enumeration co.blocke.scalajack.json.primitives.Size$ for 9 - |...Small","e2":"Medium","e3":"Large","e4":null,"e5":9,"e6":"Little} - |----------------------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ - sj.read[SampleEnum](js2) - } - val js3 = - """{"e1":"Small","e2":"Medium","e3":"Large","e4":null,"e5":false,"e6":"Little}""".asInstanceOf[JSON] - val msg3 = """Expected a Number or String here - |...Small","e2":"Medium","e3":"Large","e4":null,"e5":false,"e6":"Little} - |----------------------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg3){ - sj.read[SampleEnum](js3) - } - } - - test("Enum (Scala 3.x) must break") { - val js = """{"color1":null,"color2":"Bogus"}""".asInstanceOf[JSON] - val msg = """No value found in enumeration co.blocke.scalajack.json.primitives.Color for Bogus - |{"color1":null,"color2":"Bogus"} - |------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[TVColors](js) - } - } - - //-------------------------------------- - - test("Java Enumeration (not nullable) must work") { - describe("----------------------\n: Java Enumeration :\n----------------------", Console.BLUE) - describe("+++ Positive Tests +++") - - val inst = new JavaEnum() - inst.setTemp(Temperature.Hot) - val js = sj.render(inst) - assertEquals("""{"temp":"Hot"}""".asInstanceOf[JSON], js) - assertEquals(inst.getTemp, sj.read[JavaEnum](js).getTemp) - } - - test("Java Enumeration (not nullable) must fail") { - describe("--- Negative Tests ---") - - val js = """{"temp":"Bogus"}""".asInstanceOf[JSON] - interceptMessage[java.lang.IllegalArgumentException]("No enum constant co.blocke.scalajack.Temperature.Bogus"){ - sj.read[JavaEnum](js) - } - } diff --git a/src_old/test/scala/co.blocke.scalajack/json/primitives/JavaPrim.scala b/src_old/test/scala/co.blocke.scalajack/json/primitives/JavaPrim.scala deleted file mode 100644 index 3f1a8b24..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json/primitives/JavaPrim.scala +++ /dev/null @@ -1,326 +0,0 @@ -package co.blocke.scalajack -package json.primitives - -import TestUtil._ -import munit._ -import munit.internal.console -import co.blocke.scalajack.json.JSON -import co.blocke.scala_reflection._ - -import java.lang.{ - Boolean => JBoolean, - Byte => JByte, - Double => JDouble, - Float => JFloat, - Integer => JInt, - Long => JLong, - Short => JShort -} -import java.math.{ BigDecimal => JBigDecimal, BigInteger => JBigInteger } - -class JavaPrim() extends FunSuite: - - val sj = co.blocke.scalajack.ScalaJack() - - test("BigDecimal must work") { - describe("--------------------------\n: Java Primitive Tests :\n--------------------------", Console.BLUE) - describe("+++ Positive Tests +++") - - val inst = SampleJBigDecimal( - JBigDecimal.ZERO, - JBigDecimal.ONE, - JBigDecimal.TEN, - new JBigDecimal( - "0.1499999999999999944488848768742172978818416595458984375" - ), - null - ) - val js = sj.render(inst) - assertEquals( - """{"bd1":0,"bd2":1,"bd3":10,"bd4":0.1499999999999999944488848768742172978818416595458984375,"bd5":null}""".asInstanceOf[JSON], - js) - assertEquals(inst, sj.read[SampleJBigDecimal](js)) - } - - test("BigInteger must work") { - val inst = SampleJBigInteger( - JBigInteger.ZERO, - JBigInteger.ONE, - JBigInteger.TEN, - new JBigInteger("-90182736451928374653345"), - new JBigInteger("90182736451928374653345"), - new JBigInteger("0"), - null - ) - val js = sj.render(inst) - assertEquals( - """{"bi1":0,"bi2":1,"bi3":10,"bi4":-90182736451928374653345,"bi5":90182736451928374653345,"bi6":0,"bi7":null}""".asInstanceOf[JSON], - js) - assertEquals(inst, sj.read[SampleJBigInteger](js)) - } - - test("Boolean must work") { - val inst = - SampleJBoolean(JBoolean.TRUE, JBoolean.FALSE, true, false, null) - val js = sj.render(inst) - assertEquals( - """{"bool1":true,"bool2":false,"bool3":true,"bool4":false,"bool5":null}""".asInstanceOf[JSON], - js) - assertEquals(inst, sj.read[SampleJBoolean](js)) - } - - test("Byte must work") { - val inst = SampleJByte( - JByte.MAX_VALUE, - JByte.MIN_VALUE, - 0.asInstanceOf[Byte], - 64.asInstanceOf[Byte], - null - ) - val js = sj.render(inst) - assertEquals("""{"b1":127,"b2":-128,"b3":0,"b4":64,"b5":null}""".asInstanceOf[JSON], js ) - assertEquals(inst, sj.read[SampleJByte](js)) - } - - test("Char must work") { - val inst = SampleJChar('Z', '\u20A0', null) - val js = sj.render(inst) - assertEquals(("""{"c1":"Z","c2":"\""" + """u20a0","c3":null}""").asInstanceOf[JSON], js ) - assertEquals(inst, sj.read[SampleJChar](js)) - } - - test("Double must work") { - val inst = SampleJDouble( - JDouble.MAX_VALUE, - JDouble.MIN_VALUE, - 0.0, - -123.4567, - null - ) - val js = sj.render(inst) - assertEquals( - """{"d1":1.7976931348623157E308,"d2":4.9E-324,"d3":0.0,"d4":-123.4567,"d5":null}""".asInstanceOf[JSON], - js) - assertEquals(inst, sj.read[SampleJDouble](js)) - } - - test("Float must work") { - val inst = SampleJFloat( - JFloat.MAX_VALUE, - JFloat.MIN_VALUE, - 0.0F, - -123.4567F, - null - ) - val js = sj.render(inst) - assertEquals( - """{"f1":3.4028235E38,"f2":1.4E-45,"f3":0.0,"f4":-123.4567,"f5":null}""".asInstanceOf[JSON], - js) - assertEquals(inst, sj.read[SampleJFloat](js)) - } - - test("Int must work") { - val inst = SampleJInt(JInt.MAX_VALUE, JInt.MIN_VALUE, 0, 123, null) - val js = sj.render(inst) - assertEquals( - """{"i1":2147483647,"i2":-2147483648,"i3":0,"i4":123,"i5":null}""".asInstanceOf[JSON], - js) - assertEquals(inst, sj.read[SampleJInt](js)) - } - - test("Long must work") { - val inst = SampleJLong(JLong.MAX_VALUE, JLong.MIN_VALUE, 0L, 123L, null) - val js = sj.render(inst) - assertEquals( - """{"l1":9223372036854775807,"l2":-9223372036854775808,"l3":0,"l4":123,"l5":null}""".asInstanceOf[JSON], - js) - assertEquals(inst, sj.read[SampleJLong](js)) - } - - test("Number must work") { - val inst = SampleJNumber( - JByte.valueOf("-128"), - JByte.valueOf("127"), - JShort.valueOf("-32768"), - JShort.valueOf("32767"), - JInt.valueOf("-2147483648"), - JInt.valueOf("2147483647"), - JLong.valueOf("-9223372036854775808"), - JLong.valueOf("9223372036854755807"), - null, //new JBigInteger("9923372036854755810"), - JByte.valueOf("0"), - JFloat.valueOf("3.4e-038"), - JFloat.valueOf("3.4e+038"), - JDouble.valueOf("1.7e-308"), - JDouble.valueOf("1.7e+308"), - null, //new JBigDecimal("1.8e+308"), - JFloat.valueOf("0.0"), - null - ) - val js = sj.render(inst) - assertEquals( - """{"n1":-128,"n2":127,"n3":-32768,"n4":32767,"n5":-2147483648,"n6":2147483647,"n7":-9223372036854775808,"n8":9223372036854755807,"n9":null,"n10":0,"n11":3.4E-38,"n12":3.4E38,"n13":1.7E-308,"n14":1.7E308,"n15":null,"n16":0.0,"n17":null}""".asInstanceOf[JSON], - js) - assertEquals(inst, sj.read[SampleJNumber](js)) - } - - test("Short must work") { - val inst = SampleJShort( - JShort.MAX_VALUE, - JShort.MIN_VALUE, - 0.asInstanceOf[Short], - 123.asInstanceOf[Short], - null - ) - val js = sj.render(inst) - assertEquals("""{"s1":32767,"s2":-32768,"s3":0,"s4":123,"s5":null}""".asInstanceOf[JSON], js) - assertEquals(inst, sj.read[SampleJShort](js)) - } - - - //-------------------------------------------------------- - - - test("BigDecimal must break") { - describe("--- Negative Tests ---") - val js = - """{"bd1":0,"bd2":1,"bd3":10,"bd4":"0.1499999999999999944488848768742172978818416595458984375","bd5":null}""".asInstanceOf[JSON] - val msg = - """Expected a Number here - |{"bd1":0,"bd2":1,"bd3":10,"bd4":"0.149999999999999994448884876874217297881841... - |--------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleJBigDecimal](js) - } - } - - test("BigInt must break") { - val js = - """{"bi1":"0","bi2":1,"bi3":10,"bi4":-90182736451928374653345,"bi5":90182736451928374653345,"bi6":0,"bi7":null}""".asInstanceOf[JSON] - val msg = - """Expected a Number here - |{"bi1":"0","bi2":1,"bi3":10,"bi4":-90182736451928374653345,"bi5":901827364519... - |-------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleJBigInteger](js) - } - } - - test("Boolean must break") { - val js = """{"bool1":true,"bool2":false,"bool3":true,"bool4":"false","bool5":null}""".asInstanceOf[JSON] - val msg = - """Expected a Boolean here - |{"bool1":true,"bool2":false,"bool3":true,"bool4":"false","bool5":null} - |-------------------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleJBoolean](js) - } - } - - test("Byte must break") { - val js = """{"b1":127,"b2":-128,"b3":false,"b4":64,"b5":null}""".asInstanceOf[JSON] - val msg = """Expected a Number here - |{"b1":127,"b2":-128,"b3":false,"b4":64,"b5":null} - |-------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleJByte](js) - } - } - - test("Char must break") { - val js = """{"c1":"Z","c2":3,"c3":null}""".asInstanceOf[JSON] - val msg = """Expected a String here - |{"c1":"Z","c2":3,"c3":null} - |---------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleJChar](js) - } - val js2 = """{"c1":"Z","c2":"","c3":null}""".asInstanceOf[JSON] - val msg2 = """Tried to read a Character but empty string found - |{"c1":"Z","c2":"","c3":null} - |----------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ - sj.read[SampleJChar](js2) - } - } - - test("Double must break") { - val js = - """{"d1":1.7976931348623157E308,"d2":4.9E-324,"d3":"0.0","d4":-123.4567,"d5":null}""".asInstanceOf[JSON] - val msg = - """Expected a Number here - |{"d1":1.7976931348623157E308,"d2":4.9E-324,"d3":"0.0","d4":-123.4567,"d5":null} - |------------------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleJDouble](js) - } - } - - test("Float must break") { - val js = - """{"f1":3.4028235E38,"f2":"1.4E-45","f3":0.0,"f4":-123.4567,"f5":null}""".asInstanceOf[JSON] - val msg = - """Expected a Number here - |{"f1":3.4028235E38,"f2":"1.4E-45","f3":0.0,"f4":-123.4567,"f5":null} - |------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleJFloat](js) - } - } - - test("Int must break") { - val js = """{"i1":2147483647,"i2":-2147483648,"i3":false,"i4":123,"i5":null}""".asInstanceOf[JSON] - val msg = - """Expected a Number here - |{"i1":2147483647,"i2":-2147483648,"i3":false,"i4":123,"i5":null} - |---------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleJInt](js) - } - val js2 = """{"i1":2147483647,"i2":-2147483648,"i3":0.3,"i4":123,"i5":null}""".asInstanceOf[JSON] - interceptMessage[java.lang.NumberFormatException]("For input string: \"0.3\""){ - sj.read[SampleJInt](js2) - } - } - - test("Long must break") { - val js = - """{"l1":9223372036854775807,"l2":-9223372036854775808,"l3":"0","l4":123,"l5":null}""".asInstanceOf[JSON] - val msg = - """Expected a Number here - |...23372036854775807,"l2":-9223372036854775808,"l3":"0","l4":123,"l5":null} - |----------------------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleJLong](js) - } - val js2 = - """{"l1":9223372036854775807,"l2":-9223372036854775808,"l3":0.3,"l4":123,"l5":null}""".asInstanceOf[JSON] - interceptMessage[java.lang.NumberFormatException]("For input string: \"0.3\""){ - sj.read[SampleJLong](js2) - } - } - - test("Number must break") { - val js = """{"n1":-128,"n2":127,"n3":"-32768","n4":32767,"n5":-2147483648,"n6":2147483647,"n7":-9223372036854775808,"n8":9223372036854755807,"n9":9923372036854755810,"n10":0,"n11":3.4E-38,"n12":3.4E38,"n13":1.7E-308,"n14":1.7E308,"n15":1.8E+308,"n16":0.0,"n17":null}""".asInstanceOf[JSON] - val msg = - """Expected a Number here - |{"n1":-128,"n2":127,"n3":"-32768","n4":32767,"n5":-2147483648,"n6":2147483647... - |-------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleJNumber](js) - } - } - - test("Short must break") { - val js = """{"s1":false,"s2":-32768,"s3":0,"s4":123,"s5":null}""".asInstanceOf[JSON] - val msg = """Expected a Number here - |{"s1":false,"s2":-32768,"s3":0,"s4":123,"s5":null} - |------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleJShort](js) - } - val js2 = """{"s1":2.3,"s2":-32768,"s3":0,"s4":123,"s5":null}""".asInstanceOf[JSON] - interceptMessage[java.lang.NumberFormatException]("For input string: \"2.3\""){ - sj.read[SampleJShort](js2) - } - } diff --git a/src_old/test/scala/co.blocke.scalajack/json/primitives/Model.scala b/src_old/test/scala/co.blocke.scalajack/json/primitives/Model.scala deleted file mode 100644 index 451c9854..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json/primitives/Model.scala +++ /dev/null @@ -1,197 +0,0 @@ -package co.blocke.scalajack -package json.primitives - -import java.util.UUID -import java.lang.{ - Boolean => JBoolean, - Byte => JByte, - Character => JChar, - Double => JDouble, - Float => JFloat, - Integer => JInt, - Long => JLong, - Number => JNumber, - Short => JShort -} -import java.math.{ BigDecimal => JBigDecimal, BigInteger => JBigInteger } -import java.time._ -import scala.math._ - -// === Scala -case class SampleBigDecimal( - bd1: BigDecimal, - bd2: BigDecimal, - bd3: BigDecimal, - bd4: BigDecimal, - bd5: BigDecimal, - bd6: BigDecimal) -case class SampleBigInt(bi1: BigInt, bi2: BigInt, bi3: BigInt, bi4: BigInt) -case class SampleBinary(b1: Array[Byte], b2: Array[Byte]) -case class SampleBoolean(bool1: Boolean, bool2: Boolean) -case class SampleByte(b1: Byte, b2: Byte, b3: Byte, b4: Byte) -case class SampleChar(c1: Char, c2: Char, c3: Char) -case class SampleDouble(d1: Double, d2: Double, d3: Double, d4: Double) - -object Size extends Enumeration { - val Small, Medium, Large = Value -} -object SizeWithType extends Enumeration { - type SizeWithType = Value - val Little, Grand = Value -} -import SizeWithType._ -case class SampleEnum( - e1: Size.Value, - e2: Size.Value, - e3: Size.Value, - e4: Size.Value, - e5: Size.Value, - e6: SizeWithType) - -enum Color { - case Red, Blue, Green -} -case class TVColors( color1: Color, color2: Color ) - -sealed trait Flavor -case object Vanilla extends Flavor -case object Chocolate extends Flavor -case object Bourbon extends Flavor - -sealed trait Vehicle -case class Truck(numberOfWheels: Int) extends Vehicle -case class Car(numberOfWheels: Int, color: String) extends Vehicle -case class Plane(numberOfEngines: Int) extends Vehicle - -case class Ride( wheels: Vehicle ) -case class Favorite( flavor: Flavor ) - -case class SampleFloat(f1: Float, f2: Float, f3: Float, f4: Float) -case class SampleInt(i1: Int, i2: Int, i3: Int, i4: Int) -case class SampleLong(l1: Long, l2: Long, l3: Long, l4: Long) -case class SampleShort(s1: Short, s2: Short, s3: Short, s4: Short) -case class SampleString(s1: String, s2: String, s3: String) - -// === Java -case class SampleJBigDecimal( - bd1: JBigDecimal, - bd2: JBigDecimal, - bd3: JBigDecimal, - bd4: JBigDecimal, - bd5: JBigDecimal) -case class SampleJBigInteger( - bi1: JBigInteger, - bi2: JBigInteger, - bi3: JBigInteger, - bi4: JBigInteger, - bi5: JBigInteger, - bi6: JBigInteger, - bi7: JBigInteger) -case class SampleJBoolean( - bool1: JBoolean, - bool2: JBoolean, - bool3: JBoolean, - bool4: JBoolean, - bool5: JBoolean) -case class SampleJByte(b1: JByte, b2: JByte, b3: JByte, b4: JByte, b5: JByte) -case class SampleJChar(c1: JChar, c2: JChar, c3: JChar) -case class SampleJDouble( - d1: JDouble, - d2: JDouble, - d3: JDouble, - d4: JDouble, - d5: JDouble) -case class SampleJFloat( - f1: JFloat, - f2: JFloat, - f3: JFloat, - f4: JFloat, - f5: JFloat) -case class SampleJInt(i1: JInt, i2: JInt, i3: JInt, i4: JInt, i5: JInt) -case class SampleJLong(l1: JLong, l2: JLong, l3: JLong, l4: JLong, l5: JLong) -case class SampleJNumber( - n1: JNumber, - n2: JNumber, - n3: JNumber, - n4: JNumber, - n5: JNumber, - n6: JNumber, - n7: JNumber, - n8: JNumber, - n9: JNumber, - n10: JNumber, - n11: JNumber, - n12: JNumber, - n13: JNumber, - n14: JNumber, - n15: JNumber, - n16: JNumber, - n17: JNumber) -case class SampleJShort( - s1: JShort, - s2: JShort, - s3: JShort, - s4: JShort, - s5: JShort) -case class SampleUUID(u1: UUID, u2: UUID) - -// === Java Time -case class SampleDuration(d1: Duration, d2: Duration, d3: Duration) -case class SampleInstant( - i1: Instant, - i2: Instant, - i3: Instant, - i4: Instant, - i5: Instant) -case class SampleLocalDateTime( - d1: LocalDateTime, - d2: LocalDateTime, - d3: LocalDateTime, - d4: LocalDateTime) -case class SampleLocalDate( - d1: LocalDate, - d2: LocalDate, - d3: LocalDate, - d4: LocalDate) -case class SampleLocalTime( - d1: LocalTime, - d2: LocalTime, - d3: LocalTime, - d4: LocalTime, - d5: LocalTime, - d6: LocalTime) -case class SampleOffsetDateTime( - o1: OffsetDateTime, - o2: OffsetDateTime, - o3: OffsetDateTime, - o4: OffsetDateTime) -case class SampleOffsetTime( - o1: OffsetTime, - o2: OffsetTime, - o3: OffsetTime, - o4: OffsetTime) -case class SamplePeriod(p1: Period, p2: Period, p3: Period) -case class SampleZonedDateTime(o1: ZonedDateTime, o2: ZonedDateTime) - -// === Any primitives -case class AnyShell(a: Any) - -// === Value Classes -case class VCBigDecimal(vc: BigDecimal) extends AnyVal -case class VCBigInt(vc: BigInt) extends AnyVal -case class VCBoolean(vc: Boolean) extends AnyVal -case class VCByte(vc: Byte) extends AnyVal -case class VCChar(vc: Char) extends AnyVal -case class VCDouble(vc: Double) extends AnyVal -case class VCEnum(vc: Color) extends AnyVal -case class VCEnumeration(vc: Size.Value) extends AnyVal -case class VCFloat(vc: Float) extends AnyVal -case class VCInt(vc: Int) extends AnyVal -case class VCLong(vc: Long) extends AnyVal -case class VCShort(vc: Short) extends AnyVal -case class VCString(vc: String) extends AnyVal -case class VCUUID(vc: UUID) extends AnyVal -case class VCNumber(vc: Number) extends AnyVal - -// === Permissives test -case class Holder[T](value: T) \ No newline at end of file diff --git a/src_old/test/scala/co.blocke.scalajack/json/primitives/PermissivePrimitives.scala b/src_old/test/scala/co.blocke.scalajack/json/primitives/PermissivePrimitives.scala deleted file mode 100644 index d2e27d3f..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json/primitives/PermissivePrimitives.scala +++ /dev/null @@ -1,233 +0,0 @@ -package co.blocke.scalajack -package json.primitives - -import TestUtil._ -import munit._ -import munit.internal.console -import co.blocke.scalajack.json.JSON -import co.blocke.scala_reflection._ - -class PermissivePrimitives() extends FunSuite: - - val sj = co.blocke.scalajack.ScalaJack().allowPermissivePrimitives() - - test("Boolean must work") { - describe("--------------------------------\n: Permissive Primitive Tests :\n--------------------------------", Console.BLUE) - - assertEquals(Holder(true), sj.read[Holder[Boolean]]("""{"value":true}""".asInstanceOf[JSON])) - assertEquals(Holder(true), sj.read[Holder[Boolean]]("""{"value":"true"}""".asInstanceOf[JSON])) - assertEquals(Holder(false), sj.read[Holder[Boolean]]("""{"value":false}""".asInstanceOf[JSON])) - assertEquals(Holder(false), sj.read[Holder[Boolean]]("""{"value":"false"}""".asInstanceOf[JSON])) - assertEquals(Holder(java.lang.Boolean.TRUE), sj.read[Holder[java.lang.Boolean]]("""{"value":true}""".asInstanceOf[JSON])) - assertEquals(Holder(java.lang.Boolean.TRUE), sj.read[Holder[java.lang.Boolean]]("""{"value":"true"}""".asInstanceOf[JSON])) - assertEquals(Holder(java.lang.Boolean.FALSE), sj.read[Holder[java.lang.Boolean]]("""{"value":false}""".asInstanceOf[JSON])) - assertEquals(Holder(java.lang.Boolean.FALSE), sj.read[Holder[java.lang.Boolean]]("""{"value":"false"}""".asInstanceOf[JSON])) - assertEquals(Holder[java.lang.Boolean](null), sj.read[Holder[java.lang.Boolean]]("""{"value":null}""".asInstanceOf[JSON])) - - val msg = """Expected a java.lang.Boolean here - |{"value":""} - |----------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[Holder[java.lang.Boolean]]("""{"value":""}""".asInstanceOf[JSON]) - } - - assertEquals("""{"value":true}""".asInstanceOf[JSON], sj.render(Holder(true))) - assertEquals("""{"value":false}""".asInstanceOf[JSON], sj.render(Holder(false))) - assertEquals("""{"value":true}""".asInstanceOf[JSON], sj.render(Holder(java.lang.Boolean.TRUE))) - assertEquals("""{"value":false}""".asInstanceOf[JSON], sj.render(Holder(java.lang.Boolean.FALSE))) - assertEquals("""{"value":null}""".asInstanceOf[JSON], sj.render(Holder[java.lang.Boolean](null))) - } - - test("Byte must work") { - assertEquals(Holder(42.toByte), sj.read[Holder[Byte]]("""{"value":42}""".asInstanceOf[JSON])) - assertEquals(Holder(42.toByte), sj.read[Holder[Byte]]("""{"value":"42"}""".asInstanceOf[JSON])) - assertEquals(Holder(java.lang.Byte.valueOf(42.toByte)), sj.read[Holder[java.lang.Byte]]("""{"value":42}""".asInstanceOf[JSON])) - assertEquals(Holder(java.lang.Byte.valueOf(42.toByte)), sj.read[Holder[java.lang.Byte]]("""{"value":"42"}""".asInstanceOf[JSON])) - // assertEquals(Holder[java.lang.Byte](null), sj.read[Holder[java.lang.Byte]]("""{"value":null}""".asInstanceOf[JSON])) - - val msg = """Expected a java.lang.Byte here - |{"value":""} - |----------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[Holder[java.lang.Byte]]("""{"value":""}""".asInstanceOf[JSON]) - } - - assertEquals("""{"value":42}""".asInstanceOf[JSON], sj.render(Holder(42.toByte))) - assertEquals("""{"value":42}""".asInstanceOf[JSON], sj.render(Holder(java.lang.Byte.valueOf(42.toByte)))) - assertEquals("""{"value":null}""".asInstanceOf[JSON], sj.render(Holder[java.lang.Byte](null))) - } - - test("Double must work") { - assertEquals(Holder(42.5), sj.read[Holder[Double]]("""{"value":42.5}""".asInstanceOf[JSON])) - assertEquals(Holder(42.5), sj.read[Holder[Double]]("""{"value":"42.5"}""".asInstanceOf[JSON])) - assertEquals(Holder(java.lang.Double.valueOf(42.5)), sj.read[Holder[java.lang.Double]]("""{"value":42.5}""".asInstanceOf[JSON])) - assertEquals(Holder(java.lang.Double.valueOf(42.5)), sj.read[Holder[java.lang.Double]]("""{"value":"42.5"}""".asInstanceOf[JSON])) - // assertEquals(Holder[java.lang.Double](null), sj.read[Holder[java.lang.Double]]("""{"value":null}""".asInstanceOf[JSON])) - - val msg = """Expected a java.lang.Double here - |{"value":""} - |----------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[Holder[java.lang.Double]]("""{"value":""}""".asInstanceOf[JSON]) - } - - assertEquals("""{"value":42.5}""".asInstanceOf[JSON], sj.render(Holder(42.5))) - assertEquals("""{"value":42.5}""".asInstanceOf[JSON], sj.render(Holder(java.lang.Double.valueOf(42.5)))) - assertEquals("""{"value":null}""".asInstanceOf[JSON], sj.render(Holder[java.lang.Double](null))) - } - - test("Float must work") { - assertEquals(Holder(42.5.toFloat), sj.read[Holder[Float]]("""{"value":42.5}""".asInstanceOf[JSON])) - assertEquals(Holder(42.5.toFloat), sj.read[Holder[Float]]("""{"value":"42.5"}""".asInstanceOf[JSON])) - assertEquals(Holder(java.lang.Float.valueOf(42.5.toFloat)), sj.read[Holder[java.lang.Float]]("""{"value":42.5}""".asInstanceOf[JSON])) - assertEquals(Holder(java.lang.Float.valueOf(42.5.toFloat)), sj.read[Holder[java.lang.Float]]("""{"value":"42.5"}""".asInstanceOf[JSON])) - // assertEquals(Holder[java.lang.Float](null), sj.read[Holder[java.lang.Float]]("""{"value":null}""".asInstanceOf[JSON])) - - val msg = """Expected a java.lang.Float here - |{"value":""} - |----------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[Holder[java.lang.Float]]("""{"value":""}""".asInstanceOf[JSON]) - } - - assertEquals("""{"value":42.5}""".asInstanceOf[JSON], sj.render(Holder(42.5.toFloat))) - assertEquals("""{"value":42.5}""".asInstanceOf[JSON], sj.render(Holder(java.lang.Float.valueOf(42.5.toFloat)))) - assertEquals("""{"value":null}""".asInstanceOf[JSON], sj.render(Holder[java.lang.Float](null))) - } - - test("Int must work") { - assertEquals(Holder(42), sj.read[Holder[Int]]("""{"value":42}""".asInstanceOf[JSON])) - assertEquals(Holder(42), sj.read[Holder[Int]]("""{"value":"42"}""".asInstanceOf[JSON])) - assertEquals(Holder(java.lang.Integer.valueOf(42)), sj.read[Holder[java.lang.Integer]]("""{"value":42}""".asInstanceOf[JSON])) - assertEquals(Holder(java.lang.Integer.valueOf(42)), sj.read[Holder[java.lang.Integer]]("""{"value":"42"}""".asInstanceOf[JSON])) - // assertEquals(Holder[java.lang.Integer](null), sj.read[Holder[java.lang.Integer]]("""{"value":null}""".asInstanceOf[JSON])) - - val msg = """Expected a java.lang.Integer here - |{"value":""} - |----------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[Holder[java.lang.Integer]]("""{"value":""}""".asInstanceOf[JSON]) - } - - assertEquals("""{"value":42}""".asInstanceOf[JSON], sj.render(Holder(42))) - assertEquals("""{"value":42}""".asInstanceOf[JSON], sj.render(Holder(java.lang.Integer.valueOf(42)))) - assertEquals("""{"value":null}""".asInstanceOf[JSON], sj.render(Holder[java.lang.Integer](null))) - } - - test("Long must work") { - assertEquals(Holder(42.toLong), sj.read[Holder[Long]]("""{"value":42}""".asInstanceOf[JSON])) - assertEquals(Holder(42.toLong), sj.read[Holder[Long]]("""{"value":"42"}""".asInstanceOf[JSON])) - assertEquals(Holder(java.lang.Long.valueOf(42.toLong)), sj.read[Holder[java.lang.Long]]("""{"value":42}""".asInstanceOf[JSON])) - assertEquals(Holder(java.lang.Long.valueOf(42.toLong)), sj.read[Holder[java.lang.Long]]("""{"value":"42"}""".asInstanceOf[JSON])) - // assertEquals(Holder[java.lang.Long](null), sj.read[Holder[java.lang.Long]]("""{"value":null}""".asInstanceOf[JSON])) - - val msg = """Expected a java.lang.Long here - |{"value":""} - |----------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[Holder[java.lang.Long]]("""{"value":""}""".asInstanceOf[JSON]) - } - - assertEquals("""{"value":42}""".asInstanceOf[JSON], sj.render(Holder(42.toLong))) - assertEquals("""{"value":42}""".asInstanceOf[JSON], sj.render(Holder(java.lang.Long.valueOf(42.toLong)))) - assertEquals("""{"value":null}""".asInstanceOf[JSON], sj.render(Holder[java.lang.Long](null))) - } - - test("Short must work") { - assertEquals(Holder(42.toShort), sj.read[Holder[Short]]("""{"value":42}""".asInstanceOf[JSON])) - assertEquals(Holder(42.toShort), sj.read[Holder[Short]]("""{"value":"42"}""".asInstanceOf[JSON])) - assertEquals(Holder(java.lang.Short.valueOf(42.toShort)), sj.read[Holder[java.lang.Short]]("""{"value":42}""".asInstanceOf[JSON])) - assertEquals(Holder(java.lang.Short.valueOf(42.toShort)), sj.read[Holder[java.lang.Short]]("""{"value":"42"}""".asInstanceOf[JSON])) - // assertEquals(Holder[java.lang.Short](null), sj.read[Holder[java.lang.Short]]("""{"value":null}""".asInstanceOf[JSON])) - - val msg = """Expected a java.lang.Short here - |{"value":""} - |----------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[Holder[java.lang.Short]]("""{"value":""}""".asInstanceOf[JSON]) - } - - assertEquals("""{"value":42}""".asInstanceOf[JSON], sj.render(Holder(42.toShort))) - assertEquals("""{"value":42}""".asInstanceOf[JSON], sj.render(Holder(java.lang.Short.valueOf(42.toShort)))) - assertEquals("""{"value":null}""".asInstanceOf[JSON], sj.render(Holder[java.lang.Short](null))) - } - - test("Scala BigInt must work") { - assertEquals(Holder(BigInt(42)), sj.read[Holder[BigInt]]("""{"value":42}""".asInstanceOf[JSON])) - assertEquals(Holder(BigInt(42)), sj.read[Holder[BigInt]]("""{"value":"42"}""".asInstanceOf[JSON])) - assertEquals(Holder[BigInt](null), sj.read[Holder[BigInt]]("""{"value":null}""".asInstanceOf[JSON])) - - val msg = """Expected a scala.math.BigInt here - |{"value":""} - |----------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[Holder[BigInt]]("""{"value":""}""".asInstanceOf[JSON]) - } - - assertEquals("""{"value":42}""".asInstanceOf[JSON], sj.render(Holder(BigInt(42)))) - assertEquals("""{"value":null}""".asInstanceOf[JSON], sj.render(Holder[BigInt](null))) - } - - test("Java BigInteger must work") { - assertEquals(Holder(java.math.BigInteger.valueOf(42)), sj.read[Holder[java.math.BigInteger]]("""{"value":42}""".asInstanceOf[JSON])) - assertEquals(Holder(java.math.BigInteger.valueOf(42)), sj.read[Holder[java.math.BigInteger]]("""{"value":"42"}""".asInstanceOf[JSON])) - // assertEquals(Holder[java.math.BigInteger](null), sj.read[Holder[java.math.BigInteger]]("""{"value":null}""".asInstanceOf[JSON])) - - val msg = """Expected a java.math.BigInteger here - |{"value":""} - |----------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[Holder[java.math.BigInteger]]("""{"value":""}""".asInstanceOf[JSON]) - } - - assertEquals("""{"value":42}""".asInstanceOf[JSON], sj.render(Holder(java.math.BigInteger.valueOf(42)))) - assertEquals("""{"value":null}""".asInstanceOf[JSON], sj.render(Holder[java.math.BigInteger](null))) - } - - test("Scala BigDecimal must work") { - assertEquals(Holder(BigDecimal("12.34")), sj.read[Holder[BigDecimal]]("""{"value":12.34}""".asInstanceOf[JSON])) - assertEquals(Holder(BigDecimal("12.34")), sj.read[Holder[BigDecimal]]("""{"value":"12.34"}""".asInstanceOf[JSON])) - // assertEquals(Holder[BigDecimal](null), sj.read[Holder[BigDecimal]]("""{"value":null}""".asInstanceOf[JSON])) - - val msg = """Expected a scala.math.BigDecimal here - |{"value":""} - |----------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[Holder[BigDecimal]]("""{"value":""}""".asInstanceOf[JSON]) - } - - assertEquals("""{"value":12.34}""".asInstanceOf[JSON], sj.render(Holder(BigDecimal("12.34")))) - assertEquals("""{"value":null}""".asInstanceOf[JSON], sj.render(Holder[BigDecimal](null))) - } - - test("Java BigDecimal must work") { - assertEquals(Holder(new java.math.BigDecimal("12.34")), sj.read[Holder[java.math.BigDecimal]]("""{"value":12.34}""".asInstanceOf[JSON])) - assertEquals(Holder(new java.math.BigDecimal("12.34")), sj.read[Holder[java.math.BigDecimal]]("""{"value":"12.34"}""".asInstanceOf[JSON])) - // assertEquals(Holder[java.math.BigDecimal](null), sj.read[Holder[java.math.BigDecimal]]("""{"value":null}""".asInstanceOf[JSON])) - - val msg = """Expected a java.math.BigDecimal here - |{"value":""} - |----------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[Holder[java.math.BigDecimal]]("""{"value":""}""".asInstanceOf[JSON]) - } - - assertEquals("""{"value":12.34}""".asInstanceOf[JSON], sj.render(Holder(new java.math.BigDecimal("12.34")))) - assertEquals("""{"value":null}""".asInstanceOf[JSON], sj.render(Holder[java.math.BigDecimal](null))) - } - - test("Java Number must work") { - assertEquals(Holder(java.lang.Double.valueOf(42.5).asInstanceOf[Number]), sj.read[Holder[java.lang.Number]]("""{"value":42.5}""".asInstanceOf[JSON])) - assertEquals(Holder(java.lang.Double.valueOf(42.5).asInstanceOf[Number]), sj.read[Holder[java.lang.Number]]("""{"value":"42.5"}""".asInstanceOf[JSON])) - // assertEquals(Holder[java.lang.Number](null), sj.read[Holder[java.lang.Number]]("""{"value":null}""".asInstanceOf[JSON])) - - val msg = """Expected a java.lang.Number here - |{"value":""} - |----------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[Holder[java.lang.Number]]("""{"value":""}""".asInstanceOf[JSON]) - } - - assertEquals("""{"value":42.5}""".asInstanceOf[JSON], sj.render(Holder(java.lang.Double.valueOf(42.5).asInstanceOf[Number]))) - assertEquals("""{"value":null}""".asInstanceOf[JSON], sj.render(Holder[java.lang.Number](null))) - } \ No newline at end of file diff --git a/src_old/test/scala/co.blocke.scalajack/json/primitives/ScalaPrim.scala b/src_old/test/scala/co.blocke.scalajack/json/primitives/ScalaPrim.scala deleted file mode 100644 index ec405651..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json/primitives/ScalaPrim.scala +++ /dev/null @@ -1,334 +0,0 @@ -package co.blocke.scalajack -package json.primitives - -import co.blocke.scala_reflection._ -import scala.math.BigDecimal -import java.util.UUID -import TestUtil._ -import munit._ -import munit.internal.console -import co.blocke.scalajack.json.JSON - -class ScalaPrim() extends FunSuite: - - val sj = co.blocke.scalajack.ScalaJack() - - test("BigDecimal must work") { - describe("---------------------------\n: Scala Primitive Tests :\n---------------------------", Console.BLUE) - describe("+++ Positive Tests +++") - - val inst = SampleBigDecimal( - BigDecimal(123L), - BigDecimal(1.23), - BigDecimal(0), - BigDecimal("123.456"), - BigDecimal( - "0.1499999999999999944488848768742172978818416595458984375" - ), - null - ) - - val js = sj.render(inst) - assertEquals( - """{"bd1":123,"bd2":1.23,"bd3":0,"bd4":123.456,"bd5":0.1499999999999999944488848768742172978818416595458984375,"bd6":null}""".asInstanceOf[JSON], - js - ) - assertEquals(inst, sj.read[SampleBigDecimal](js)) - } - - test("BigInt must work") { - val inst = SampleBigInt( - BigInt("-90182736451928374653345"), - BigInt("90182736451928374653345"), - BigInt(0), - null - ) - val js = sj.render(inst) - assertEquals( - """{"bi1":-90182736451928374653345,"bi2":90182736451928374653345,"bi3":0,"bi4":null}""".asInstanceOf[JSON], - js - ) - assertEquals(inst, sj.read[SampleBigInt](js)) - } - - test("Binary must work") { - val inst = SampleBinary( - null, - hexStringToByteArray("e04fd020ea3a6910a2d808002b30309d") - ) - val js = sj.render(inst) - // assertEquals("""{"b1":null,"b2":"4E/QIOo6aRCi2AgAKzAwnQ=="}""".asInstanceOf[JSON], js) - val inst2 = sj.read[SampleBinary](js) - assert(null == inst2.b1) - assertEquals(inst.b2.toList, inst2.b2.toList) - } - - test("Boolean must work (not nullable)") { - val inst = SampleBoolean(bool1 = true, bool2 = false) - val js = sj.render(inst) - assertEquals("""{"bool1":true,"bool2":false}""".asInstanceOf[JSON], js) - assertEquals(inst, sj.read[SampleBoolean](js)) - } - - test("Byte must work (not nullable)") { - val inst = SampleByte(Byte.MaxValue, Byte.MinValue, 0, 64) - val js = sj.render(inst) - assertEquals("""{"b1":127,"b2":-128,"b3":0,"b4":64}""".asInstanceOf[JSON], js ) - assertEquals(inst, sj.read[SampleByte](js)) - } - - test("Char must work (not nullable)") { - val inst = SampleChar(Char.MaxValue, 'Z', '\u20A0') - val js = sj.render(inst) - assertEquals( - ("""{"c1":"\""" + """uffff","c2":"Z","c3":"\""" + """u20a0"}""").asInstanceOf[JSON], - js) - assertEquals(inst, sj.read[SampleChar](js)) - } - - test("Double must work (not nullable)") { - val inst = - SampleDouble(Double.MaxValue, Double.MinValue, 0.0, -123.4567) - val js = sj.render(inst) - assertEquals( - """{"d1":1.7976931348623157E308,"d2":-1.7976931348623157E308,"d3":0.0,"d4":-123.4567}""".asInstanceOf[JSON], - js) - assertEquals(inst, sj.read[SampleDouble](js)) - } - - test("Float must work") { - val inst = SampleFloat(Float.MaxValue, Float.MinValue, 0.0F, -123.4567F) - val js = sj.render(inst) - assertEquals( - """{"f1":3.4028235E38,"f2":-3.4028235E38,"f3":0.0,"f4":-123.4567}""".asInstanceOf[JSON], - js) - assertEquals(inst, sj.read[SampleFloat](js)) - } - test("Int must work (not nullable)") { - val inst = SampleInt(Int.MaxValue, Int.MinValue, 0, 123) - val js = sj.render(inst) - assertEquals("""{"i1":2147483647,"i2":-2147483648,"i3":0,"i4":123}""".asInstanceOf[JSON], js) - assertEquals(inst, sj.read[SampleInt](js)) - } - test("Long must work (not nullable)") { - val inst = SampleLong(Long.MaxValue, Long.MinValue, 0L, 123L) - val js = sj.render(inst) - assertEquals( - """{"l1":9223372036854775807,"l2":-9223372036854775808,"l3":0,"l4":123}""".asInstanceOf[JSON], - js) - assertEquals(inst, sj.read[SampleLong](js)) - } - test("Short must work (not nullable)") { - val inst = SampleShort(Short.MaxValue, Short.MinValue, 0, 123) - val js = sj.render(inst) - assertEquals("""{"s1":32767,"s2":-32768,"s3":0,"s4":123}""".asInstanceOf[JSON], js ) - assertEquals(inst, sj.read[SampleShort](js)) - } - test("String must work") { - val inst = SampleString("something\b\n\f\r\t☆", "", null) - val js = sj.render(inst) - // The weird '+' here is to break up the unicode so it won't be interpreted and wreck the test. - assertEquals( - ("""{"s1":"something\b\n\f\r\t\""" + """u2606","s2":"","s3":null}""").asInstanceOf[JSON], - js) - assertEquals(inst, sj.read[SampleString](js)) - } - - test("UUID must work") { - val inst = SampleUUID( - null, - UUID.fromString("580afe0d-81c0-458f-9e09-4486c7af0fe9") - ) - val js = sj.render(inst) - assertEquals( - """{"u1":null,"u2":"580afe0d-81c0-458f-9e09-4486c7af0fe9"}""".asInstanceOf[JSON], - js) - assertEquals(inst, sj.read[SampleUUID](js)) - } - - - //-------------------------------------------------------- - - - test("BigDecimal must break") { - describe("--- Negative Tests ---") - val js = - """{"bd1":123,"bd2":1.23,"bd3":0,"bd4":123.456,"bd5":"0.1499999999999999944488848768742172978818416595458984375","bd6":null}""".asInstanceOf[JSON] - val msg = - """Expected a Number here - |{"bd1":123,"bd2":1.23,"bd3":0,"bd4":123.456,"bd5":"0.149999999999999994448884... - |--------------------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleBigDecimal](js) - } - } - - test("BigInt must break") { - val js = - """{"bi1":"-90182736451928374653345","bi2":90182736451928374653345,"bi3":0,"bi4":null}""".asInstanceOf[JSON] - val msg = - """Expected a Number here - |{"bi1":"-90182736451928374653345","bi2":90182736451928374653345,"bi3":0,"bi4"... - |-------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleBigInt](js) - } - } - - test("Boolean must break") { - val js = """{"bool1":true,"bool2":"false"}""".asInstanceOf[JSON] - val msg = """Expected a Boolean here - |{"bool1":true,"bool2":"false"} - |----------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleBoolean](js) - } - val js2 = """{"bool1":true,"bool2":123}""".asInstanceOf[JSON] - val msg2 = """Expected a Boolean here - |{"bool1":true,"bool2":123} - |----------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ - sj.read[SampleBoolean](js2) - } - val js3 = """{"bool1":true,"bool2":null}""".asInstanceOf[JSON] - val msg3 = """Expected a Boolean here - |{"bool1":true,"bool2":null} - |----------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg3){ - sj.read[SampleBoolean](js3) - } - } - - test("Byte must break") { - val js = """{"b1":true,"b2":-128,"b3":0,"b4":64}""".asInstanceOf[JSON] - val msg = """Expected a Number here - |{"b1":true,"b2":-128,"b3":0,"b4":64} - |------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleByte](js) - } - val js2 = """{"b1":12,"b2":-128,"b3":0,"b4":null}""".asInstanceOf[JSON] - val msg2 = """Expected a Number here - |{"b1":12,"b2":-128,"b3":0,"b4":null} - |-------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ - sj.read[SampleByte](js2) - } - } - - test("Char must break") { - val js = """{"c1":null,"c2":"Y","c3":"Z"}""".asInstanceOf[JSON] - val msg = """A Char typed value cannot be null - |{"c1":null,"c2":"Y","c3":"Z"} - |---------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleChar](js) - } - val js2 = """{"c1":"","c2":"Y","c3":"Z"}""".asInstanceOf[JSON] - val msg2 = """Tried to read a Char but empty string found - |{"c1":"","c2":"Y","c3":"Z"} - |-------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ - sj.read[SampleChar](js2) - } - } - - test("Double must break") { - val js = - """{"d1":1.79769313486E23157E308,"d2":-1.7976931348623157E308,"d3":0.0,"d4":-123.4567}""".asInstanceOf[JSON] - val msg = - """Cannot parse an Double from value - |{"d1":1.79769313486E23157E308,"d2":-1.7976931348623157E308,"d3":0.0,"d4":-123... - |----------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleDouble](js) - } - } - - test("Float must break") { - val js = - """{"f1":3.4028235E38,"f2":"-3.4028235E38","f3":0.0,"f4":-123.4567}""".asInstanceOf[JSON] - val msg = - """Expected a Number here - |{"f1":3.4028235E38,"f2":"-3.4028235E38","f3":0.0,"f4":-123.4567} - |------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleFloat](js) - } - } - - test("Int must break") { - val js = """{"i1":2147483647,"i2":-2147483648,"i3":"0","i4":123}""".asInstanceOf[JSON] - val msg = """Expected a Number here - |{"i1":2147483647,"i2":-2147483648,"i3":"0","i4":123} - |---------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleInt](js) - } - val js2 = """{"i1":2147483647,"i2":-2147483648,"i3":2.3,"i4":123}""".asInstanceOf[JSON] - val msg2 = """Cannot parse an Int from value - |{"i1":2147483647,"i2":-2147483648,"i3":2.3,"i4":123} - |-----------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ - sj.read[SampleInt](js2) - } - } - - test("Long must break") { - val js = - """{"l1":9223372036854775807,"l2":-9223372036854775808,"l3":true,"l4":123}""".asInstanceOf[JSON] - val msg = - """Expected a Number here - |...23372036854775807,"l2":-9223372036854775808,"l3":true,"l4":123} - |----------------------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleLong](js) - } - val js2 = - """{"l1":9223372036854775807,"l2":-9223372036854775808,"l3":0.3,"l4":123}""".asInstanceOf[JSON] - val msg2 = - """Cannot parse an Long from value - |...372036854775807,"l2":-9223372036854775808,"l3":0.3,"l4":123} - |----------------------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ - sj.read[SampleLong](js2) - } - } - - test("Short must break") { - val js = """{"s1":32767,"s2":true,"s3":0,"s4":123}""".asInstanceOf[JSON] - val msg = """Expected a Number here - |{"s1":32767,"s2":true,"s3":0,"s4":123} - |-----------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleShort](js) - } - val js2 = """{"s1":32767,"s2":3.4,"s3":0,"s4":123}""".asInstanceOf[JSON] - val msg2 = """Cannot parse an Short from value - |{"s1":32767,"s2":3.4,"s3":0,"s4":123} - |-------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ - sj.read[SampleShort](js2) - } - } - - test("String must break") { - val js = """{"s1":"something","s2":-19,"s3":null}""".asInstanceOf[JSON] - val msg = """Expected a String here - |{"s1":"something","s2":-19,"s3":null} - |-----------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleString](js) - } - } - - test("UUID must break") { - val js = - """{"u1":"bogus","u2":"580afe0d-81c0-458f-9e09-4486c7af0fe9"}""".asInstanceOf[JSON] - val msg = """Failed to create UUID value from parsed text bogus - |{"u1":"bogus","u2":"580afe0d-81c0-458f-9e09-4486c7af0fe9"} - |------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleUUID](js) - } - } diff --git a/src_old/test/scala/co.blocke.scalajack/json/primitives/TimePrim.scala b/src_old/test/scala/co.blocke.scalajack/json/primitives/TimePrim.scala deleted file mode 100644 index b908f511..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json/primitives/TimePrim.scala +++ /dev/null @@ -1,299 +0,0 @@ -package co.blocke.scalajack -package json.primitives - -import co.blocke.scala_reflection._ -import TestUtil._ -import munit._ -import munit.internal.console -import co.blocke.scalajack.json.JSON -import java.time._ - -class TimePrim() extends FunSuite: - - val sj = co.blocke.scalajack.ScalaJack() - - test("Duration must work") { - describe("--------------------------\n: Time Primitive Tests :\n--------------------------", Console.BLUE) - describe("+++ Positive Tests +++") - val inst = SampleDuration(Duration.ZERO, Duration.parse("P2DT3H4M"), null) - val js = sj.render(inst) - assertEquals("""{"d1":"PT0S","d2":"PT51H4M","d3":null}""".asInstanceOf[JSON],js) - assert(inst == sj.read[SampleDuration](js)) - } - - test("Instant must work") { - val inst = SampleInstant( - Instant.EPOCH, - Instant.MAX, - Instant.MIN, - Instant.parse("2007-12-03T10:15:30.00Z"), - null - ) - val js = sj.render(inst) - assertEquals( - """{"i1":"1970-01-01T00:00:00Z","i2":"+1000000000-12-31T23:59:59.999999999Z","i3":"-1000000000-01-01T00:00:00Z","i4":"2007-12-03T10:15:30Z","i5":null}""".asInstanceOf[JSON],js) - assert(inst == sj.read[SampleInstant](js)) - } - - test("LocalDateTime must work") { - val inst = SampleLocalDateTime( - LocalDateTime.MAX, - LocalDateTime.MIN, - LocalDateTime.parse("2007-12-03T10:15:30"), - null - ) - val js = sj.render(inst) - assertEquals( - """{"d1":"+999999999-12-31T23:59:59.999999999","d2":"-999999999-01-01T00:00:00","d3":"2007-12-03T10:15:30","d4":null}""".asInstanceOf[JSON],js) - assert(inst == sj.read[SampleLocalDateTime](js)) - } - - test("LocalDate must work") { - val inst = SampleLocalDate( - LocalDate.MAX, - LocalDate.MIN, - LocalDate.parse("2007-12-03"), - null - ) - val js = sj.render(inst) - assertEquals( - """{"d1":"+999999999-12-31","d2":"-999999999-01-01","d3":"2007-12-03","d4":null}""".asInstanceOf[JSON],js) - assert(inst == sj.read[SampleLocalDate](js)) - } - - test("LocalTime must work") { - val inst = SampleLocalTime( - LocalTime.MAX, - LocalTime.MIN, - LocalTime.MIDNIGHT, - LocalTime.NOON, - LocalTime.parse("10:15:30"), - null - ) - val js = sj.render(inst) - assertEquals( - """{"d1":"23:59:59.999999999","d2":"00:00:00","d3":"00:00:00","d4":"12:00:00","d5":"10:15:30","d6":null}""".asInstanceOf[JSON],js) - assert(inst == sj.read[SampleLocalTime](js)) - } - - test("OffsetDateTime must work") { - val inst = SampleOffsetDateTime( - OffsetDateTime.MAX, - OffsetDateTime.MIN, - OffsetDateTime.parse("2007-12-03T10:15:30+01:00"), - null - ) - val js = sj.render(inst) - assertEquals( - """{"o1":"+999999999-12-31T23:59:59.999999999-18:00","o2":"-999999999-01-01T00:00:00+18:00","o3":"2007-12-03T10:15:30+01:00","o4":null}""".asInstanceOf[JSON],js) - assert(inst == sj.read[SampleOffsetDateTime](js)) - } - - test("OffsetTime must work") { - val inst = SampleOffsetTime( - OffsetTime.MAX, - OffsetTime.MIN, - OffsetTime.parse("10:15:30+01:00"), - null - ) - val js = sj.render(inst) - assertEquals( - """{"o1":"23:59:59.999999999-18:00","o2":"00:00:00+18:00","o3":"10:15:30+01:00","o4":null}""".asInstanceOf[JSON],js) - assert(inst == sj.read[SampleOffsetTime](js)) - } - - test("Period must work") { - val inst = SamplePeriod(Period.ZERO, Period.parse("P1Y2M3D"), null) - val js = sj.render(inst) - assertEquals("""{"p1":"P0D","p2":"P1Y2M3D","p3":null}""".asInstanceOf[JSON],js) - assert(inst == sj.read[SamplePeriod](js)) - } - - test("ZonedDateTime must work") { - val inst = SampleZonedDateTime( - ZonedDateTime.parse("2007-12-03T10:15:30+01:00[Europe/Paris]"), - null - ) - val js = sj.render(inst) - assertEquals("""{"o1":"2007-12-03T10:15:30+01:00[Europe/Paris]","o2":null}""".asInstanceOf[JSON],js) - assert(inst == sj.read[SampleZonedDateTime](js)) - } - - test("Duration must break") { - describe("--- Negative Tests ---") - - val js = """{"d1":"PT0S","d2":21,"d3":null}""".asInstanceOf[JSON] - val msg = """Expected a String here - |{"d1":"PT0S","d2":21,"d3":null} - |------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleDuration](js) - } - val js2 = """{"d1":"PT0S","d2":"bogus","d3":null}""".asInstanceOf[JSON] - val msg2 = """Failed to parse Duration from input 'bogus' - |{"d1":"PT0S","d2":"bogus","d3":null} - |------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ - sj.read[SampleDuration](js2) - } - } - - test("Instant must break") { - val js = - """{"i1":"1970-01-01T00:00:00Z","i2":false,"i3":"-1000000000-01-01T00:00:00Z","i4":"2007-12-03T10:15:30Z","i5":null}""".asInstanceOf[JSON] - val msg = - """Expected a String here - |{"i1":"1970-01-01T00:00:00Z","i2":false,"i3":"-1000000000-01-01T00:00:00Z","i... - |----------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleInstant](js) - } - val js2 = - """{"i1":"1970-01-01T00:00:00Z","i2":"bogus","i3":"-1000000000-01-01T00:00:00Z","i4":"2007-12-03T10:15:30Z","i5":null}""".asInstanceOf[JSON] - val msg2 = - """Failed to parse Instant from input 'bogus' - |{"i1":"1970-01-01T00:00:00Z","i2":"bogus","i3":"-1000000000-01-01T00:00:00Z",... - |----------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ - sj.read[SampleInstant](js2) - } - } - - test("LocalDateTime must break") { - val js = - """{"d1":-1,"d2":"-999999999-01-01T00:00:00","d3":"2007-12-03T10:15:30","d4":null}""".asInstanceOf[JSON] - val msg = - """Expected a String here - |{"d1":-1,"d2":"-999999999-01-01T00:00:00","d3":"2007-12-03T10:15:30","d4":null} - |------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleLocalDateTime](js) - } - val js2 = - """{"d1":"bogus","d2":"-999999999-01-01T00:00:00","d3":"2007-12-03T10:15:30","d1":null}""".asInstanceOf[JSON] - val msg2 = - """Failed to parse LocalDateTime from input 'bogus' - |{"d1":"bogus","d2":"-999999999-01-01T00:00:00","d3":"2007-12-03T10:15:30","d1... - |------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ - sj.read[SampleLocalDateTime](js2) - } - } - - test("LocalDate must break") { - val js = - """{"d1":-1,"d2":"-999999999-01-01","d3":"2007-12-03","d4":null}""".asInstanceOf[JSON] - val msg = """Expected a String here - |{"d1":-1,"d2":"-999999999-01-01","d3":"2007-12-03","d4":null} - |------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleLocalDate](js) - } - val js2 = - """{"d1":"bogus","d2":"-999999999-01-01","d3":"2007-12-03","d4":null}""".asInstanceOf[JSON] - val msg2 = - """Failed to parse LocalDate from input 'bogus' - |{"d1":"bogus","d2":"-999999999-01-01","d3":"2007-12-03","d4":null} - |------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ - sj.read[SampleLocalDate](js2) - } - } - - test("LocalTime must break") { - val js = - """{"d1":"23:59:59.999999999","d2":"00:00:00","d3":"00:00:00","d4":"12:00:00","d5":false,"d6":null}""".asInstanceOf[JSON] - val msg = - """Expected a String here - |...:"00:00:00","d3":"00:00:00","d4":"12:00:00","d5":false,"d6":null} - |----------------------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleLocalTime](js) - } - val js2 = - """{"d1":"23:59:59.999999999","d2":"00:00:00","d3":"00:00:00","d4":"12:00:00","d5":"Bogus","d6":null}""".asInstanceOf[JSON] - val msg2 = - """Failed to parse LocalTime from input 'Bogus' - |...0:00","d3":"00:00:00","d4":"12:00:00","d5":"Bogus","d6":null} - |----------------------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ - sj.read[SampleLocalTime](js2) - } - } - - test("OffsetDateTime must break") { - val js = - """{"o1":"+999999999-12-31T23:59:59.999999999-18:00","o2":2,"o3":"2007-12-03T10:15:30+01:00","o4":null}""".asInstanceOf[JSON] - val msg = - """Expected a String here - |..."+999999999-12-31T23:59:59.999999999-18:00","o2":2,"o3":"2007-12-03T10:15:30... - |----------------------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleOffsetDateTime](js) - } - val js2 = - """{"o1":"+999999999-12-31T23:59:59.999999999-18:00","o2":"-999999999-01T00:00:00+18:00","o3":"2007-12-03T10:15:30+01:00","o4":null}""".asInstanceOf[JSON] - val msg2 = - """Failed to parse OffsetDateTime from input '-999999999-01T00:00:00+18:00' - |...9999999-18:00","o2":"-999999999-01T00:00:00+18:00","o3":"2007-12-03T10:15:30... - |----------------------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ - sj.read[SampleOffsetDateTime](js2) - } - } - - test("OffsetTime must break") { - val js = - """{"o1":"23:59:59.999999999-18:00","o2":false,"o3":"10:15:30+01:00","o4":null}""".asInstanceOf[JSON] - val msg = - """Expected a String here - |{"o1":"23:59:59.999999999-18:00","o2":false,"o3":"10:15:30+01:00","o4":null} - |--------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleOffsetTime](js) - } - val js2 = - """{"o1":"23:59:59.999999999-18:00","o2":"00:00:00:00+18:00","o3":"10:15:30+01:00","o4":null}""".asInstanceOf[JSON] - val msg2 = - """Failed to parse OffsetTime from input '00:00:00:00+18:00' - |...23:59:59.999999999-18:00","o2":"00:00:00:00+18:00","o3":"10:15:30+01:00","o4... - |----------------------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ - sj.read[SampleOffsetTime](js2) - } - } - - test("Period must break") { - val js = """{"p1":"P0D","p2":5,"p3":null}""".asInstanceOf[JSON] - val msg = """Expected a String here - |{"p1":"P0D","p2":5,"p3":null} - |-----------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SamplePeriod](js) - } - val js2 = """{"p1":"P0D","p2":"bogus","p3":null}""".asInstanceOf[JSON] - val msg2 = """Failed to parse Period from input 'bogus' - |{"p1":"P0D","p2":"bogus","p3":null} - |-----------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ - sj.read[SamplePeriod](js2) - } - } - - test("ZonedDateTime must break") { - val js = """{"o1":true,"o2":null}""".asInstanceOf[JSON] - val msg = """Expected a String here - |{"o1":true,"o2":null} - |------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[SampleZonedDateTime](js) - } - val js2 = """{"o1":"2007-12-03T10:15:30+01:00 Earth","o2":null}""".asInstanceOf[JSON] - val msg2 = - """Failed to parse ZonedDateTime from input '2007-12-03T10:15:30+01:00 Earth' - |{"o1":"2007-12-03T10:15:30+01:00 Earth","o2":null} - |--------------------------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg2){ - sj.read[SampleZonedDateTime](js2) - } - } diff --git a/src_old/test/scala/co.blocke.scalajack/json/primitives/ValueClassPrim.scala b/src_old/test/scala/co.blocke.scalajack/json/primitives/ValueClassPrim.scala deleted file mode 100644 index f71c126c..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json/primitives/ValueClassPrim.scala +++ /dev/null @@ -1,179 +0,0 @@ -package co.blocke.scalajack -package json.primitives - -import TestUtil._ -import munit._ -import munit.internal.console -import co.blocke.scalajack.json.JSON -import co.blocke.scala_reflection._ -import java.util.UUID - - -class ValueClassPrim() extends FunSuite: - - val sj = co.blocke.scalajack.ScalaJack() - - test("Value class of BigDecimal") { - describe("--------------------------------\n: ValueClass Primitive Tests :\n--------------------------------", Console.BLUE) - describe("+++ Positive Tests +++") - - val inst = VCBigDecimal(BigDecimal(12.34)) - val js = sj.render(inst) - assertEquals("""12.34""".asInstanceOf[JSON], js) - assertEquals(inst, sj.read[VCBigDecimal](js)) - } - - test("Value class of BigDecimal with null") { - val inst = VCBigDecimal(null) - val js = sj.render(inst) - assertEquals("""null""".asInstanceOf[JSON], js) - assertEquals(inst, sj.read[VCBigDecimal](js)) - } - - test("Value class of BigInt") { - val inst = VCBigInt(BigInt(1)) - val js = sj.render(inst) - assertEquals("""1""".asInstanceOf[JSON], js) - assertEquals(inst, sj.read[VCBigInt](js)) - } - - test("Value class of BigInt with null") { - val inst = VCBigInt(null) - val js = sj.render(inst) - assertEquals("""null""".asInstanceOf[JSON], js) - assertEquals(inst, sj.read[VCBigInt](js)) - } - - test("Value class of Byte") { - val inst = VCByte(100.asInstanceOf[Byte]) - val js = sj.render(inst) - assertEquals("""100""".asInstanceOf[JSON], js) - assertEquals(inst, sj.read[VCByte](js)) - } - - test("Value class of Boolean") { - val inst = VCBoolean(false) - val js = sj.render(inst) - assertEquals("""false""".asInstanceOf[JSON], js) - assertEquals(inst, sj.read[VCBoolean](js)) - } - - test("Value class of Char") { - val inst = VCChar('Z') - val js = sj.render(inst) - assertEquals(""""Z"""".asInstanceOf[JSON], js) - assertEquals(inst, sj.read[VCChar](js)) - } - - test("Value class of Double") { - val inst = VCDouble(100.5) - val js = sj.render(inst) - assertEquals("""100.5""".asInstanceOf[JSON], js) - assertEquals(inst, sj.read[VCDouble](js)) - } - - test("Value class of Enumeration") { - val inst = VCEnumeration(Size.Medium) - val js = sj.render(inst) - assertEquals(""""Medium"""".asInstanceOf[JSON], js) - assertEquals(inst, sj.read[VCEnumeration](js)) - } - - test("Value class of Enumeration with null") { - val inst = VCEnumeration(null) - val js = sj.render(inst) - assertEquals("""null""".asInstanceOf[JSON], js) - assertEquals(inst, sj.read[VCEnumeration](js)) - } - - test("Value class of Enum") { - val inst = VCEnum(Color.Green) - val js = sj.render(inst) - assertEquals(""""Green"""".asInstanceOf[JSON], js) - assertEquals(inst, sj.read[VCEnum](js)) - } - - test("Value class of Enum with null") { - val inst = VCEnum(null) - val js = sj.render(inst) - assertEquals("""null""".asInstanceOf[JSON], js) - assertEquals(inst, sj.read[VCEnum](js)) - } - - test("Value class of Float") { - val inst = VCFloat(100.5F) - val js = sj.render(inst) - assertEquals("""100.5""".asInstanceOf[JSON], js) - assertEquals(inst, sj.read[VCFloat](js)) - } - - test("Value class of Int") { - val inst = VCInt(100) - val js = sj.render(inst) - assertEquals("""100""".asInstanceOf[JSON], js) - assertEquals(inst, sj.read[VCInt](js)) - } - - test("Value class of Long") { - val inst = VCLong(100L) - val js = sj.render(inst) - assertEquals("""100""".asInstanceOf[JSON], js) - assertEquals(inst, sj.read[VCLong](js)) - } - - test("Value class of Short") { - val inst = VCShort(100.asInstanceOf[Short]) - val js = sj.render(inst) - assertEquals("""100""".asInstanceOf[JSON], js) - assertEquals(inst, sj.read[VCShort](js)) - } - - test("Value class of String") { - val inst = VCString("foo") - val js = sj.render(inst) - assertEquals(""""foo"""".asInstanceOf[JSON], js) - assertEquals(inst, sj.read[VCString](js)) - } - - test("Value class of String with null") { - val inst = VCString(null) - val js = sj.render(inst) - assertEquals("""null""".asInstanceOf[JSON], js) - assertEquals(inst, sj.read[VCString](js)) - } - - test("Value class of UUID") { - val inst = VCUUID(UUID.fromString("54cab778-7b9e-4b07-9d37-87b97a011e55")) - val js = sj.render(inst) - assertEquals(""""54cab778-7b9e-4b07-9d37-87b97a011e55"""".asInstanceOf[JSON], js) - assertEquals(inst, sj.read[VCUUID](js)) - } - - test("Value class of UUID with null") { - val inst = VCUUID(null) - val js = sj.render(inst) - assertEquals("""null""".asInstanceOf[JSON], js) - assertEquals(inst, sj.read[VCUUID](js)) - } - - test("Value class of Number") { - val inst = VCNumber(25) - val js = sj.render(inst) - assertEquals("""25""".asInstanceOf[JSON], js) - assertEquals((inst,true), { - val r = sj.read[VCNumber](js) - (r, r.vc.isInstanceOf[Byte]) - }) - } - - test("Wrong JSON for wrapped type") { - describe("--- Negative Tests ---") - - val js = """100.25""".asInstanceOf[JSON] - val msg = """Cannot parse an Short from value - |100.25 - |-----^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[VCShort](js) - } - } diff --git a/src_old/test/scala/co.blocke.scalajack/json/structures/Eithers.scala b/src_old/test/scala/co.blocke.scalajack/json/structures/Eithers.scala deleted file mode 100644 index 9eadc235..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json/structures/Eithers.scala +++ /dev/null @@ -1,96 +0,0 @@ -package co.blocke.scalajack -package json.structures - -import co.blocke.scala_reflection._ -import TestUtil._ -import munit._ -import munit.internal.console -import co.blocke.scalajack.json.JSON - - -class Eithers() extends FunSuite: - - val sj = co.blocke.scalajack.ScalaJack() - - test("Left - two class types") { - describe("------------------\n: Either Tests :\n------------------",Console.BLUE) - describe("+++ Positive Tests +++") - - val inst: Either[Parrot, DumpTruck] = Left(Parrot("blue")) - try { - val js = sj.render(inst) - assertEquals("""{"color":"blue"}""".asInstanceOf[JSON],js) - assertEquals(inst,sj.read[Either[Parrot, DumpTruck]](js)) - } catch { - case t: Throwable => println("Boom") - } - } - - test("Right - two class types") { - val inst: Either[Parrot, DumpTruck] = Right(DumpTruck(axles = 2)) - val js = sj.render(inst) - assertEquals("""{"axles":2}""".asInstanceOf[JSON],js) - assertEquals(inst,sj.read[Either[Parrot, DumpTruck]](js)) - } - - test("Left - class type and scalar type") { - val inst: Either[Parrot, String] = Left(Parrot("red")) - val js = sj.render(inst) - assertEquals("""{"color":"red"}""".asInstanceOf[JSON],js) - assert(inst == sj.read[Either[Parrot, DumpTruck]](js)) - } - - test("Right - class type and scalar type") { - val inst = EitherHolder[Parrot, String](Right("quack")) - val js = sj.render(inst) - assertEquals("""{"either":"quack"}""".asInstanceOf[JSON],js) - assertEquals(inst,sj.read[EitherHolder[Parrot, String]](js)) - } - - test("Either is null") { - val inst: Either[Parrot, String] = null - val js = sj.render(inst) - assertEquals("null".asInstanceOf[JSON],js) - assert(null == sj.read[Either[Parrot, String]](js)) - } - - test("Different classes with identical fields--favor Right") { - val js = """{"numLegs":4}""".asInstanceOf[JSON] - assertEquals(sj.read[Either[Chair, Table]](js),Right(Table(4))) - } - - test("Handles traits - Right") { - val inst = EitherHolder[String, Pet](Right(Dog("Fido", 13))) - val js = sj.render(inst) - assertEquals( - """{"either":{"_hint":"co.blocke.scalajack.json.structures.Dog","name":"Fido","kind":13}}""".asInstanceOf[JSON],js) - assertEquals(inst,sj.read[EitherHolder[String, Pet]](js)) - } - - test("Handles traits - Left") { - val inst = EitherHolder[Pet, String](Left(Dog("Fido", 13))) - val js = sj.render(inst) - assertEquals( - """{"either":{"_hint":"co.blocke.scalajack.json.structures.Dog","name":"Fido","kind":13}}""".asInstanceOf[JSON],js) - assertEquals(inst,sj.read[EitherHolder[Pet, String]](js)) - } - - test("Same instance Left and Right") { - describe("--- Negative Tests ---",Console.BLUE) - val js = "\"foo\"" - val msg = - """Types java.lang.String and java.lang.String are not mutually exclusive""".stripMargin - interceptMessage[IllegalArgumentException](msg){ - sj.read[Either[String, String]](js.asInstanceOf[JSON]) - } - } - - test("Neither value works") { - val js = "25" - val msg = """Failed to read either side of Either - |25 - |^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[Either[String, Boolean]](js.asInstanceOf[JSON]) - } - } diff --git a/src_old/test/scala/co.blocke.scalajack/json/structures/Model.scala b/src_old/test/scala/co.blocke.scalajack/json/structures/Model.scala deleted file mode 100644 index d7282ff5..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json/structures/Model.scala +++ /dev/null @@ -1,55 +0,0 @@ -package co.blocke.scalajack -package json.structures - - -// === Eithers -case class Parrot(color: String) -case class DumpTruck(axles: Int) -case class EitherHolder[L, R](either: Either[L, R]) - -case class Chair(numLegs: Int) -case class Table(numLegs: Int) - -trait Pet { val name: String } -case class Dog[A](name: String, kind: A) extends Pet - - -// === Structures -trait Body -case class FancyBody(message: String) extends Body -case class DefaultBody(message: String = "Unknown body") extends Body -case class AnyBody(stuff: Any) extends Body - -trait Hobby -case class InsideHobby(desc: String) extends Hobby - -case class Envelope[T <: Body](id: String, body: T) { - type Giraffe = T -} - -// Type member X should be ignored! Only used internally -case class BigEnvelope[T <: Body, H <: Hobby, X]( - id: String, - body: T, - hobby: H) { - type Giraffe = T - type Hippo = H - type IgnoreMe = X - - val x: IgnoreMe = null.asInstanceOf[IgnoreMe] -} - -case class Bigger(foo: Int, env: Envelope[FancyBody]) - -// === Unions -case class Person(name: String, age: Int) -case class Multi2(one: List[Boolean] | List[String]) -case class Multi3(one: List[String] | List[Int] | Boolean) -case class Multi4(one: List[String] | List[Int] | Boolean | Person) - -// === Intersections -trait InterA{ val a: Int } -trait InterB{ val b: Boolean } -trait InterC{ val c: Char } -case class InterImpl(a: Int, b: Boolean, c: Char) extends InterA with InterB with InterC -case class IntersectionHolder( a: InterA & InterB & InterC ) \ No newline at end of file diff --git a/src_old/test/scala/co.blocke.scalajack/json/structures/SealedTraits.scala b/src_old/test/scala/co.blocke.scalajack/json/structures/SealedTraits.scala deleted file mode 100644 index 2f3ced76..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json/structures/SealedTraits.scala +++ /dev/null @@ -1,104 +0,0 @@ -package co.blocke.scalajack -package json.structures - -import co.blocke.scala_reflection._ -import TestUtil._ -import munit._ -import munit.internal.console -import co.blocke.scalajack.json.JSON - -// Unambiguous member names -sealed trait ContactPoint -case class EmailAddress(emailAddress: String) extends ContactPoint -case class PhoneNumber(phoneNumber: String) extends ContactPoint - -// Ambiguous member names -sealed trait Vehicle -case class Truck(numberOfWheels: Int) extends Vehicle -case class Car(numberOfWheels: Int, color: String) extends Vehicle -case class Plane(numberOfEngines: Int) extends Vehicle - -// Case object implementation -sealed trait Flavor -case object Vanilla extends Flavor -case object Chocolate extends Flavor -case object Bourbon extends Flavor - -sealed trait Stay -case class VillaStay(name: String) extends Stay -case class RanchStay(name: String) extends Stay - -case class NotSealed() - - -class SealedTraits() extends FunSuite: - - val sj = co.blocke.scalajack.ScalaJack() - - test("Read - unambiguous") { - describe("------------------------\n: Sealed Trait Tests :\n------------------------",Console.BLUE) - - assert(EmailAddress("foo@bar.com") == sj.read[ContactPoint]("""{"emailAddress":"foo@bar.com"}""".asInstanceOf[JSON])) - } - - test("Write - unambiguous") { - assertEquals("""{"phoneNumber":"12223334444"}""".asInstanceOf[JSON], sj.render[ContactPoint](PhoneNumber("12223334444"))) - } - - test("Read - ambiguous") { - val msg = - """Multiple sub-classes of co.blocke.scalajack.json.structures.Stay match field names Set(name) - |{"name":"Wilderness"} - |--------------------^""".stripMargin - val js = """{"name":"Wilderness"}""".asInstanceOf[JSON] - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[Stay](js) - } - } - - test("Write - ambiguous") { - assertEquals( - """{"name":"Wilderness"}""".asInstanceOf[JSON], sj.render[Stay](VillaStay("Wilderness"))) - } - - test("Case object implementation") { - val flavors: List[Flavor] = List(Bourbon, Vanilla, Chocolate) - val js = sj.render(flavors) - assertEquals("""["Bourbon","Vanilla","Chocolate"]""".asInstanceOf[JSON],js) - assertEquals(flavors, sj.read[List[Flavor]](js) ) - } - - // No more hints on sealed traits!! - // test("Type hints with modification") { - // val sj2 = co.blocke.scalajack.ScalaJack().withHints((RType.of[Stay] -> "stay_kind")) - // val s: Stay = VillaStay("Hacienda") - // val js = sj2.render(s) - // assertEquals( - // """{"stay_kind":"co.blocke.scalajack.json.misc.VillaStay","name":"Hacienda"}""".asInstanceOf[JSON],js) - // assertEquals(s, sj2.read[Stay](js) ) - // } - - test("Handles null") { - val js = """null""".asInstanceOf[JSON] - val inst = sj.read[ContactPoint](js) - assertEquals(inst, null) - assert( sj.render[ContactPoint](inst) == js) - } - - test("Handle not a sealed trait") { - val js = """{"d":3,"color":"Red"}""".asInstanceOf[JSON] - val msg = - """No sub-classes of co.blocke.scalajack.json.structures.ContactPoint match field names Set(d, color) - |{"d":3,"color":"Red"} - |--------------------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[ContactPoint](js) - } - } - - test("Resolved ambiguous trait") { - val inst = Car(4,"Red") - val js = sj.render(inst) - assertEquals("""{"numberOfWheels":4,"color":"Red"}""".asInstanceOf[JSON], js) - assert(inst == sj.read[Vehicle](js)) - } \ No newline at end of file diff --git a/src_old/test/scala/co.blocke.scalajack/json/structures/TryAndCapture.scala b/src_old/test/scala/co.blocke.scalajack/json/structures/TryAndCapture.scala deleted file mode 100644 index 03ce9a82..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json/structures/TryAndCapture.scala +++ /dev/null @@ -1,60 +0,0 @@ -package co.blocke.scalajack -package json.structures - -import co.blocke.scala_reflection._ -import TestUtil._ -import munit._ -import munit.internal.console -import co.blocke.scalajack.json.JSON -import scala.util._ -import json.JsonMatcher -import co.blocke.scalajack.SJCapture - -case class Embed(stuff: List[String], num: Int) -case class Boom(name: String, other: Try[Embed]) -case class Cap(name: String) extends SJCapture - - -class TryAndCapture() extends FunSuite: - - val sj = co.blocke.scalajack.ScalaJack() - - test("Try success") { - describe( "---------------------------\n: Try and Capture Tests :\n---------------------------",Console.BLUE) - describe("Try:") - - val js = """{"name":"Greg","other":{"stuff":["a","b","c"],"num":2}}""".asInstanceOf[JSON] - val obj = sj.read[Boom](js) - assertEquals(Boom("Greg", Success(Embed(List("a", "b", "c"), 2))), obj) - assertEquals("""{"name":"Greg","other":{"stuff":["a","b","c"],"num":2}}""".asInstanceOf[JSON], sj.render(obj) ) - } - - test("Try failure") { - val js = """{"name":"Greg","other":[1,2,3]}""".asInstanceOf[JSON] - val obj = sj.read[Boom](js) - val msg = """Expected start of object here - |{"name":"Greg","other":[1,2,3]} - |-----------------------^""".stripMargin - assertEquals(msg, obj.other.asInstanceOf[Failure[_]].exception.getMessage) - assertEquals(js, sj.render(obj) ) - } - - test("Try failure 2") { - val js = """{"name":"Greg","other": -12.45 ,"num":2}""".asInstanceOf[JSON] - val obj = sj.read[Boom](js) - val msg = """Expected start of object here - |{"name":"Greg","other": -12.45 ,"num":2} - |-------------------------^""".stripMargin - assertEquals(msg, obj.other.asInstanceOf[Failure[_]].exception.getMessage) - assert("""{"name":"Greg","other":-12.45}""".asInstanceOf[JSON] == sj.render(obj) ) - } - - test("Capture can write semantically equivalent JSON") { - describe("Capture:") - - val js = - """{"name":"Greg", "foo":[1,2,"t" ], "zing" : {"dot":{"age":25,"food":"Pizza"}}, "blather":"wow", "boo": -29384.34, "maybe": false }""".asInstanceOf[JSON] - val h = sj.read[Cap](js) - assertEquals(h, Cap("Greg")) - assert(JsonMatcher.jsonMatches(sj.render(h), js)) - } diff --git a/src_old/test/scala/co.blocke.scalajack/json/structures/TypeMembers.scala b/src_old/test/scala/co.blocke.scalajack/json/structures/TypeMembers.scala deleted file mode 100644 index a3821683..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json/structures/TypeMembers.scala +++ /dev/null @@ -1,92 +0,0 @@ -package co.blocke.scalajack -package json.structures - -import co.blocke.scala_reflection._ -import TestUtil._ -import munit._ -import munit.internal.console -import co.blocke.scalajack.json.JSON - -trait Thing -case class BigThing(s: String) extends Thing -case class Boo[T <: Thing]( a: T ) - - -class TypeMembers extends FunSuite: - - val sj = co.blocke.scalajack.ScalaJack() - val sj2 = co.blocke.scalajack.ScalaJack().parseOrElse((RType.of[Body] -> RType.of[DefaultBody])) - - test("Read and match") { - describe("-----------------------------\n: Externalized Type Tests :\n-----------------------------",Console.BLUE) - - val json = - """{"Giraffe":"co.blocke.scalajack.json.structures.FancyBody","id":"ABC","body":{"message":"Hello"}}""".asInstanceOf[JSON] - val expected: Envelope[Body] = Envelope("ABC", FancyBody("Hello")) - val x = sj.read[Envelope[Body]](json) - // Test match functionality - val num = x.body match { - case _: FancyBody => 1 - case _ => 2 - } - assertEquals((expected, 1),(x, num)) - } - - test("Write -- Concrete T value") { - val value: Envelope[FancyBody] = Envelope("DEF", FancyBody("BOO")) - val expected = - """{"Giraffe":"co.blocke.scalajack.json.structures.FancyBody","id":"DEF","body":{"message":"BOO"}}""".asInstanceOf[JSON] - assertEquals(expected, sj.render[Envelope[FancyBody]](value)) - } - - test("Write -- Trait T value") { - val value: Envelope[Body] = Envelope("DEF", FancyBody("BOO")) - val expected = - """{"Giraffe":"co.blocke.scalajack.json.structures.FancyBody","id":"DEF","body":{"message":"BOO"}}""".asInstanceOf[JSON] - assertEquals(expected, sj.render[Envelope[Body]](value)) - } - - test("Wrapped") { - val inst = Bigger(25, Envelope("abc", FancyBody("msg here"))) - val js = sj.render(inst) - assertEquals( - """{"foo":25,"env":{"Giraffe":"co.blocke.scalajack.json.structures.FancyBody","id":"abc","body":{"message":"msg here"}}}""".asInstanceOf[JSON], js) - assertEquals(inst, sj.read[Bigger](js)) - } - - test("Type modifier works") { - val sjm = co.blocke.scalajack.ScalaJack().withTypeValueModifier( - co.blocke.scalajack.model.ClassNameHintModifier( - (hint: String) => "co.blocke.scalajack.json.structures." + hint, - (cname: String) => cname.split('.').last - ) - ) - val value: Envelope[Body] = Envelope("DEF", FancyBody("BOO")) - val js = sjm.render[Envelope[Body]](value) - assertEquals( - """{"Giraffe":"FancyBody","id":"DEF","body":{"message":"BOO"}}""".asInstanceOf[JSON], js) - assertEquals(value, sjm.read[Envelope[Body]](js)) - } - - test("Handles mutliple externalized types (bonus: with modifier)") { - val sjm = co.blocke.scalajack.ScalaJack().withTypeValueModifier( - co.blocke.scalajack.model.ClassNameHintModifier( - (hint: String) => "co.blocke.scalajack.json.structures." + hint, - (cname: String) => cname.split('.').last - ) - ) - val value: BigEnvelope[Body, Hobby, Int] = - BigEnvelope("DEF", FancyBody("BOO"), InsideHobby("stamps")) - val js = sjm.render[BigEnvelope[Body, Hobby, Int]](value) - assertEquals( - """{"Giraffe":"FancyBody","Hippo":"InsideHobby","id":"DEF","body":{"message":"BOO"},"hobby":{"desc":"stamps"}}""".asInstanceOf[JSON], - js) - assertEquals(value, sjm.read[BigEnvelope[Body, Hobby, Int]](js)) - } - - test("Works with ParseOrElse") { - val js = - """{"Giraffe":"co.blocke.scalajack.json.structures.FancyBody","id":"DEF","body":{"bogus":"BOO"}}""".asInstanceOf[JSON] - val expected: Envelope[Body] = Envelope("DEF", DefaultBody("Unknown body")) - assertEquals(expected,sj2.read[Envelope[Body]](js)) - } diff --git a/src_old/test/scala/co.blocke.scalajack/json/structures/UnionsAndIntersections.scala b/src_old/test/scala/co.blocke.scalajack/json/structures/UnionsAndIntersections.scala deleted file mode 100644 index 33590eec..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json/structures/UnionsAndIntersections.scala +++ /dev/null @@ -1,111 +0,0 @@ -package co.blocke.scalajack -package json.structures - -import co.blocke.scala_reflection._ -import scala.math.BigDecimal -import java.util.UUID -import TestUtil._ -import munit._ -import munit.internal.console -import co.blocke.scalajack.json.JSON - - -class UnionsAndIntersections() extends FunSuite: - - val sj = co.blocke.scalajack.ScalaJack() - - test("Simple union type") { - describe("-----------------------------------\n: Union and Intersection Tests :\n----------------------------------",Console.BLUE) - - // Right - val inst = Multi2(List("a","b","c")) - val js = sj.render(inst) - assertEquals(js, """{"one":["a","b","c"]}""".asInstanceOf[JSON]) - assertEquals(sj.read[Multi2](js), inst) - - // Left - val inst2 = Multi2(List(true,false)) - val js2 = sj.render(inst2) - assertEquals(js2, """{"one":[true,false]}""".asInstanceOf[JSON]) - assertEquals(sj.read[Multi2](js2), inst2) - - // Failure - val js3 = """{"one":12.34}""".asInstanceOf[JSON] - val msg = """Failed to read any values for union type - |{"one":12.34} - |------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[Multi2](js3) - } - } - - test("3-way union type") { - // Position 1 - val inst = Multi3(List("a","b","c")) - val js = sj.render(inst) - assertEquals(js, """{"one":["a","b","c"]}""".asInstanceOf[JSON]) - assertEquals(sj.read[Multi3](js), inst) - - // Position 2 - val inst2 = Multi3(List(1,2,3)) - val js2 = sj.render(inst2) - assertEquals(js2, """{"one":[1,2,3]}""".asInstanceOf[JSON]) - assertEquals(sj.read[Multi3](js2), inst2) - - // Position 3 - val inst3 = Multi3(false) - val js3 = sj.render(inst3) - assertEquals(js3, """{"one":false}""".asInstanceOf[JSON]) - assertEquals(sj.read[Multi3](js3), inst3) - - // Failure - val js4 = """{"one":12.34}""".asInstanceOf[JSON] - val msg = """Failed to read any values for union type - |{"one":12.34} - |------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[Multi3](js4) - } - } - - test("4-way union type") { - // Position 1 - val inst = Multi4(List("a","b","c")) - val js = sj.render(inst) - assertEquals(js, """{"one":["a","b","c"]}""".asInstanceOf[JSON]) - assertEquals(sj.read[Multi4](js), inst) - - // Position 2 - val inst2 = Multi4(List(1,2,3)) - val js2 = sj.render(inst2) - assertEquals(js2, """{"one":[1,2,3]}""".asInstanceOf[JSON]) - assertEquals(sj.read[Multi4](js2), inst2) - - // Position 3 - val inst3 = Multi4(false) - val js3 = sj.render(inst3) - assertEquals(js3, """{"one":false}""".asInstanceOf[JSON]) - assertEquals(sj.read[Multi4](js3), inst3) - - // Position 4 - val inst4 = Multi4(Person("Bob",34)) - val js4 = sj.render(inst4) - assertEquals(js4, """{"one":{"name":"Bob","age":34}}""".asInstanceOf[JSON]) - assertEquals(sj.read[Multi4](js4), inst4) - - // Failure - val js5 = """{"one":12.34}""".asInstanceOf[JSON] - val msg = """Failed to read any values for union type - |{"one":12.34} - |------^""".stripMargin - interceptMessage[co.blocke.scalajack.ScalaJackError](msg){ - sj.read[Multi4](js5) - } - } - - test("Intersection type") { - val inst = IntersectionHolder( InterImpl(5,true,'Z') ) - val js = sj.render(inst) - assertEquals(js, """{"a":{"_hint":"co.blocke.scalajack.json.structures.InterImpl","a":5,"b":true,"c":"Z"}}""".asInstanceOf[JSON]) - assertEquals(inst, sj.read[IntersectionHolder](js)) - } diff --git a/src_old/test/scala/co.blocke.scalajack/json4s/AnyColl.scala b/src_old/test/scala/co.blocke.scalajack/json4s/AnyColl.scala deleted file mode 100644 index ba269f28..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json4s/AnyColl.scala +++ /dev/null @@ -1,77 +0,0 @@ -package co.blocke.scalajack -package json4s - -import org.json4s._ -import org.json4s.{ Diff, JDecimal, JNothing, JObject } -import TestUtil._ -import munit._ -import munit.internal.console -import co.blocke.scalajack.json4s.Json4sFlavor - -import scala.math.BigDecimal - -class AnyColl() extends FunSuite: - - val sj = co.blocke.scalajack.ScalaJack(Json4sFlavor()) - - test("List works (Int)") { - describe( - "-----------------------------------\n: Any Collection Tests (Json4s) :\n-----------------------------------", Console.BLUE - ) - val inst: Any = List(1, 2L, 3.2, BigDecimal(123.45)) - val js4s = sj.render(inst) - val expected = JArray(List(JInt(1), JLong(2), JDouble(3.2), JDecimal(123.45))) - assertEquals(Diff(JNothing, JNothing, JNothing), js4s.diff(expected)) - assertEquals(inst, sj.read[Any](js4s)) - } - - test("First-Level List works (Class)") { - val inst: Any = List(Player("Mike", 34), Player("Sarah", 29)) - val js4s = sj.render(inst) - val expected = JArray( - List( - JObject( - List( - "_hint" -> JString("co.blocke.scalajack.json4s.Player"), - "name" -> JString("Mike"), - "age" -> JInt(34) - ) - ), - JObject( - List( - "_hint" -> JString("co.blocke.scalajack.json4s.Player"), - "name" -> JString("Sarah"), - "age" -> JInt(29) - ) - ) - ) - ) - assertEquals(Diff(JNothing, JNothing, JNothing), js4s.diff(expected)) - assert(List(Player("Mike", 34), Player("Sarah", 29)) == sj.read[List[Any]](js4s)) - } - - test("Map works (Int,Int)") { - val inst: Any = Map(1 -> 2, 3 -> 4) - val js4s = sj.render(inst) - val expected = JObject(List("1" -> JInt(2), "3" -> JInt(4))) - assertEquals(Diff(JNothing, JNothing, JNothing), js4s.diff(expected)) - assert(Map("1" -> 2, "3" -> 4) == - sj.read[Any](js4s)) // May keys converted to String when read back in (because they're Any) - } - - test("Map works (String,Int)") { - val inst: Any = Map("yes" -> 1, "no" -> 2) - val js4s = sj.render(inst) - val expected = JObject(List("yes" -> JInt(1), "no" -> JInt(2))) - assertEquals(Diff(JNothing, JNothing, JNothing), js4s.diff(expected)) - assertEquals(inst, sj.read[Any](js4s)) - } - - test("First-Level Map works (Class)") { - val js4s = JObject( - "_hint" -> JString("co.blocke.scalajack.json4s.Player"), - "name" -> JString("Mike"), - "age" -> JInt(34) - ) - assert(Player("Mike", 34) == sj.read[Any](js4s)) - } diff --git a/src_old/test/scala/co.blocke.scalajack/json4s/Custom.scala b/src_old/test/scala/co.blocke.scalajack/json4s/Custom.scala deleted file mode 100644 index 2e2f9bd0..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json4s/Custom.scala +++ /dev/null @@ -1,90 +0,0 @@ -package co.blocke.scalajack -package json4s - -import TestUtil._ -import munit._ -import munit.internal.console -import org.json4s._ -import co.blocke.scalajack.json4s._ -import co.blocke.scala_reflection.RType - -class Custom extends FunSuite: - - val sj = co.blocke.scalajack.ScalaJack(Json4sFlavor()) - - test("parse()") { - describe( - "---------------------------\n: Custom Tests (Json4s) :\n---------------------------", Console.BLUE - ) - val p = sj.parse(JInt(5)) - assertEquals(p.isInstanceOf[Json4sParser], true) - } - - test("allowPermissivePrimitives()") { - interceptMessage[co.blocke.scalajack.ScalaJackError]("Permissive primitives not supported for Json4s"){ - sj.allowPermissivePrimitives() - } - } - - test("parseOrElse()") { - val sj2 = sj.parseOrElse(RType.of[Address] -> RType.of[DefaultAddress]) - val js4s = - JObject( - List("_hint" -> JString("unknown"), "postalCode" -> JString("12345")) - ) - assertEquals(sj2.read[Address](js4s), DefaultAddress("12345")) - } - - test("withAdapters()") { - val sj2 = sj.withAdapters(PhoneAdapter) - val dbo = JObject( - List("name" -> JString("Fred"), "phone" -> JString("123-456-7890")) - ) - assertEquals(sj2.read[Employee](dbo), Employee("Fred", "1234567890".asInstanceOf[Phone])) - } - - test("withDefaultHint()") { - val sj2 = co.blocke.scalajack.ScalaJack(Json4sFlavor()).withDefaultHint("kind") - val dbo = new JObject( - List( - "kind" -> JString("co.blocke.scalajack.json4s.USDemographic"), - "age" -> JInt(34), - "address" -> JObject( - List( - "kind" -> JString("co.blocke.scalajack.json4s.USAddress"), - "street" -> JString("123 Main"), - "city" -> JString("New York"), - "state" -> JString("NY"), - "postalCode" -> JString("39822") - ) - ) - ) - ) - assertEquals(sj2.read[Demographic](dbo), USDemographic(34, USAddress("123 Main", "New York", "NY", "39822")) - ) - } - - test("withHints()") { - val sj2 = co.blocke.scalajack.ScalaJack(Json4sFlavor()).withHints( - RType.of[Address] -> "addr_kind", - RType.of[Demographic] -> "demo" - ) - val dbo = new JObject( - List( - "demo" -> JString("co.blocke.scalajack.json4s.USDemographic"), - "age" -> JInt(34), - "address" -> JObject( - List( - "addr_kind" -> JString("co.blocke.scalajack.json4s.USAddress"), - "street" -> JString("123 Main"), - "city" -> JString("New York"), - "state" -> JString("NY"), - "postalCode" -> JString("39822") - ) - ) - ) - ) - assertEquals(sj2.read[Demographic](dbo), USDemographic(34, USAddress("123 Main", "New York", "NY", "39822")) - ) - } - // Other custom configs are already covered in other tests elsewhere diff --git a/src_old/test/scala/co.blocke.scalajack/json4s/Json4sSpec.scala b/src_old/test/scala/co.blocke.scalajack/json4s/Json4sSpec.scala deleted file mode 100644 index 0ec02a5f..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json4s/Json4sSpec.scala +++ /dev/null @@ -1,238 +0,0 @@ -package co.blocke.scalajack -package json4s - -import org.json4s._ -import org.json4s.{ Diff, JNothing, JObject } -import TestUtil._ -import munit._ -import munit.internal.console -import co.blocke.scalajack._ -import co.blocke.scalajack.json4s._ -import co.blocke.scala_reflection.RType - -class Json4sSpec extends FunSuite: - - val sj = co.blocke.scalajack.ScalaJack(Json4sFlavor()) - - test("Null Arrays work") { - describe( - "------------------\n: Json4s Tests :\n------------------", Console.BLUE - ) - val inst: List[String] = null - val js4s = sj.render(inst) - assert(Diff(JNothing, JNothing, JNothing) == js4s.diff(JNull) ) - assertEquals(inst, sj.read[List[String]](js4s)) - } - - test("Null Maps work") { - val inst: Map[String, String] = null - val js4s = sj.render(inst) - assert(Diff(JNothing, JNothing, JNothing) == js4s.diff(JNull) ) - assertEquals(inst, sj.read[Map[String, String]](js4s)) - - val s: String = null - val inst2 = Map("a" -> 1, s -> 2) - interceptMessage[co.blocke.scalajack.ScalaJackError]("Map keys cannot be null."){ - sj.render(inst2) - } - } - - test("Null strings work") { - val inst: String = null - val js4s = sj.render(inst) - assert(Diff(JNothing, JNothing, JNothing) == js4s.diff(JNull) ) - assertEquals(inst, sj.read[String](js4s)) - } - - test("Null objects work") { - val inst: Player = null - val js4s = sj.render(inst) - assert(Diff(JNothing, JNothing, JNothing) == js4s.diff(JNull) ) - assert(inst == sj.read[Player](js4s)) - } - - test("Tuples work") { - val inst = List(("Fred", 34), ("Sally", 29)) - val js4s = sj.render(inst) - val expected = JArray( - List( - JArray(List(JString("Fred"), JInt(34))), - JArray(List(JString("Sally"), JInt(29))) - ) - ) - assert(Diff(JNothing, JNothing, JNothing) == js4s.diff(expected) ) - assertEquals(inst, sj.read[List[(String, Int)]](js4s)) - } - - test("Bad JValueBuilder access") { - val b = JValueBuilder() - interceptMessage[co.blocke.scalajack.ScalaJackError]("No value set for internal json4s builder"){ - b.result() - } - } - test("SJCapture works") { - val js4s = JObject( - List( - "name" -> JString("Harry"), - "age" -> JInt(43), - "foo" -> JBool(true), - "bar" -> JInt(3) - ) - ) - val inst = sj.read[PlayerCapture](js4s) - assertEquals(PlayerCapture("Harry", 43), inst ) - assert(Diff(JNothing, JNothing, JNothing) == js4s.diff(sj.render(inst))) - } - - test("Trait support") { - val inst: Thing[Int, String] = AThing(5, "foo") - val js4s = sj.render(inst) - val expected = JObject( - List( - "_hint" -> JString("co.blocke.scalajack.json4s.AThing"), - "a" -> JInt(5), - "b" -> JString("foo") - ) - ) - assert(Diff(JNothing, JNothing, JNothing) == js4s.diff(expected) ) - assertEquals(inst, sj.read[Thing[Int, String]](js4s)) - } - - test("Malformed error works") { - val js4s = JArray(List(JInt(3), JDouble(3.1))) - interceptMessage[co.blocke.scalajack.ScalaJackError]("Cannot parse an Int from value"){ - sj.read[List[Int]](js4s) - } - } - - test("Unexpected error works") { - val js4s = JArray(List(JInt(3), JDouble(3.1))) - interceptMessage[co.blocke.scalajack.ScalaJackError]("Expected object here, not 'JArray(List(JInt(3), JDouble(3.1)))'"){ - sj.read[Player](js4s) - } - } - - test("Hint mods work") { - val prependHintMod = model.ClassNameHintModifier( - (hint: String) => "co.blocke.scalajack.json4s." + hint, - (cname: String) => cname.split('.').last - ) - val sjx = sj.withHintModifiers((RType.of[Address], prependHintMod)) - val inst: Demographic = - USDemographic(50, USAddress("123 Main", "New York", "NY", "39822")) - val js4s = sjx.render(inst) - val expected = JObject( - List( - "_hint" -> JString("co.blocke.scalajack.json4s.USDemographic"), - "age" -> JInt(50), - "address" -> JObject( - List( - "_hint" -> JString("USAddress"), - "street" -> JString("123 Main"), - "city" -> JString("New York"), - "state" -> JString("NY"), - "postalCode" -> JString("39822") - ) - ) - ) - ) - assert(Diff(JNothing, JNothing, JNothing) == js4s.diff(expected) ) - assertEquals(inst, sjx.read[Demographic](js4s)) - } - - test("Broken hint mod (no class)") { - val prependHintMod = model.ClassNameHintModifier( - (hint: String) => "co.blocke.scalajack.bogus." + hint, - (cname: String) => cname.split('.').last - ) - val sjx = sj.withHintModifiers((RType.of[Address], prependHintMod)) - val js4s = JObject( - List( - "_hint" -> JString("co.blocke.scalajack.json4s.USDemographic"), - "age" -> JInt(50), - "address" -> JObject( - List( - "_hint" -> JString("BogusAddress"), - "street" -> JString("123 Main"), - "city" -> JString("New York"), - "state" -> JString("NY"), - "postalCode" -> JString("39822") - ) - ) - ) - ) - interceptMessage[co.blocke.scalajack.ScalaJackError]("Couldn't marshal class for BogusAddress"){ - sj.read[Demographic](js4s) - } - } - - test("Null object value") { - val inst = USDemographic(25, null) - val js4s = sj.render(inst) - assert(Diff(JNothing, JNothing, JNothing) == js4s.diff(JObject(List("age" -> JInt(25), "address" -> JNull)))) - assertEquals(inst, sj.read[USDemographic](js4s)) - } - - test("No type hint in trait") { - val js4s = JObject(List("a" -> JInt(5), "b" -> JString("foo"))) - interceptMessage[co.blocke.scalajack.ScalaJackError]("Type hint '_hint' not found"){ - sj.read[Thing[Int, String]](js4s) - } - } - - test("Any type that looks like trait but unknown hint") { - val js4s = JObject( - List("_hint" -> JInt(4), "name" -> JString("Fred"), "age" -> JInt(55)) - ) - assert(Map("_hint" -> 4, "name" -> "Fred", "age" -> 55) == sj.read[Any](js4s)) - } - - test("Non-scalars can't be map keys for Json4s") { - val p = Player("Fred", 1) - val m = Map(p -> 3) - interceptMessage[co.blocke.scalajack.ScalaJackError]("Json4s type org.json4s.JsonAST$JObject is not supported as a Map key"){ - sj.render(m) - } - val js4s = JObject( - List( - "name" -> JString("Harry"), - "age" -> JInt(43), - "foo" -> JBool(true), - "bar" -> JInt(3) - ) - ) - interceptMessage[co.blocke.scalajack.ScalaJackError]("Only scalar values are supported as JValue Map keys"){ - sj.read[Map[Player, Int]](js4s) - } - } - - test("Externalized type hints work (with type modifier!)") { - import model._ - val scalaJack = co.blocke.scalajack.ScalaJack(Json4sFlavor()).withTypeValueModifier( - ClassNameHintModifier( - (hint: String) => "co.blocke.scalajack.json4s." + hint, - (cname: String) => cname.split('.').last - ) - ) - val value: Envelope[Body] = Envelope("DEF", FancyBody("BOO")) - val d = scalaJack.render[Envelope[Body]](value) - assertEquals( - "JObject(List((Giraffe,JString(FancyBody)), (id,JString(DEF)), (body,JObject(List((message,JString(BOO)))))))", - d.toString) - assertEquals(scalaJack.read[Envelope[Body]](d), value) - } - - test("Source as string") { - val js4s = JObject( - List( - "name" -> JString("Harry"), - "age" -> JInt(43), - "foo" -> JBool(true), - "bar" -> JInt(3) - ) - ) - val p = sj.parse(js4s) - assertEquals(p.sourceAsString, - "JObject(List((name,JString(Harry)), (age,JInt(43)), (foo,JBool(true)), (bar,JInt(3))))" - ) - } diff --git a/src_old/test/scala/co.blocke.scalajack/json4s/Model.scala b/src_old/test/scala/co.blocke.scalajack/json4s/Model.scala deleted file mode 100644 index b6db8484..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json4s/Model.scala +++ /dev/null @@ -1,118 +0,0 @@ -package co.blocke.scalajack -package json4s - -import java.util.UUID -import co.blocke.scalajack.model._ -import co.blocke.scala_reflection.RType -import co.blocke.scala_reflection.info.AliasInfo -import scala.collection.mutable -import co.blocke.scalajack.SJCapture - -// === Scala -case class SampleBigDecimal( - bd1: BigDecimal, - bd2: BigDecimal, - bd3: BigDecimal, - bd4: BigDecimal, - bd5: BigDecimal, - bd6: BigDecimal) -case class SampleBigInt(bi1: BigInt, bi2: BigInt, bi3: BigInt, bi4: BigInt) -case class SampleBinary(b1: Array[Byte], b2: Array[Byte]) -case class SampleBoolean(bool1: Boolean, bool2: Boolean) -case class SampleByte(b1: Byte, b2: Byte, b3: Byte, b4: Byte) -case class SampleChar(c1: Char, c2: Char, c3: Char) -case class SampleDouble(d1: Double, d2: Double, d3: Double, d4: Double) - -object Size extends Enumeration { - val Small, Medium, Large = Value -} -case class SampleEnum( - e1: Size.Value, - e2: Size.Value, - e3: Size.Value, - e4: Size.Value, - e5: Size.Value) - -case class SampleFloat(f1: Float, f2: Float, f3: Float, f4: Float) -case class SampleInt(i1: Int, i2: Int, i3: Int, i4: Int) -case class SampleLong(l1: Long, l2: Long, l3: Long, l4: Long) -case class SampleShort(s1: Short, s2: Short, s3: Short, s4: Short) -case class SampleString(s1: String, s2: String, s3: String) - -case class SampleUUID(u1: UUID, u2: UUID) - -case class Player(name: String, age: Int) -case class PlayerCapture(name: String, age: Int) extends SJCapture - -case class OptionBigInt(o: Option[BigInt]) -case class OptionClass(name: String, age: Option[Int]) -case class OptionTuple(foo: Int, t: (Boolean, Option[String], Int)) -trait Person { val name: String } -case class SomeClass(name: String, age: Int) extends Person -trait Thing[A, B] { val a: A; val b: B } -case class AThing[Y, X](a: X, b: Y) extends Thing[X, Y] - -case class WrappedMaps( - a: Map[Byte, Int], - b: Map[Int, Int], - c: Map[Long, Int], - d: Map[Double, Int], - e: Map[Float, Int], - f: Map[Short, Int], - g: Map[BigInt, Int], - h: Map[BigDecimal, Int], - i: Map[Boolean, Int], - j: Map[Char, Int], - k: Map[String, Int]) - -trait Address { val postalCode: String } -case class USAddress( - street: String, - city: String, - state: String, - postalCode: String) - extends Address -case class DefaultAddress(postalCode: String) extends Address -trait Demographic { val address: Address } -case class USDemographic(age: Int, address: Address) extends Demographic - -trait Body -case class FancyBody(message: String) extends Body -case class Envelope[T <: Body](id: String, body: T) { - type Giraffe = T -} - -opaque type Phone >: Null = String - -// Override just Phone -object PhoneAdapter extends TypeAdapterFactory with TypeAdapter[Phone]: - def matches(concrete: RType): Boolean = - concrete match { - case a: AliasInfo if a.name == "Phone" => - true - case _ => - false - } - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[Phone] = this - val info = RType.of[Phone] - override def isStringish: Boolean = true - - def read(parser: Parser): Phone = - parser.expectString() match { - case null => null.asInstanceOf[Phone] - case s: String => s.replaceAll("-", "").asInstanceOf[Phone] - } - - def write[WIRE]( - t: Phone, - writer: Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit = t match { - case null => writer.writeNull(out) - case _ => - writer.writeString( - "%s-%s-%s".format(t.toString.substring(0, 3), t.toString.substring(3, 6), t.toString.substring(6)), - out - ) - } - -case class Employee(name: String, phone: Phone) diff --git a/src_old/test/scala/co.blocke.scalajack/json4s/Parsing.scala b/src_old/test/scala/co.blocke.scalajack/json4s/Parsing.scala deleted file mode 100644 index 4ece3a6c..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json4s/Parsing.scala +++ /dev/null @@ -1,103 +0,0 @@ -package co.blocke.scalajack -package json4s - -import TestUtil._ -import munit._ -import munit.internal.console -import org.json4s._ -import co.blocke.scalajack.json4s.Json4sFlavor - -class Parsing() extends FunSuite: - - val sj = co.blocke.scalajack.ScalaJack(Json4sFlavor()) - - test("Null String value") { - describe( - "----------------------\n: Parsing (Json4s) :\n----------------------", Console.BLUE - ) - assertEquals(sj.read[String](null), null) - } - - test("Null (BSON null) String value") { - assertEquals(sj.read[String](JNull), null) - } - - test("Non-String value where String expected") { - interceptMessage[co.blocke.scalajack.ScalaJackError]("Expected string here, not 'JInt(5)'"){ - sj.read[String](JInt(5)) - } - } - - test("Null List value") { - assertEquals(sj.read[List[Int]](null), null) - } - - test("Null (BSON null) List value") { - assertEquals(sj.read[List[Int]](JNull), null) - } - - test("Non-List value where List expected") { - interceptMessage[co.blocke.scalajack.ScalaJackError]("Expected list here, not 'JInt(5)'"){ - sj.read[List[Int]](JInt(5)) - } - } - - test("Null tuple value") { - assertEquals(sj.read[(Int, Int)](null), null) - } - - test("Null (BSON null) tuple value") { - assertEquals(sj.read[(Int, Int)](JNull), null) - } - - test("Non-tuple value where tuple expected") { - interceptMessage[co.blocke.scalajack.ScalaJackError]("Expected tuple (list) here, not 'JInt(5)'"){ - sj.read[(Int, Int)](JInt(5)) - } - } - - test("Null Map value") { - assertEquals(sj.read[Map[String, Int]](null), null) - } - - test("Null (BSON null) Map value") { - assertEquals(sj.read[Map[String, Int]](JNull), null) - } - - test("Non-Map value where Map expected") { - interceptMessage[co.blocke.scalajack.ScalaJackError]("Expected map here, not 'JInt(5)'"){ - sj.read[Map[String, Int]](JInt(5)) - } - } - - test("Null object value") { - assertEquals(sj.read[Person](null), null) - } - - test("Null (BSON null) object value") { - assertEquals(sj.read[Person](JNull), null) - } - - test("Non-Boolean value where Boolean expected") { - interceptMessage[co.blocke.scalajack.ScalaJackError]("Expected boolean here, not 'JInt(5)'"){ - sj.read[Boolean](JInt(5)) - } - } - - test("Non-Number value where Number expected") { - interceptMessage[co.blocke.scalajack.ScalaJackError]("Expected number here, not 'JString(x)'"){ - sj.read[Int](new JString("x")) - } - } - - test("Attempt to scan for type hint on a non-object") { - interceptMessage[co.blocke.scalajack.ScalaJackError]("Expected object here, not 'JInt(5)'"){ - sj.read[Address](JInt(5)) - } - } - - test("Attempt to resolve type members on a non-object") { - interceptMessage[co.blocke.scalajack.ScalaJackError]("Expected object here, not 'JInt(5)'"){ - sj.read[Envelope[FancyBody]](JInt(5)) - } - } \ No newline at end of file diff --git a/src_old/test/scala/co.blocke.scalajack/json4s/ScalaPrim.scala b/src_old/test/scala/co.blocke.scalajack/json4s/ScalaPrim.scala deleted file mode 100644 index fffcc201..00000000 --- a/src_old/test/scala/co.blocke.scalajack/json4s/ScalaPrim.scala +++ /dev/null @@ -1,154 +0,0 @@ -package co.blocke.scalajack -package json4s - -import TestUtil._ -import munit._ -import munit.internal.console -import scala.math.BigDecimal -import org.json4s.JsonDSL._ -import org.json4s._ -import co.blocke.scalajack.json4s.Json4sFlavor - -class ScalaPrim() extends FunSuite: - - val sj = co.blocke.scalajack.ScalaJack(Json4sFlavor()) - - test("BigDecimal must work") { - describe( - "------------------------------------\n: Scala Primitive Tests (Json4s) :\n------------------------------------", Console.BLUE - ) - val inst = SampleBigDecimal( - BigDecimal(123L), - BigDecimal(1.23), - BigDecimal(0), - BigDecimal("123.456"), - BigDecimal( - "0.1499999999999999944488848768742172978818416595458984375" - ), - null - ) - val js4s = sj.render(inst) - val expected = JObject() ~ ("bd1" -> JDecimal(123L)) ~ ("bd2" -> JDecimal( - 1.23 - )) ~ ("bd3" -> JDecimal(0)) ~ ("bd4" -> JDecimal(123.456)) ~ ("bd5" -> JDecimal( - BigDecimal( - "0.1499999999999999944488848768742172978818416595458984375" - ) - )) ~ ("bd6" -> JNull) - assert(Diff(JNothing, JNothing, JNothing) == js4s.diff(expected) ) - assertEquals(inst, sj.read[SampleBigDecimal](js4s)) - } - - test("BigInt must work") { - val inst = SampleBigInt( - BigInt("-90182736451928374653345"), - BigInt("90182736451928374653345"), - BigInt(0), - null - ) - val js4s = sj.render(inst) - val expected = JObject() ~ ("bi1" -> JInt( - BigInt("-90182736451928374653345") - )) ~ ("bi2" -> JInt(BigInt("90182736451928374653345"))) ~ ("bi3" -> JInt(0)) ~ ("bi4" -> JNull) - assert(Diff(JNothing, JNothing, JNothing) == js4s.diff(expected) ) - assertEquals(inst, sj.read[SampleBigInt](js4s)) - } - - test("Boolean must work (not nullable)") { - val inst = SampleBoolean(true, false) - val js4s = sj.render(inst) - val expected = JObject() ~ ("bool1" -> JBool(true)) ~ ("bool2" -> JBool( - false - )) - assert(Diff(JNothing, JNothing, JNothing) == js4s.diff(expected) ) - assertEquals(inst, sj.read[SampleBoolean](js4s)) - } - - test("Double must work (not nullable)") { - val inst = - SampleDouble(Double.MaxValue, Double.MinValue, 0.0, -123.4567) - val js4s = sj.render(inst) - val expected = JObject() ~ ("d1" -> JDouble(Double.MaxValue)) ~ ("d2" -> JDouble( - Double.MinValue - )) ~ ("d3" -> JDouble(0.0)) ~ ("d4" -> JDouble(-123.4567)) - assert(Diff(JNothing, JNothing, JNothing) ==js4s.diff(expected) ) - assertEquals(inst, sj.read[SampleDouble](js4s)) - } - - test("Enumeration must work (not nullable)") { - val inst = - SampleEnum(Size.Small, Size.Medium, Size.Large, null, Size.Medium) - val js4s = sj.render(inst) - val expected = JObject() ~ ("e1" -> JString("Small")) ~ ("e2" -> JString("Medium")) - ~ ("e3" -> JString("Large")) ~ ("e4" -> JNull) ~ ("e5" -> JString("Medium")) - assert(Diff(JNothing, JNothing, JNothing) == js4s.diff(expected) ) - // mutate e5 into an ordinal... - val js2 = js4s.asInstanceOf[JObject] ~ ("e5" -> JInt(1)) - assertEquals(inst, sj.read[SampleEnum](js2)) - } - - test("Enumerations as Ints must work") { - val sj2 = sj.enumsAsInts() - val inst = - SampleEnum(Size.Small, Size.Medium, Size.Large, null, Size.Medium) - val js4s = sj2.render(inst) - val expected = JObject() ~ ("e1" -> JInt(0)) ~ ("e2" -> JInt(1)) ~ ("e3" -> JInt( - 2 - )) ~ ("e4" -> JNull) ~ ("e5" -> JInt(1)) - assert(Diff(JNothing, JNothing, JNothing) == js4s.diff(expected) ) - assertEquals(inst, sj2.read[SampleEnum](js4s)) - } - - test("Int must work (not nullable)") { - val inst = SampleInt(Int.MaxValue, Int.MinValue, 0, 123) - val js4s = sj.render(inst) - val expected = JObject() ~ ("i1" -> JInt(Int.MaxValue)) ~ ("i2" -> JInt( - Int.MinValue - )) ~ ("i3" -> JInt(0)) ~ ("i4" -> JInt(123)) - assert(Diff(JNothing, JNothing, JNothing) == js4s.diff(expected) ) - assertEquals(inst, sj.read[SampleInt](js4s)) - } - - test("Long must work (not nullable)") { - val inst = SampleLong(Long.MaxValue, Long.MinValue, 0L, 123L) - val js4s = sj.render(inst) - val expected = JObject() ~ ("l1" -> JLong(Long.MaxValue)) ~ ("l2" -> JLong( - Long.MinValue - )) ~ ("l3" -> JLong(0L)) ~ ("l4" -> JLong(123L)) - assert(Diff(JNothing, JNothing, JNothing) == js4s.diff(expected) ) - assertEquals(inst, sj.read[SampleLong](js4s)) - } - - test("Map of string-wrapped primitives work") { - val inst = WrappedMaps( - Map(3.toByte -> 2), - Map(1 -> 2), - Map(5L -> 7), - Map(1.2 -> 3), - Map(1.2F -> 3), - Map(2.toShort -> 9), - Map(BigInt(5) -> 6), - Map(BigDecimal(4.9) -> 8), - Map(true -> 1), - Map('c' -> 1), - Map("stuff" -> 99) - ) - val js4s = sj.render(inst) - val expected = JObject( - List( - "a" -> JObject(List("3" -> JInt(2))), - "b" -> JObject(List("1" -> JInt(2))), - "c" -> JObject(List("5" -> JInt(7))), - "d" -> JObject(List("1.2" -> JInt(3))), - "e" -> JObject(List("1.2" -> JInt(3))), - "f" -> JObject(List("2" -> JInt(9))), - "g" -> JObject(List("5" -> JInt(6))), - "h" -> JObject(List("4.9" -> JInt(8))), - "i" -> JObject(List("true" -> JInt(1))), - "j" -> JObject(List("c" -> JInt(1))), - "k" -> JObject(List("stuff" -> JInt(99))) - ) - ) - assert(Diff(JNothing, JNothing, JNothing) == js4s.diff(expected) ) - assertEquals(inst, sj.read[WrappedMaps](js4s)) - } diff --git a/src_old/test/scala/co.blocke.scalajack/yaml/collections/AnyColl.scala b/src_old/test/scala/co.blocke.scalajack/yaml/collections/AnyColl.scala deleted file mode 100644 index b6f4b8bc..00000000 --- a/src_old/test/scala/co.blocke.scalajack/yaml/collections/AnyColl.scala +++ /dev/null @@ -1,115 +0,0 @@ -package co.blocke.scalajack -package yaml -package collections - -import TestUtil._ -import munit._ -import munit.internal.console - -case class Player(name: String, age: Int) - -class AnyColl() extends FunSuite: - - val sj = ScalaJack(YamlFlavor()) - - test("List works (Int)") { - describe( - "---------------------------------\n: Any Collection Tests (YAML) :\n---------------------------------", Console.BLUE - ) - val inst: Any = List(1, 2, 3) - val yaml = sj.render(inst) - val comparison = """- 1 - |- 2 - |- 3 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assert(List(1, 2, 3) == sj.read[Any](yaml)) - } - - test("List works (String)") { - val inst: Any = List("one", "two", "three") - val yaml = sj.render(inst) - val comparison = """- one - |- two - |- three - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml) - assert(List("one", "two", "three") == sj.read[Any](yaml)) - } - - test("First-Level List works (Class)") { - val inst: Any = List(Player("Mike", 34), Player("Sarah", 29)) - val yaml = sj.render(inst) - val comparison = """- _hint: co.blocke.scalajack.yaml.collections.Player - | name: Mike - | age: 34 - |- _hint: co.blocke.scalajack.yaml.collections.Player - | name: Sarah - | age: 29 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assert(List(Player("Mike", 34), Player("Sarah", 29)) == sj.read[List[Any]](yaml)) - } - - test("Map works (Int,Int)") { - val inst: Any = Map(1 -> 2, 3 -> 4) - val yaml = sj.render(inst) - val comparison = """1: 2 - |3: 4 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[Any](yaml)) - } - - test("Map works (String,Int)") { - val inst: Any = Map("yes" -> 1, "no" -> 2) - val yaml = sj.render(inst) - val comparison = """yes: 1 - |no: 2 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assert(Map("yes" -> 1, "no" -> 2) == sj.read[Any](yaml)) - } - - test("First-Level Map works (Class)") { - val yaml = - """_hint: co.blocke.scalajack.yaml.collections.Player - |name: Mike - |age: 34 - |""".stripMargin.asInstanceOf[YAML] - assert(Player("Mike", 34) == sj.read[Any](yaml)) - } - - test("Second-Level Map works (Class keys) (Class)") { - val yaml = - """? - | _hint: co.blocke.scalajack.yaml.collections.Player - | name: Mike - | age: 34 - |: 15 - |? - | name: Mike - | age: 34 - |: 16 - |""".stripMargin.asInstanceOf[YAML] - assert( - Map(Player("Mike", 34) -> 15, Map("name" -> "Mike", "age" -> 34) -> 16) == sj.read[Any](yaml) - ) - } - - test("Second-Level Map (List keys) works (Class)") { - val yaml = - """? - | - - | _hint: co.blocke.scalajack.yaml.collections.Player - | name: Mike - | age: 34 - | - - | name: Mike - | age: 34 - |: 15 - |""".stripMargin.asInstanceOf[YAML] - assert( - Map(List(Player("Mike", 34), Map("name" -> "Mike", "age" -> 34)) -> 15) == sj.read[Any](yaml) - ) - } diff --git a/src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/ClassPrimKeys.scala b/src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/ClassPrimKeys.scala deleted file mode 100644 index fd6dfe3e..00000000 --- a/src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/ClassPrimKeys.scala +++ /dev/null @@ -1,271 +0,0 @@ -package co.blocke.scalajack -package yaml -package mapkeys - -import TestUtil._ -import munit._ -import munit.internal.console -import java.util.UUID -import model.StringMatchHintModifier -import co.blocke.scala_reflection.RType - -class ClassPrimKeys() extends FunSuite: - - val sj = ScalaJack(YamlFlavor()) - - test("Simple (flat) class as key") { - describe( - "--------------------------------\n: Class Map Key Tests (YAML) :\n--------------------------------", Console.BLUE - ) - describe("+++ Positive Tests +++") - val a = SimpleClass("Larry", 32, isOk = true, "golf") - val b = SimpleClass("Mike", 27, isOk = false, 125) - val inst = SampleSimple(Map(a -> b)) - val yaml = sj.render(inst) - val comparison = """m: - | ? name: Larry - | age: 32 - | isOk: true - | favorite: golf - | : name: Mike - | age: 27 - | isOk: false - | favorite: 125 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleSimple](yaml)) - } - - test("Complex class (having members that are classes) as key") { - val a = SimpleClass("Larry", 32, isOk = true, "golf") - val b = SimpleClass("Mike", 27, isOk = false, 125) - val c1 = ComplexClass( - UUID.fromString("1e6c2b31-4dfe-4bf6-a0a0-882caaff0e9c"), - a, - allDone = true - ) - val c2 = ComplexClass( - UUID.fromString("1e6c2b31-4dfe-4bf6-a0a0-882caaff0e9d"), - b, - allDone = false - ) - val inst = SampleComplex(Map(c1 -> c2)) - val yaml = sj.render(inst) - val comparison = """m: - | ? id: 1e6c2b31-4dfe-4bf6-a0a0-882caaff0e9c - | simple: - | name: Larry - | age: 32 - | isOk: true - | favorite: golf - | allDone: true - | : id: 1e6c2b31-4dfe-4bf6-a0a0-882caaff0e9d - | simple: - | name: Mike - | age: 27 - | isOk: false - | favorite: 125 - | allDone: false - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleComplex](yaml)) - } - - test("Simple (flat) trait as key") { - val a: Pet = FishPet("Flipper", Food.Veggies, 74.33) - val b: Pet = DogPet("Fido", Food.Meat, 3) - val inst = SamplePet(Map(a -> b)) - val yaml = sj.render(inst) - val comparison = """m: - | ? _hint: co.blocke.scalajack.yaml.mapkeys.FishPet - | name: Flipper - | food: Veggies - | waterTemp: 74.33 - | : _hint: co.blocke.scalajack.yaml.mapkeys.DogPet - | name: Fido - | food: Meat - | numLegs: 3 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SamplePet](yaml)) - } - - test("Complex trait (having members that are traits) as key") { - val a: Pet = FishPet("Flipper", Food.Veggies, 74.33) - val b: Pet = DogPet("Fido", Food.Meat, 3) - val c: Pet = CompoundPet("Legion", Food.Pellets, b) - val inst = SamplePet(Map(c -> a)) - val yaml = sj.render(inst) - val comparison = """m: - | ? _hint: co.blocke.scalajack.yaml.mapkeys.CompoundPet - | name: Legion - | food: Pellets - | pet: - | _hint: co.blocke.scalajack.yaml.mapkeys.DogPet - | name: Fido - | food: Meat - | numLegs: 3 - | : _hint: co.blocke.scalajack.yaml.mapkeys.FishPet - | name: Flipper - | food: Veggies - | waterTemp: 74.33 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SamplePet](yaml)) - } - - test( - "Complex trait (having members that are traits) as key where trait member is null" - ) { - val a: Pet = FishPet("Flipper", Food.Veggies, 74.33) - val b: Pet = null.asInstanceOf[Pet] // DogPet("Fido", Food.Meat, 3) - val c: Pet = CompoundPet("Legion", Food.Pellets, b) - val inst = SamplePet(Map(c -> a)) - val yaml = sj.render(inst) - val comparison = """m: - | ? _hint: co.blocke.scalajack.yaml.mapkeys.CompoundPet - | name: Legion - | food: Pellets - | pet: null - | : _hint: co.blocke.scalajack.yaml.mapkeys.FishPet - | name: Flipper - | food: Veggies - | waterTemp: 74.33 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SamplePet](yaml)) - } - - test("Class having collections as members") { - val a = PolyClass(Map("a" -> 1, "b" -> 2), List("one", "two")) - val b = PolyClass(Map("x" -> 9, "y" -> 10), List("aye", "you")) - val inst = SamplePolyClass(Map(a -> b)) - val yaml = sj.render(inst) - val comparison = """m: - | ? lookup: - | a: 1 - | b: 2 - | favs: [one, two] - | : lookup: - | x: 9 - | y: 10 - | favs: [aye, you] - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SamplePolyClass](yaml)) - } - - test("Class having collections as members (empty collections") { - val a = PolyClass(Map.empty[String, Int], List.empty[String]) - val b = PolyClass(Map.empty[String, Int], List.empty[String]) - val inst = SamplePolyClass(Map(a -> b)) - val yaml = sj.render(inst) - val comparison = """m: - | ? lookup: {} - | favs: [] - | : lookup: {} - | favs: [] - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SamplePolyClass](yaml)) - } - - test("Custom trait hint field and value for key trait") { - val petHintMod = StringMatchHintModifier( - Map("BreathsWater" -> "co.blocke.scalajack.yaml.mapkeys.FishPet", "BreathsAir" -> "co.blocke.scalajack.yaml.mapkeys.DogPet") - ) - val sj2 = sj - .withHints((RType.of[Pet] -> "kind")) - .withHintModifiers((RType.of[Pet] -> petHintMod)) - - val a: Pet = FishPet("Flipper", Food.Veggies, 74.33) - val b: Pet = DogPet("Fido", Food.Meat, 3) - val inst = SamplePet(Map(a -> b)) - val yaml = sj2.render(inst) - val comparison = """m: - | ? kind: BreathsWater - | name: Flipper - | food: Veggies - | waterTemp: 74.33 - | : kind: BreathsAir - | name: Fido - | food: Meat - | numLegs: 3 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj2.read[SamplePet](yaml)) - } - - test("Custom trait hint field and value for key member's trait") { - val petHintMod = StringMatchHintModifier( - Map("BreathsWater" -> "co.blocke.scalajack.yaml.mapkeys.FishPet", "BreathsAir" -> "co.blocke.scalajack.yaml.mapkeys.DogPet") - ) - val sj2 = sj - .withHints((RType.of[Pet] -> "kind")) - .withHintModifiers((RType.of[Pet] -> petHintMod)) - - val a: PetHolder = - ShinyPetHolder("123 Main", FishPet("Flipper", Food.Veggies, 74.33)) - val b: PetHolder = - ShinyPetHolder("210 North", DogPet("Fido", Food.Meat, 3)) - val inst = SampleShiny(Map(a -> b)) - val yaml = sj2.render(inst) - val comparison = """m: - | ? _hint: co.blocke.scalajack.yaml.mapkeys.ShinyPetHolder - | address: 123 Main - | pet: - | kind: BreathsWater - | name: Flipper - | food: Veggies - | waterTemp: 74.33 - | : _hint: co.blocke.scalajack.yaml.mapkeys.ShinyPetHolder - | address: 210 North - | pet: - | kind: BreathsAir - | name: Fido - | food: Meat - | numLegs: 3 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj2.read[SampleShiny](yaml)) - } - - test("Key value is a class having a noncanoncial map") { - val a = NCKey(Map(0 -> false, 1 -> true), "truth") - val b = NCKey(Map(1 -> false, 0 -> true), "lie") - val inst = SampleNCKey(Map(a -> b)) - val yaml = sj.render(inst) - val comparison = """m: - | ? nc: - | 0: false - | 1: true - | name: truth - | : nc: - | 1: false - | 0: true - | name: lie - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleNCKey](yaml)) - } - - test("Extra/unneeded fields in key's JSON harmlessly ignored") { - val yaml = - """m: - | ? - | name: Larry - | bogus: false - | age: 32 - | isOk: true - | favorite: golf - | : - | name: Mike - | age: 27 - | isOk: false - | favorite: 125 - |""".stripMargin.asInstanceOf[YAML] - val a = SimpleClass("Larry", 32, isOk = true, "golf") - val b = SimpleClass("Mike", 27, isOk = false, 125) - val inst = SampleSimple(Map(a -> b)) - assertEquals(inst, sj.read[SampleSimple](yaml)) - } \ No newline at end of file diff --git a/src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/JavaPrimKeys.scala b/src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/JavaPrimKeys.scala deleted file mode 100644 index f23e63e7..00000000 --- a/src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/JavaPrimKeys.scala +++ /dev/null @@ -1,357 +0,0 @@ -package co.blocke.scalajack -package yaml -package mapkeys - -import TestUtil._ -import munit._ -import munit.internal.console -import java.lang.{Boolean => JBoolean, Byte => JByte, Character => JChar, Double => JDouble, Float => JFloat, Integer => JInteger, Long => JLong, Short => JShort} -import java.math.{BigDecimal => JBigDecimal, BigInteger => JBigInteger} -import java.time._ - -class JavaPrimKeys() extends FunSuite: - - val sj = ScalaJack(YamlFlavor()) - - test("With BigDecimal Key") { - describe( - "-----------------------------------------\n: Java Primitive Map Key Tests (YAML) :\n-----------------------------------------", Console.BLUE - ) - describe("Simple DelimSpec:") - val inst = SampleJBigDecimal( - Map( - new JBigDecimal("123.456") -> new JBigDecimal("1"), - new JBigDecimal("789.123") -> new JBigDecimal("2") - ) - ) - val yaml = sj.render(inst) - val comparison = """m: - | 123.456: !!float '1' - | 789.123: !!float '2' - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleJBigDecimal](yaml)) - } - - test("With BigInteger Key") { - val inst = SampleJBigInteger( - Map( - new JBigInteger("123") -> new JBigInteger("1"), - new JBigInteger("789") -> new JBigInteger("2") - ) - ) - val yaml = sj.render(inst) - val comparison = """m: - | 123: 1 - | 789: 2 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleJBigInteger](yaml)) - } - - test("With Boolean Key") { - val inst = SampleJBoolean( - Map( - true.asInstanceOf[JBoolean] -> false.asInstanceOf[JBoolean], - false.asInstanceOf[JBoolean] -> true.asInstanceOf[JBoolean] - ) - ) - val yaml = sj.render(inst) - val comparison = """m: - | true: false - | false: true - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleJBoolean](yaml)) - } - - test("With Byte Key") { - val inst = SampleJByte( - Map( - 16.toByte.asInstanceOf[JByte] -> 2.toByte.asInstanceOf[JByte], - 48.toByte.asInstanceOf[JByte] -> 9.toByte.asInstanceOf[JByte] - ) - ) - val yaml = sj.render(inst) - val comparison = """m: - | 16: 2 - | 48: 9 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleJByte](yaml)) - } - - test("With Char Key") { - val inst = SampleJChar( - Map( - 'a'.asInstanceOf[JChar] -> 'A'.asInstanceOf[JChar], - 'z'.asInstanceOf[JChar] -> 'Z'.asInstanceOf[JChar] - ) - ) - val yaml = sj.render(inst) - val comparison = """m: - | a: A - | z: Z - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleJChar](yaml)) - } - - test("With Double Key") { - val inst = SampleJDouble( - Map( - 12.34.asInstanceOf[JDouble] -> 56.78.asInstanceOf[JDouble], - 90.12.asInstanceOf[JDouble] -> 34.56.asInstanceOf[JDouble] - ) - ) - val yaml = sj.render(inst) - val comparison = """m: - | 12.34: 56.78 - | 90.12: 34.56 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleJDouble](yaml)) - } - - test("With Float Key") { - val inst = SampleJFloat( - Map( - 12.34F.asInstanceOf[JFloat] -> 56.78F.asInstanceOf[JFloat], - 90.12F.asInstanceOf[JFloat] -> 34.56F.asInstanceOf[JFloat] - ) - ) - val yaml = sj.render(inst) - val comparison = """m: - | 12.34: 56.78 - | 90.12: 34.56 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleJFloat](yaml)) - } - - test("With Integer Key") { - val inst = SampleJInteger( - Map( - 12.asInstanceOf[JInteger] -> 56.asInstanceOf[JInteger], - 90.asInstanceOf[JInteger] -> 34.asInstanceOf[JInteger] - ) - ) - val yaml = sj.render(inst) - val comparison = """m: - | 12: 56 - | 90: 34 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleJInteger](yaml)) - } - - test("With Long Key") { - val inst = SampleJLong( - Map( - 12L.asInstanceOf[JLong] -> 56L.asInstanceOf[JLong], - 90L.asInstanceOf[JLong] -> 34L.asInstanceOf[JLong] - ) - ) - val yaml = sj.render(inst) - val comparison = """m: - | 12: 56 - | 90: 34 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleJLong](yaml)) - } - - test("With Number Key") { - val inst = SampleJNumber( - Map( - JByte.valueOf("-128") -> JByte.valueOf("127"), - JShort.valueOf("-32768") -> JShort.valueOf("32767"), - JInteger.valueOf("-2147483648") -> JInteger.valueOf("2147483647"), - JLong.valueOf("-9223372036854775808") -> JLong - .valueOf("9223372036854755807"), - JByte.valueOf("0") -> new JBigInteger("9923372036854755810"), - JFloat.valueOf("3.4e-038") -> JFloat.valueOf("3.4e+038"), - JDouble.valueOf("1.7e-308") -> JDouble.valueOf("1.7e+308"), - new JBigDecimal("1.8e+308") -> JFloat.valueOf("0.0") - ) - ) - val result = SampleJNumber( - Map( - JByte.valueOf("0") -> new JBigDecimal("9923372036854755810"), - JInteger.valueOf("-2147483648") -> JInteger.valueOf("2147483647"), - JLong.valueOf("-9223372036854775808") -> JLong - .valueOf("9223372036854755807"), - JByte.valueOf("-128") -> JByte.valueOf("127"), - JFloat.valueOf("3.4E-38") -> JFloat.valueOf("3.4E38"), - JShort.valueOf("-32768") -> JShort.valueOf("32767"), - new JBigDecimal("1.8E+308") -> JByte.valueOf("0"), - JDouble.valueOf("1.7E-308") -> JDouble.valueOf("1.7E308") - ) - ) - val yaml = sj.render(inst) - val comparison = """m: - | -128: 127 - | !!float '3.4E-38': !!float '3.4E38' - | -32768: 32767 - | !!float '1.8E+308': 0.0 - | !!float '1.7E-308': !!float '1.7E308' - | 0: 9923372036854755810 - | -2147483648: 2147483647 - | -9223372036854775808: 9223372036854755807 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - val read = sj.read[SampleJNumber](yaml) - assertEquals(result, read ) - } - - test("With Short Key") { - val inst = SampleJShort( - Map( - 12.toShort.asInstanceOf[JShort] -> 56.toShort - .asInstanceOf[JShort], - 90.toShort.asInstanceOf[JShort] -> 34.toShort.asInstanceOf[JShort] - ) - ) - val yaml = sj.render(inst) - val comparison = """m: - | 12: 56 - | 90: 34 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleJShort](yaml)) - } - - test("With Duration Key") { - describe("Time DelimSpec:") - val inst = - SampleDuration(Map(Duration.ZERO -> Duration.parse("P2DT3H4M"))) - val yaml = sj.render(inst) - val comparison = """m: - | PT0S: PT51H4M - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleDuration](yaml)) - } - - test("With Instant Key") { - val inst = SampleInstant( - Map( - Instant.EPOCH -> Instant.MAX, - Instant.MIN -> Instant.parse("2007-12-03T10:15:30.00Z") - ) - ) - val yaml = sj.render(inst) - val comparison = """m: - | 1970-01-01T00:00:00Z: +1000000000-12-31T23:59:59.999999999Z - | -1000000000-01-01T00:00:00Z: 2007-12-03T10:15:30Z - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleInstant](yaml)) - } - - test("With LocalDateTime Key") { - val inst = SampleLocalDateTime( - Map( - LocalDateTime.MAX -> LocalDateTime.MIN, - LocalDateTime.parse("2007-12-03T10:15:30") -> null - ) - ) - val yaml = sj.render(inst) - val comparison = """m: - | +999999999-12-31T23:59:59.999999999: -999999999-01-01T00:00:00 - | 2007-12-03T10:15:30: null - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleLocalDateTime](yaml)) - } - - test("With LocalDate Key") { - val inst = SampleLocalDate( - Map( - LocalDate.MAX -> LocalDate.MIN, - LocalDate.parse("2007-12-03") -> null - ) - ) - val yaml = sj.render(inst) - val comparison = """m: - | +999999999-12-31: -999999999-01-01 - | 2007-12-03: null - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleLocalDate](yaml)) - } - - test("With LocalTime Key") { - val inst = SampleLocalTime( - Map( - LocalTime.MAX -> LocalTime.MIN, - LocalTime.MIDNIGHT -> LocalTime.NOON, - LocalTime.parse("10:15:30") -> null - ) - ) - val yaml = sj.render(inst) - val comparison = """m: - | 23:59:59.999999999: 00:00:00 - | 00:00:00: 12:00:00 - | 10:15:30: null - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleLocalTime](yaml)) - } - - test("With OffsetDateTime Key") { - val inst = SampleOffsetDateTime( - Map( - OffsetDateTime.MAX -> OffsetDateTime.MIN, - OffsetDateTime.parse("2007-12-03T10:15:30+01:00") -> null - ) - ) - val yaml = sj.render(inst) - val comparison = """m: - | +999999999-12-31T23:59:59.999999999-18:00: -999999999-01-01T00:00:00+18:00 - | 2007-12-03T10:15:30+01:00: null - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleOffsetDateTime](yaml)) - } - - test("With OffsetTime Key") { - val inst = SampleOffsetTime( - Map( - OffsetTime.MAX -> OffsetTime.MIN, - OffsetTime.parse("10:15:30+01:00") -> null - ) - ) - val yaml = sj.render(inst) - val comparison = """m: - | 23:59:59.999999999-18:00: 00:00:00+18:00 - | 10:15:30+01:00: null - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleOffsetTime](yaml)) - } - - test("With Period Key") { - val inst = SamplePeriod(Map(Period.ZERO -> Period.parse("P1Y2M3D"))) - val yaml = sj.render(inst) - val comparison = """m: - | P0D: P1Y2M3D - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SamplePeriod](yaml)) - } - - test("With ZonedDateTime Key") { - val inst = SampleZonedDateTime( - Map( - ZonedDateTime - .parse("2007-12-03T10:15:30+01:00[Europe/Paris]") -> null - ) - ) - val yaml = sj.render(inst) - val comparison = """m: - | 2007-12-03T10:15:30+01:00[Europe/Paris]: null - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleZonedDateTime](yaml)) - } \ No newline at end of file diff --git a/src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/ListCollKeys.scala b/src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/ListCollKeys.scala deleted file mode 100644 index 3befa84b..00000000 --- a/src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/ListCollKeys.scala +++ /dev/null @@ -1,212 +0,0 @@ -package co.blocke.scalajack -package yaml -package mapkeys - -import TestUtil._ -import munit._ -import munit.internal.console - -class ListCollKeys() extends FunSuite: - - val sj = ScalaJack(YamlFlavor()) - - test("List as key") { - describe( - "-------------------------------\n: List Map Key Tests (YAML) :\n-------------------------------", Console.BLUE - ) - val l1 = List(1, 2, 3) - val l2 = List(4, 5, 6) - val inst = Map(l1 -> l2) - val yaml = sj.render(inst) - val comparison = """? [1, 2, 3] - |: [4, 5, 6] - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[Map[List[Int], List[Int]]](yaml)) - } - - test("List of Lists as key") { - val l1 = List(List(1, 2, 3), List(9, 8, 7)) - val l2 = List(List(4, 5, 6), List(1, 3, 5)) - val inst = Map(l1 -> l2) - val yaml = sj.render(inst) - val comparison = """? - [1, 2, 3] - | - [9, 8, 7] - |: - [4, 5, 6] - | - [1, 3, 5] - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[Map[List[List[Int]], List[List[Int]]]](yaml)) - } - - test("List of Tuples as key") { - val l1: List[(String, String)] = List(("A", "a"), ("B", "b"), (null, "c")) - val l2: List[(String, String)] = List(("X", "x"), ("Y", "y"), (null, "z")) - val inst = Map(l1 -> l2) - val yaml = sj.render(inst) - val comparison = """? - [A, a] - | - [B, b] - | - [null, c] - |: - [X, x] - | - [Y, y] - | - [null, z] - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[Map[List[(String, String)], List[(String, String)]]](yaml)) - } - - test("List of Maps as key") { - val l1 = List(Map("wow" -> true), Map("ya" -> false)) - val l2 = List(Map("zing" -> false), Map("bling" -> true)) - val inst = Map(l1 -> l2) - val yaml = sj.render(inst) - val comparison = """? - wow: true - | - ya: false - |: - zing: false - | - bling: true - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[Map[List[Map[String, Boolean]], List[Map[String, Boolean]]]](yaml)) - } - - test("List of Case Class as key") { - val fish = FishPet("Flipper", Food.Meat, 68.9) - val inst = Map(List(fish, fish) -> List(fish, fish)) - val yaml = sj.render(inst) - val comparison = """? - name: Flipper - | food: Meat - | waterTemp: 68.9 - | - name: Flipper - | food: Meat - | waterTemp: 68.9 - |: - name: Flipper - | food: Meat - | waterTemp: 68.9 - | - name: Flipper - | food: Meat - | waterTemp: 68.9 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[Map[List[FishPet], List[FishPet]]](yaml)) - } - - test("List of Trait as key") { - val fish: Pet = FishPet("Flipper", Food.Meat, 68.9) - val inst = Map(List(fish, fish) -> List(fish, fish)) - val yaml = sj.render(inst) - val comparison = """? - _hint: co.blocke.scalajack.yaml.mapkeys.FishPet - | name: Flipper - | food: Meat - | waterTemp: 68.9 - | - _hint: co.blocke.scalajack.yaml.mapkeys.FishPet - | name: Flipper - | food: Meat - | waterTemp: 68.9 - |: - _hint: co.blocke.scalajack.yaml.mapkeys.FishPet - | name: Flipper - | food: Meat - | waterTemp: 68.9 - | - _hint: co.blocke.scalajack.yaml.mapkeys.FishPet - | name: Flipper - | food: Meat - | waterTemp: 68.9 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[Map[List[Pet], List[Pet]]](yaml)) - } - - test("List of Any as key") { - val inst: Map[List[Any], List[Any]] = - Map(List(23L, "wow", true) -> List(12.2, 0)) - val yaml = sj.render(inst) - val comparison = """? - 23 - | - wow - | - true - |: - 12.2 - | - 0.0 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(true, - sj.read[Map[List[Any], List[Any]]](yaml) - .isInstanceOf[Map[List[Any], List[Any]]] - ) - } - - test("List of parameterized class as key") { - val inst = Map( - List(AThing(true, "True"), AThing(false, "False")) -> List( - AThing(true, "Yes"), - AThing(false, "No") - ) - ) - val yaml = sj.render(inst) - val comparison = """? - a: true - | b: True - | - a: false - | b: False - |: - a: true - | b: Yes - | - a: false - | b: No - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(true, - sj.read[Map[List[AThing[String, Boolean]], List[AThing[String, Boolean]]]](yaml) - .isInstanceOf[Map[List[AThing[String, Boolean]], List[AThing[String, Boolean]]]] - ) - } - - test("List of parameterized trait as key") { - val inst: Map[List[Thing[Boolean, String]], List[Thing[Boolean, String]]] = - Map( - List(AThing(true, "True"), AThing(false, "False")) -> List( - AThing(true, "Yes"), - AThing(false, "No") - ) - ) - val yaml = sj.render(inst) - val comparison = """? - _hint: co.blocke.scalajack.yaml.mapkeys.AThing - | a: true - | b: True - | - _hint: co.blocke.scalajack.yaml.mapkeys.AThing - | a: false - | b: False - |: - _hint: co.blocke.scalajack.yaml.mapkeys.AThing - | a: true - | b: Yes - | - _hint: co.blocke.scalajack.yaml.mapkeys.AThing - | a: false - | b: No - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(true, - sj.read[Map[List[Thing[Boolean, String]], List[Thing[Boolean, String]]]](yaml) - .isInstanceOf[Map[List[Thing[Boolean, String]], List[Thing[Boolean, String]]]] - ) - } - - test("List of Optional as key") { - val inst: Map[List[Option[String]], List[Option[String]]] = - Map(List(Some("hey"), Some("you")) -> List(Some("stop"), Some("go"))) - val yaml = sj.render(inst) - val comparison = """? - hey - | - you - |: - stop - | - go - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[Map[List[Option[String]], List[Option[String]]]](yaml)) - } - - test("List of ValueClass as key") { - val inst = - Map(List(VCChar('A'), VCChar('a')) -> List(VCChar('B'), VCChar('b'))) - val yaml = sj.render(inst) - val comparison = """? - A - | - a - |: - B - | - b - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[Map[List[VCChar], List[VCChar]]](yaml)) - } \ No newline at end of file diff --git a/src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/MapCollKeys.scala b/src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/MapCollKeys.scala deleted file mode 100644 index 23fa9bd7..00000000 --- a/src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/MapCollKeys.scala +++ /dev/null @@ -1,311 +0,0 @@ -package co.blocke.scalajack -package yaml -package mapkeys - -import TestUtil._ -import munit._ -import munit.internal.console - -class MapCollKeys() extends FunSuite: - - val sj = ScalaJack(YamlFlavor()) - - test("Map as key") { - describe( - "------------------------------\n: Map Map Key Tests (YAML) :\n------------------------------", Console.BLUE - ) - val m1 = Map(1 -> 2) - val m2 = Map(3 -> 4) - val inst = Map(m1 -> m2) - val yaml = sj.render(inst) - val comparison = """? 1: 2 - |: 3: 4 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[Map[Map[Int, Int], Map[Int, Int]]](yaml)) - } - - test("Map as key, map value is null") { - val m1 = Map(1 -> 2) - val m2: Map[Int, Int] = null - val inst = Map(m1 -> m2) - val yaml = sj.render(inst) - val comparison = """? 1: 2 - |: null - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[Map[Map[Int, Int], Map[Int, Int]]](yaml)) - } - - test("Map of Lists as key") { - val m1 = List(Food.Meat, Food.Veggies) - val m2 = List(Food.Seeds, Food.Pellets) - val inst = Map(Map(m1 -> m2) -> Map(m2 -> m1)) - val yaml = sj.render(inst) - val comparison = """? ? - Meat - | - Veggies - | : - Seeds - | - Pellets - |: ? - Seeds - | - Pellets - | : - Meat - | - Veggies - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[Map[Map[List[Food.Value], List[Food.Value]], Map[List[Food.Value], List[Food.Value]]]](yaml)) - } - - test("Map of Maps as key") { - val m1 = Map(Food.Meat -> Food.Veggies) - val m2 = Map(Food.Seeds -> Food.Pellets) - val inst = Map(Map(m1 -> m2) -> Map(m2 -> m1)) - val yaml = sj.render(inst) - val comparison = """? ? Meat: Veggies - | : Seeds: Pellets - |: ? Seeds: Pellets - | : Meat: Veggies - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[Map[Map[Map[Food.Value, Food.Value], Map[Food.Value, Food.Value]], Map[Map[Food.Value, Food.Value], Map[Food.Value, Food.Value]]]](yaml)) - } - - test("Map of Tuples as key") { - val m1 = (Food.Meat, Food.Veggies) - val m2 = (Food.Seeds, Food.Pellets) - val inst = Map(Map(m1 -> m2) -> Map(m2 -> m1)) - val yaml = sj.render(inst) - val comparison = """? ? - Meat - | - Veggies - | : - Seeds - | - Pellets - |: ? - Seeds - | - Pellets - | : - Meat - | - Veggies - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, - sj.read[Map[Map[(Food.Value, Food.Value), (Food.Value, Food.Value)], Map[(Food.Value, Food.Value), (Food.Value, Food.Value)]]]( - yaml - ) - ) - } - - test("Map of Case Class as key") { - val m1 = - Map(DogPet("Fido", Food.Meat, 4) -> FishPet("Flipper", Food.Meat, 87.3)) - val m2 = - Map(FishPet("Flipper", Food.Meat, 87.3) -> DogPet("Fido", Food.Meat, 4)) - val inst = Map(Map(m1 -> m2) -> Map(m2 -> m1)) - val yaml = sj.render(inst) - val comparison = """? ? ? name: Fido - | food: Meat - | numLegs: 4 - | : name: Flipper - | food: Meat - | waterTemp: 87.3 - | : ? name: Flipper - | food: Meat - | waterTemp: 87.3 - | : name: Fido - | food: Meat - | numLegs: 4 - |: ? ? name: Flipper - | food: Meat - | waterTemp: 87.3 - | : name: Fido - | food: Meat - | numLegs: 4 - | : ? name: Fido - | food: Meat - | numLegs: 4 - | : name: Flipper - | food: Meat - | waterTemp: 87.3 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[Map[Map[Map[DogPet, FishPet], Map[FishPet, DogPet]], Map[Map[FishPet, DogPet], Map[DogPet, FishPet]]]](yaml)) - } - - test("Map of Trait as key") { - val m1: Map[Pet, Pet] = - Map(DogPet("Fido", Food.Meat, 4) -> FishPet("Flipper", Food.Meat, 87.3)) - val m2: Map[Pet, Pet] = - Map(FishPet("Flipper", Food.Meat, 87.3) -> DogPet("Fido", Food.Meat, 4)) - val inst = Map(Map(m1 -> m2) -> Map(m2 -> m1)) - val yaml = sj.render(inst) - val comparison = """? ? ? _hint: co.blocke.scalajack.yaml.mapkeys.DogPet - | name: Fido - | food: Meat - | numLegs: 4 - | : _hint: co.blocke.scalajack.yaml.mapkeys.FishPet - | name: Flipper - | food: Meat - | waterTemp: 87.3 - | : ? _hint: co.blocke.scalajack.yaml.mapkeys.FishPet - | name: Flipper - | food: Meat - | waterTemp: 87.3 - | : _hint: co.blocke.scalajack.yaml.mapkeys.DogPet - | name: Fido - | food: Meat - | numLegs: 4 - |: ? ? _hint: co.blocke.scalajack.yaml.mapkeys.FishPet - | name: Flipper - | food: Meat - | waterTemp: 87.3 - | : _hint: co.blocke.scalajack.yaml.mapkeys.DogPet - | name: Fido - | food: Meat - | numLegs: 4 - | : ? _hint: co.blocke.scalajack.yaml.mapkeys.DogPet - | name: Fido - | food: Meat - | numLegs: 4 - | : _hint: co.blocke.scalajack.yaml.mapkeys.FishPet - | name: Flipper - | food: Meat - | waterTemp: 87.3 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[Map[Map[Map[Pet, Pet], Map[Pet, Pet]], Map[Map[Pet, Pet], Map[Pet, Pet]]]](yaml)) - } - - test("Map of Any as key") { - val m1: Map[Any, Any] = Map(123.45 -> 2) - val m2: Map[Any, Any] = Map(398328372 -> 0) - val inst = Map(Map(m1 -> m2) -> Map(m2 -> m1)) - val yaml = sj.render(inst) - val comparison = """? ? 123.45: 2 - | : 398328372: 0 - |: ? 398328372: 0 - | : 123.45: 2 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(true, - sj.read[Map[Map[Map[Any, Any], Map[Any, Any]], Map[Map[Any, Any], Map[Any, Any]]]](yaml) - .isInstanceOf[Map[Map[Map[Any, Any], Map[Any, Any]], Map[Map[Any, Any], Map[Any, Any]]]] - ) - } - - test("Map of parameterized class as key") { - val m1: Map[AThing[Int, String], AThing[Int, String]] = - Map(AThing("one", 1) -> AThing("two", 2)) - val m2: Map[AThing[Int, String], AThing[Int, String]] = - Map(AThing("four", 4) -> AThing("three", 3)) - val inst = Map(Map(m1 -> m2) -> Map(m2 -> m1)) - val yaml = sj.render(inst) - val comparison = """? ? ? a: one - | b: 1 - | : a: two - | b: 2 - | : ? a: four - | b: 4 - | : a: three - | b: 3 - |: ? ? a: four - | b: 4 - | : a: three - | b: 3 - | : ? a: one - | b: 1 - | : a: two - | b: 2 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(true, - sj.read[Map[Map[Map[AThing[Int, String], AThing[Int, String]], Map[AThing[Int, String], AThing[Int, String]]], - Map[Map[AThing[Int, String], AThing[Int, String]], Map[AThing[Int, String], AThing[Int, String]]]]](yaml) - .isInstanceOf[Map[Map[Map[AThing[Int, String], AThing[Int, String]], Map[AThing[Int, String], AThing[Int, String]]], - Map[Map[AThing[Int, String], AThing[Int, String]], Map[AThing[Int, String], AThing[Int, String]]]]] - ) - } - - test("Map of parameterized trait as key") { - val m1: Map[Thing[String, Int], Thing[String, Int]] = - Map(AThing("one", 1) -> AThing("two", 2)) - val m2: Map[Thing[String, Int], Thing[String, Int]] = - Map(AThing("four", 4) -> AThing("three", 3)) - val inst = Map(Map(m1 -> m2) -> Map(m2 -> m1)) - val yaml = sj.render(inst) - val comparison = """? ? ? _hint: co.blocke.scalajack.yaml.mapkeys.AThing - | a: one - | b: 1 - | : _hint: co.blocke.scalajack.yaml.mapkeys.AThing - | a: two - | b: 2 - | : ? _hint: co.blocke.scalajack.yaml.mapkeys.AThing - | a: four - | b: 4 - | : _hint: co.blocke.scalajack.yaml.mapkeys.AThing - | a: three - | b: 3 - |: ? ? _hint: co.blocke.scalajack.yaml.mapkeys.AThing - | a: four - | b: 4 - | : _hint: co.blocke.scalajack.yaml.mapkeys.AThing - | a: three - | b: 3 - | : ? _hint: co.blocke.scalajack.yaml.mapkeys.AThing - | a: one - | b: 1 - | : _hint: co.blocke.scalajack.yaml.mapkeys.AThing - | a: two - | b: 2 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(true, - sj.read[Map[Map[Map[Thing[String, Int], Thing[String, Int]], Map[Thing[String, Int], Thing[String, Int]]], - Map[Map[Thing[String, Int], Thing[String, Int]], Map[Thing[String, Int], Thing[String, Int]]]]](yaml) - .isInstanceOf[Map[Map[Map[Thing[String, Int], Thing[String, Int]], Map[Thing[String, Int], Thing[String, Int]]], - Map[Map[Thing[String, Int], Thing[String, Int]], Map[Thing[String, Int], Thing[String, Int]]]]] - ) - } - - test("Map of Optional as key") { - val m1: Map[Option[Int], Option[Int]] = Map(Some(3) -> None) - val m2: Map[Option[Int], Option[Int]] = - Map(None -> Some(2), Some(5) -> null) - val inst = Map(Map(m1 -> m2) -> Map(m2 -> m1)) - val yaml = sj.render(inst) - val comparison = """? {}: - | 5: null - |: ? 5: null - | : {} - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assert( - Map( - Map(Map() -> Map(Some(5) -> None)) -> Map( - Map(Some(5) -> None) -> Map() - ) - ) == - sj.read[Map[Map[Map[Option[Int], Option[Int]], Map[Option[Int], Option[Int]]], Map[Map[Option[Int], Option[Int]], Map[Option[Int], Option[Int]]]]](yaml) - ) - } - - test("Map of Option as key where Option is null must fail") { - val m1: Map[Option[Int], Option[Int]] = Map(Some(3) -> None) - val m0 = Map.empty[Option[Int], Option[Int]] - val bad: Option[Int] = null - val m2 = m0 + (bad -> Some(99)) - val inst = Map(Map(m1 -> m2) -> Map(m2 -> m1)) - val msg = "Map keys cannot be null." - interceptMessage[ScalaJackError](msg){ - sj.render(inst) - } - } - - test("Map of ValueClass as key") { - val m1: Map[VCChar, VCChar] = Map(VCChar('Z') -> VCChar('z')) - val m2: Map[VCChar, VCChar] = Map(VCChar('A') -> VCChar('a')) - val inst = Map(Map(m1 -> m2) -> Map(m2 -> m1)) - val yaml = sj.render(inst) - val comparison = """? ? Z: z - | : A: a - |: ? A: a - | : Z: z - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[Map[Map[Map[VCChar, VCChar], Map[VCChar, VCChar]], Map[Map[VCChar, VCChar], Map[VCChar, VCChar]]]](yaml)) - } \ No newline at end of file diff --git a/src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/Model.scala b/src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/Model.scala deleted file mode 100644 index c6cebbae..00000000 --- a/src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/Model.scala +++ /dev/null @@ -1,175 +0,0 @@ -package co.blocke.scalajack -package yaml.mapkeys - -import java.util.UUID -import java.lang.{ - Boolean => JBoolean, - Byte => JByte, - Character => JChar, - Double => JDouble, - Float => JFloat, - Integer => JInteger, - Long => JLong, - Number => JNumber, - Short => JShort -} -import java.math.{BigDecimal => JBigDecimal, BigInteger => JBigInteger} -import java.time._ - -object Size extends Enumeration { - val Small, Medium, Large = Value -} - -trait Thing[A, B] { val a: A; val b: B } -case class AThing[Y, X](a: X, b: Y) extends Thing[X, Y] -trait Part[A] { val p: A } -case class APart[A](p: A) extends Part[A] - -// === Scala Primitive Keys -case class SampleBigDecimal(m: Map[BigDecimal, BigDecimal]) -case class SampleBigInt(m: Map[BigInt, BigInt]) -case class SampleBoolean(m: Map[Boolean, Boolean]) -case class SampleByte(m: Map[Byte, Byte]) -case class SampleChar(m: Map[Char, Char]) -case class SampleDouble(m: Map[Double, Double]) -case class SampleEnumeration(m: Map[Size.Value, Size.Value]) -case class SampleFloat(m: Map[Float, Float]) -case class SampleInt(m: Map[Int, Int]) -case class SampleLong(m: Map[Long, Long]) -case class SampleShort(m: Map[Short, Short]) - -// === Java Primitive Keys -case class SampleJBigDecimal(m: Map[JBigDecimal, JBigDecimal]) -case class SampleJBigInteger(m: Map[JBigInteger, JBigInteger]) -case class SampleJBoolean(m: Map[JBoolean, JBoolean]) -case class SampleJByte(m: Map[JByte, JByte]) -case class SampleJChar(m: Map[JChar, JChar]) -case class SampleJDouble(m: Map[JDouble, JDouble]) -case class SampleJFloat(m: Map[JFloat, JFloat]) -case class SampleJInteger(m: Map[JInteger, JInteger]) -case class SampleJLong(m: Map[JLong, JLong]) -case class SampleJNumber(m: Map[JNumber, JNumber]) -case class SampleJShort(m: Map[JShort, JShort]) - -// === Java Time Keys -case class SampleDuration(m: Map[Duration, Duration]) -case class SampleInstant(m: Map[Instant, Instant]) -case class SampleLocalDateTime(m: Map[LocalDateTime, LocalDateTime]) -case class SampleLocalDate(m: Map[LocalDate, LocalDate]) -case class SampleLocalTime(m: Map[LocalTime, LocalTime]) -case class SampleOffsetDateTime(m: Map[OffsetDateTime, OffsetDateTime]) -case class SampleOffsetTime(m: Map[OffsetTime, OffsetTime]) -case class SamplePeriod(m: Map[Period, Period]) -case class SampleZonedDateTime(m: Map[ZonedDateTime, ZonedDateTime]) - -// === Any primitives -case class AnyShell(m: Map[Any, Any]) - -// === Class Keys -case class SimpleClass(name: String, age: Int, isOk: Boolean, favorite: Any) -case class SampleSimple(m: Map[SimpleClass, SimpleClass]) -case class ComplexClass(id: UUID, simple: SimpleClass, allDone: Boolean) -case class SampleComplex(m: Map[ComplexClass, ComplexClass]) - -object Food extends Enumeration { - val Seeds, Meat, Pellets, Veggies = Value -} -trait Pet { - val name: String - val food: Food.Value -} -case class FishPet(name: String, food: Food.Value, waterTemp: Double) extends Pet -case class DogPet(name: String, food: Food.Value, numLegs: Int) extends Pet -case class CompoundPet(name: String, food: Food.Value, pet: Pet) extends Pet -trait PetHolder { - val address: String - val pet: Pet -} -case class ShinyPetHolder(address: String, pet: Pet) extends PetHolder -case class SamplePet(m: Map[Pet, Pet]) -case class PolyClass(lookup: Map[String, Int], favs: List[String]) -case class SamplePolyClass(m: Map[PolyClass, PolyClass]) -case class SampleShiny(m: Map[PetHolder, PetHolder]) -case class NCKey(nc: Map[Int, Boolean], name: String) -case class SampleNCKey(m: Map[NCKey, NCKey]) - -// === Collections - Tuple -case class SampleTuple(m: Map[(Int, String, Char), (String, Boolean)]) -case class SampleTupleList( - m: Map[(List[String], List[Int]), (List[String], List[Int])] -) -case class SampleTupleMap( - m: Map[(Map[String, Int], Map[Int, String]), (Map[String, Int], Map[Int, String])] -) -case class SampleTupleTuple( - m: Map[((String, Boolean), (Int, Double)), ((String, Boolean), (Int, Double))] -) -case class SampleTupleClass( - m: Map[(SampleChar, SampleInt), (SampleChar, SampleInt)] -) -case class SampleTupleTrait(m: Map[(Pet, Pet), (Pet, Pet)]) -case class SampleTupleAny(m: Map[(Any, Any), (Any, Any)]) -case class SampleTupleOptional( - m: Map[(Option[Int], Option[String]), (Option[Boolean], Option[Food.Value])] -) -case class SampleTupleVC(m: Map[(VCChar, VCChar), (VCChar, VCChar)]) -case class SampleTupleComplex( - m: Map[(ComplexClass, ComplexClass), (ComplexClass, ComplexClass)] -) -case class SampleTuplePolyClass( - m: Map[(PolyClass, PolyClass), (PolyClass, PolyClass)] -) - -// === Value Classes -case class VCBigDecimal(vc: BigDecimal) extends AnyVal -case class SampleVCBigDecimal(m: Map[VCBigDecimal, VCBigDecimal]) -case class VCBigInt(vc: BigInt) extends AnyVal -case class SampleVCBigInt(m: Map[VCBigInt, VCBigInt]) -case class VCBoolean(vc: Boolean) extends AnyVal -case class SampleVCBoolean(m: Map[VCBoolean, VCBoolean]) -case class VCByte(vc: Byte) extends AnyVal -case class SampleVCByte(m: Map[VCByte, VCByte]) -case class VCChar(vc: Char) extends AnyVal -case class SampleVCChar(m: Map[VCChar, VCChar]) -case class VCDouble(vc: Double) extends AnyVal -case class SampleVCDouble(m: Map[VCDouble, VCDouble]) -case class VCEnumeration(vc: Food.Value) extends AnyVal -case class SampleVCEnumeration(m: Map[VCEnumeration, VCEnumeration]) -case class VCFloat(vc: Float) extends AnyVal -case class SampleVCFloat(m: Map[VCFloat, VCFloat]) -case class VCInt(vc: Int) extends AnyVal -case class SampleVCInt(m: Map[VCInt, VCInt]) -case class VCLong(vc: Long) extends AnyVal -case class SampleVCLong(m: Map[VCLong, VCLong]) -case class VCShort(vc: Short) extends AnyVal -case class SampleVCShort(m: Map[VCShort, VCShort]) -case class VCString(vc: String) extends AnyVal -case class SampleVCString(m: Map[VCString, VCString]) -case class VCUUID(vc: UUID) extends AnyVal -case class SampleVCUUID(m: Map[VCUUID, VCUUID]) -case class VCNumber(vc: Number) extends AnyVal -case class SampleVCNumber(m: Map[VCNumber, VCNumber]) -case class VCList(vc: List[Int]) extends AnyVal -case class SampleVCList(m: Map[VCList, VCList]) -case class VCMap(vc: Map[Int, Int]) extends AnyVal -case class SampleVCMap(m: Map[VCMap, VCMap]) -case class VCTuple(vc: Tuple3[Int, String, Boolean]) extends AnyVal -case class SampleVCTuple(m: Map[VCTuple, VCTuple]) - -case class VCClass(vc: ComplexClass) extends AnyVal -case class SampleVCClass(m: Map[VCClass, VCClass]) -case class VCTrait(vc: Pet) extends AnyVal -case class SampleVCTrait(m: Map[VCTrait, VCTrait]) -case class VCOption(vc: Option[String]) extends AnyVal -case class SampleVCOption(m: Map[VCOption, VCOption]) -case class VCNested(vc: List[Map[String, String]]) extends AnyVal -case class SampleVCNested(m: Map[VCNested, VCNested]) - -case class VCParamClass[A, B](vc: AThing[A, B]) extends AnyVal -case class SampleVCParamClass[A, B]( - m: Map[VCParamClass[A, B], VCParamClass[A, B]] -) -case class VCParamTrait[A, B](vc: Thing[A, B]) extends AnyVal -case class SampleVCParamTrait[A, B]( - m: Map[VCParamTrait[A, B], VCParamTrait[A, B]] -) \ No newline at end of file diff --git a/src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/ScalaPrimKeys.scala b/src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/ScalaPrimKeys.scala deleted file mode 100644 index fa838092..00000000 --- a/src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/ScalaPrimKeys.scala +++ /dev/null @@ -1,308 +0,0 @@ -package co.blocke.scalajack -package yaml -package mapkeys - -import TestUtil._ -import munit._ -import munit.internal.console - -class ScalaPrimKeys() extends FunSuite: - - val sj = ScalaJack(YamlFlavor()) - - test("With Any Key") { - describe( - "------------------------------------------\n: Scala Primitive Map Key Tests (YAML) :\n------------------------------------------", Console.BLUE - ) - describe("+++ Positive Tests +++") - val inst = AnyShell( - Map( - List(1, 2, 3) -> List("a", "b", "c"), - DogPet("Fido", Food.Meat, 4) -> DogPet("Fifi", Food.Meat, 4), - Size.Small -> "ok", - 123.456 -> true, - 293845 -> "Greg", - false -> "16", - "Fred" -> "Wilma", - 16.toByte -> null - ) - ) - val yaml = sj.render(inst) - val comparison = """m: - | Fred: Wilma - | 293845: Greg - | ? _hint: co.blocke.scalajack.yaml.mapkeys.DogPet - | name: Fido - | food: Meat - | numLegs: 4 - | : _hint: co.blocke.scalajack.yaml.mapkeys.DogPet - | name: Fifi - | food: Meat - | numLegs: 4 - | 16: null - | false: '16' - | Small: ok - | 123.456: true - | ? - 1 - | - 2 - | - 3 - | : - a - | - b - | - c - |""".stripMargin.asInstanceOf[YAML] - assertEquals(yaml, comparison) - val read = sj.read[AnyShell](yaml).m.keySet.map(z => (z, z.getClass.getName)) - assertEquals(read.contains((16, "java.lang.Integer")), true) - assertEquals(read.contains((293845, "java.lang.Integer")), true) - assertEquals(read.contains((123.456, "java.lang.Double")), true) - assertEquals(read.contains(("Small", "java.lang.String")), true) - assertEquals(read.contains(("Fred", "java.lang.String")), true) - assertEquals(read.contains((false, "java.lang.Boolean")), true) - assertEquals(read.contains( - ( - DogPet("Fido", Food.Meat, 4), - "co.blocke.scalajack.yaml.mapkeys.DogPet" - ) - ), true) - } - - test("With BigDecimal Key") { - val inst = SampleBigDecimal( - Map( - BigDecimal(123.456) -> BigDecimal(1), - BigDecimal(789.123) -> BigDecimal(2) - ) - ) - val yaml = sj.render(inst) - val comparison = """m: - | 123.456: !!float '1' - | 789.123: !!float '2' - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleBigDecimal](yaml)) - } - - test("With BigInt Key") { - val inst = - SampleBigInt(Map(BigInt(123) -> BigInt(1), BigInt(789) -> BigInt(2))) - val yaml = sj.render(inst) - val comparison = """m: - | 123: 1 - | 789: 2 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleBigInt](yaml)) - } - - test("With Boolean Key") { - val inst = SampleBoolean(Map(true -> false, false -> true)) - val yaml = sj.render(inst) - val comparison = """m: - | true: false - | false: true - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleBoolean](yaml)) - } - - test("With Byte Key") { - val inst = SampleByte(Map(16.toByte -> 2.toByte, 48.toByte -> 9.toByte)) - val yaml = sj.render(inst) - val comparison = """m: - | 16: 2 - | 48: 9 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleByte](yaml)) - } - - test("With Char Key") { - val inst = SampleChar(Map('a' -> 'A', 'z' -> 'Z')) - val yaml = sj.render(inst) - val comparison = """m: - | a: A - | z: Z - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleChar](yaml)) - } - - test("With Double Key") { - val inst = SampleDouble(Map(12.34 -> 56.78, 90.12 -> 34.56)) - val yaml = sj.render(inst) - val comparison = """m: - | 12.34: 56.78 - | 90.12: 34.56 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleDouble](yaml)) - } - - test("With Enumeration Key") { - val inst = SampleEnumeration( - Map(Size.Small -> Size.Large, Size.Large -> Size.Medium) - ) - val yaml = sj.render(inst) - val comparison = """m: - | Small: Large - | Large: Medium - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleEnumeration](yaml)) - } - - test("With Float Key") { - val inst = SampleFloat(Map(12.34F -> 56.78F, 90.12F -> 34.56F)) - val yaml = sj.render(inst) - val comparison = """m: - | 12.34: 56.78 - | 90.12: 34.56 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleFloat](yaml)) - } - - test("With Int Key") { - val inst = SampleInt(Map(12 -> 56, 90 -> 34)) - val yaml = sj.render(inst) - val comparison = """m: - | 12: 56 - | 90: 34 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleInt](yaml)) - } - - test("With Long Key") { - val inst = SampleLong(Map(12L -> 56L, 90L -> 34L)) - val yaml = sj.render(inst) - val comparison = """m: - | 12: 56 - | 90: 34 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleLong](yaml)) - } - - test("With Short Key") { - val inst = - SampleShort(Map(12.toShort -> 56.toShort, 90.toShort -> 34.toShort)) - val yaml = sj.render(inst) - val comparison = """m: - | 12: 56 - | 90: 34 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleShort](yaml)) - } - - test("Bad BigDecimal Key") { - describe("--- Negative Tests ---") - val yaml = - """m: - | 789.123: 1 - | fred: 2""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 2: Expected a Number value here: =VAL :fred"){ - sj.read[SampleBigDecimal](yaml) - } - } - - test("Bad BigInt Key") { - val yaml = - """m: - | fred: 1 - | 789: 2""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 1: Expected a Number value here: =VAL :fred"){ - sj.read[SampleBigInt](yaml) - } - } - - test("Bad Boolean Key") { - val yaml = - """m: - | true: false - | 123: true""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 2: Expected a Boolean value here: =VAL :123"){ - sj.read[SampleBoolean](yaml) - } - } - - test("Bad Byte Key") { - val yaml = - """m: - | 16: 2 - | x48: 9""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 2: Expected a Number value here: =VAL :x48"){ - sj.read[SampleByte](yaml) - } - } - - test("Bad Char Key") { // NOTE: This comprehensively tests for any null keyed Map - val yaml = - """m: - | null: A - | z: Z""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 1: A Char typed value cannot be null"){ - sj.read[SampleChar](yaml) - } - } - - test("Bad Double Key") { - val yaml = - """m: - | 12.34: 56.78 - | true: 34.56""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 2: Expected a Number value here: =VAL :true"){ - sj.read[SampleDouble](yaml) - } - } - - test("Bad Enumeration Key") { - val yaml = - """m: - | Small: Large - | Bogus: Medium""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 2: No value found in enumeration co.blocke.scalajack.yaml.mapkeys.Size$ for Bogus"){ - sj.read[SampleEnumeration](yaml) - } - } - - test("Bad Float Key") { - val yaml = - """m: - | 12.34: 56.78 - | 90.12.3: 34.56""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 2: Cannot parse an Float from value"){ - sj.read[SampleFloat](yaml) - } - } - - test("Bad Int Key") { - val yaml = - """m: - | 12.0: 56 - | 90: 34""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 1: Cannot parse an Int from value"){ - sj.read[SampleInt](yaml) - } - } - - test("Bad Long Key") { - val yaml = - """m: - | 12: 56 - | hey: 34""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 2: Expected a Number value here: =VAL :hey"){ - sj.read[SampleLong](yaml) - } - } - - test("Bad Short Key") { - val yaml = - """m: - | p99999: 56 - | 90: 34""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 1: Expected a Number value here: =VAL :p99999"){ - sj.read[SampleShort](yaml) - } - } \ No newline at end of file diff --git a/src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/TupleCollKeys.scala b/src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/TupleCollKeys.scala deleted file mode 100644 index 9220a090..00000000 --- a/src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/TupleCollKeys.scala +++ /dev/null @@ -1,450 +0,0 @@ -package co.blocke.scalajack -package yaml -package mapkeys - -import TestUtil._ -import munit._ -import munit.internal.console -import java.util.UUID -import model.StringMatchHintModifier -import co.blocke.scala_reflection.RType - -class TupleCollKeys() extends FunSuite: - - val sj = ScalaJack(YamlFlavor()) - - test("Tuple as key") { - describe( - "--------------------------------\n: Tuple Map Key Tests (YAML) :\n--------------------------------", Console.BLUE - ) - val a = (2, "Blather", 'Q') - val b = ("Foo", true) - val inst = SampleTuple(Map(a -> b)) - val yaml = sj.render(inst) - val comparison = """m: - | ? [2, Blather, Q] - | : [Foo, true] - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleTuple](yaml)) - } - - test("Tuple as key, null tuple as value") { - val a = (2, "Blather", 'Q') - val b: (String, Boolean) = null - val inst = SampleTuple(Map(a -> b)) - val yaml = sj.render(inst) - val comparison = """m: - | ? [2, Blather, Q] - | : null - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleTuple](yaml)) - } - - test("Tuple of Lists as key") { - val a = (List("one", "two", "three"), List(1, 2, 3)) - val b = (List("four", "five", "six"), List(4, 5, 6)) - val inst = SampleTupleList(Map(a -> b)) - val yaml = sj.render(inst) - val comparison = """m: - | ? - [one, two, three] - | - [1, 2, 3] - | : - [four, five, six] - | - [4, 5, 6] - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleTupleList](yaml)) - } - - test("Tuple of Maps as key") { - val a = (Map("one" -> 1), Map(2 -> "two")) - val b = (Map("three" -> 3), Map(4 -> "four")) - val inst = SampleTupleMap(Map(a -> b)) - val yaml = sj.render(inst) - val comparison = """m: - | ? - one: 1 - | - 2: two - | : - three: 3 - | - 4: four - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleTupleMap](yaml)) - } - - test("Tuple of Tuples as key") { - val a = (("yes", true), (1, 0.25)) - val b = (("no", false), (2, 0.5)) - val inst = SampleTupleTuple(Map(a -> b)) - val yaml = sj.render(inst) - val comparison = """m: - | ? - [yes, true] - | - [1, 0.25] - | : - [no, false] - | - [2, 0.5] - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleTupleTuple](yaml)) - } - - test("Tuple of Case Class as key") { - val a = (SampleChar(Map('a' -> 'A')), SampleInt(Map(1 -> 2))) - val b = (SampleChar(Map('z' -> 'Z')), SampleInt(Map(99 -> 100))) - val inst = SampleTupleClass(Map(a -> b)) - val yaml = sj.render(inst) - val comparison = """m: - | ? - m: - | a: A - | - m: - | 1: 2 - | : - m: - | z: Z - | - m: - | 99: 100 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleTupleClass](yaml)) - } - - test("Tuple of Trait as key") { - val a = (DogPet("Fido", Food.Meat, 4), FishPet("Jaws", Food.Meat, 69.8)) - val b = - (DogPet("Chey", Food.Meat, 3), FishPet("Flipper", Food.Seeds, 80.1)) - val inst = SampleTupleTrait(Map(a -> b)) - val yaml = sj.render(inst) - val comparison = """m: - | ? - _hint: co.blocke.scalajack.yaml.mapkeys.DogPet - | name: Fido - | food: Meat - | numLegs: 4 - | - _hint: co.blocke.scalajack.yaml.mapkeys.FishPet - | name: Jaws - | food: Meat - | waterTemp: 69.8 - | : - _hint: co.blocke.scalajack.yaml.mapkeys.DogPet - | name: Chey - | food: Meat - | numLegs: 3 - | - _hint: co.blocke.scalajack.yaml.mapkeys.FishPet - | name: Flipper - | food: Seeds - | waterTemp: 80.1 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleTupleTrait](yaml)) - } - - test("Tuple of Any as key") { - val a = (DogPet("Fido", Food.Meat, 4), FishPet("Jaws", Food.Meat, 69.8)) - val b = - (DogPet("Chey", Food.Meat, 3), FishPet("Flipper", Food.Seeds, 80.1)) - val inst = SampleTupleTrait(Map(a -> b)) - val yaml = sj.render(inst) - val comparison = """m: - | ? - _hint: co.blocke.scalajack.yaml.mapkeys.DogPet - | name: Fido - | food: Meat - | numLegs: 4 - | - _hint: co.blocke.scalajack.yaml.mapkeys.FishPet - | name: Jaws - | food: Meat - | waterTemp: 69.8 - | : - _hint: co.blocke.scalajack.yaml.mapkeys.DogPet - | name: Chey - | food: Meat - | numLegs: 3 - | - _hint: co.blocke.scalajack.yaml.mapkeys.FishPet - | name: Flipper - | food: Seeds - | waterTemp: 80.1 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleTupleTrait](yaml)) - } - - test("Tuple of Optional as key") { - val a = (Some(5), Some("Fred")) - val b = (None, Some(Food.Meat)) - val inst = SampleTupleOptional(Map(a -> b)) - val yaml = sj.render(inst) - val comparison = """m: - | ? - 5 - | - Fred - | : - null - | - Meat - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleTupleOptional](yaml)) - } - - test("Tuple of ValueClass as key") { - val a = (VCChar('a'), VCChar('A')) - val b = (VCChar('z'), VCChar('Z')) - val inst = SampleTupleVC(Map(a -> b)) - val yaml = sj.render(inst) - val comparison = """m: - | ? - a - | - A - | : - z - | - Z - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleTupleVC](yaml)) - } - - test("Complex class (having members that are classes) as key") { - val a = SimpleClass("Larry", 32, isOk = true, "golf") - val b = SimpleClass("Mike", 27, isOk = false, 125) - val c1 = ComplexClass( - UUID.fromString("1e6c2b31-4dfe-4bf6-a0a0-882caaff0e9c"), - a, - allDone = true - ) - val c2 = ComplexClass( - UUID.fromString("1e6c2b31-4dfe-4bf6-a0a0-882caaff0e9d"), - b, - allDone = false - ) - val t1 = (c1, c2) - val t2 = (c2, c1) - val inst = SampleTupleComplex(Map(t1 -> t2)) - val yaml = sj.render(inst) - val comparison = """m: - | ? - id: 1e6c2b31-4dfe-4bf6-a0a0-882caaff0e9c - | simple: - | name: Larry - | age: 32 - | isOk: true - | favorite: golf - | allDone: true - | - id: 1e6c2b31-4dfe-4bf6-a0a0-882caaff0e9d - | simple: - | name: Mike - | age: 27 - | isOk: false - | favorite: 125 - | allDone: false - | : - id: 1e6c2b31-4dfe-4bf6-a0a0-882caaff0e9d - | simple: - | name: Mike - | age: 27 - | isOk: false - | favorite: 125 - | allDone: false - | - id: 1e6c2b31-4dfe-4bf6-a0a0-882caaff0e9c - | simple: - | name: Larry - | age: 32 - | isOk: true - | favorite: golf - | allDone: true - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleTupleComplex](yaml)) - } - - test("Class having collections as members") { - val a = PolyClass(Map("a" -> 1, "b" -> 2), List("one", "two")) - val b = PolyClass(Map("x" -> 9, "y" -> 10), List("aye", "you")) - val t1 = (a, b) - val t2 = (b, a) - val inst = SampleTuplePolyClass(Map(t1 -> t2)) - val yaml = sj.render(inst) - val comparison = """m: - | ? - lookup: - | a: 1 - | b: 2 - | favs: [one, two] - | - lookup: - | x: 9 - | y: 10 - | favs: [aye, you] - | : - lookup: - | x: 9 - | y: 10 - | favs: [aye, you] - | - lookup: - | a: 1 - | b: 2 - | favs: [one, two] - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleTuplePolyClass](yaml)) - } - - test("Class having collections as members (empty collections") { - val a = PolyClass(Map.empty[String, Int], List.empty[String]) - val b = PolyClass(Map.empty[String, Int], List.empty[String]) - val t1 = (a, b) - val t2 = (b, a) - val inst = Map(t1 -> t2) - val yaml = sj.render(inst) - val comparison = """? - lookup: {} - | favs: [] - | - lookup: {} - | favs: [] - |: - lookup: {} - | favs: [] - | - lookup: {} - | favs: [] - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[Map[(PolyClass, PolyClass), (PolyClass, PolyClass)]](yaml)) - } - - test("Custom trait hint field and value for key trait") { - val petHintMod = StringMatchHintModifier( - Map("BreathsWater" -> "co.blocke.scalajack.yaml.mapkeys.FishPet", "BreathsAir" -> "co.blocke.scalajack.yaml.mapkeys.DogPet") - ) - val sj2 = sj - .withHints(RType.of[Pet] -> "kind") - .withHintModifiers(RType.of[Pet] -> petHintMod) - val a: Pet = FishPet("Flipper", Food.Veggies, 74.33) - val b: Pet = DogPet("Fido", Food.Meat, 3) - val t1 = (a, b) - val t2 = (b, a) - val inst = Map(t1 -> t2) - val yaml = sj2.render(inst) - val comparison = """? - kind: BreathsWater - | name: Flipper - | food: Veggies - | waterTemp: 74.33 - | - kind: BreathsAir - | name: Fido - | food: Meat - | numLegs: 3 - |: - kind: BreathsAir - | name: Fido - | food: Meat - | numLegs: 3 - | - kind: BreathsWater - | name: Flipper - | food: Veggies - | waterTemp: 74.33 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj2.read[Map[(Pet, Pet), (Pet, Pet)]](yaml)) - } - - test("Custom trait hint field and value for key member's trait") { - val petHintMod = StringMatchHintModifier( - Map("BreathsWater" -> "co.blocke.scalajack.yaml.mapkeys.FishPet", "BreathsAir" -> "co.blocke.scalajack.yaml.mapkeys.DogPet") - ) - val sj2 = sj - .withHints(RType.of[Pet] -> "kind") - .withHintModifiers(RType.of[Pet] -> petHintMod) - - val a: PetHolder = - ShinyPetHolder("123 Main", FishPet("Flipper", Food.Veggies, 74.33)) - val b: PetHolder = - ShinyPetHolder("210 North", DogPet("Fido", Food.Meat, 3)) - val t1 = (a, b) - val t2 = (b, a) - val inst = Map(t1 -> t2) - val yaml = sj2.render(inst) - val comparison = """? - _hint: co.blocke.scalajack.yaml.mapkeys.ShinyPetHolder - | address: 123 Main - | pet: - | kind: BreathsWater - | name: Flipper - | food: Veggies - | waterTemp: 74.33 - | - _hint: co.blocke.scalajack.yaml.mapkeys.ShinyPetHolder - | address: 210 North - | pet: - | kind: BreathsAir - | name: Fido - | food: Meat - | numLegs: 3 - |: - _hint: co.blocke.scalajack.yaml.mapkeys.ShinyPetHolder - | address: 210 North - | pet: - | kind: BreathsAir - | name: Fido - | food: Meat - | numLegs: 3 - | - _hint: co.blocke.scalajack.yaml.mapkeys.ShinyPetHolder - | address: 123 Main - | pet: - | kind: BreathsWater - | name: Flipper - | food: Veggies - | waterTemp: 74.33 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj2.read[Map[(PetHolder, PetHolder), (PetHolder, PetHolder)]](yaml)) - } - - test("Parameterized class") { - val t1 = (AThing("wow", 4), AThing("boom", 1)) - val t2 = (AThing("yep", 3), AThing("yikes", 11)) - val inst = Map(t1 -> t2) - val yaml = sj.render(inst) - val comparison = """? - a: wow - | b: 4 - | - a: boom - | b: 1 - |: - a: yep - | b: 3 - | - a: yikes - | b: 11 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[Map[(AThing[Int, String], AThing[Int, String]), (AThing[Int, String], AThing[Int, String])]](yaml)) - } - - test("Parameterized trait") { - val t1: (Thing[String, Int], Thing[String, Int]) = - (AThing("wow", 4), AThing("boom", 1)) - val t2: (Thing[String, Int], Thing[String, Int]) = - (AThing("yep", 3), AThing("yikes", 11)) - val inst = Map(t1 -> t2) - val yaml = sj.render(inst) - val comparison = """? - _hint: co.blocke.scalajack.yaml.mapkeys.AThing - | a: wow - | b: 4 - | - _hint: co.blocke.scalajack.yaml.mapkeys.AThing - | a: boom - | b: 1 - |: - _hint: co.blocke.scalajack.yaml.mapkeys.AThing - | a: yep - | b: 3 - | - _hint: co.blocke.scalajack.yaml.mapkeys.AThing - | a: yikes - | b: 11 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[Map[(Thing[String, Int], Thing[String, Int]), (Thing[String, Int], Thing[String, Int])]](yaml)) - } - - test("Parameterized trait having parameterized trait members") { - val t1: (Thing[String, Part[Double]], Thing[String, Part[Double]]) = - (AThing("wow", APart(1.2)), AThing("boom", APart(2.3))) - val t2: (Thing[String, Part[Double]], Thing[String, Part[Double]]) = - (AThing("yep", APart(4.5)), AThing("yikes", APart(6.7))) - val inst = Map(t1 -> t2) - val yaml = sj.render(inst) - val comparison = """? - _hint: co.blocke.scalajack.yaml.mapkeys.AThing - | a: wow - | b: - | _hint: co.blocke.scalajack.yaml.mapkeys.APart - | p: 1.2 - | - _hint: co.blocke.scalajack.yaml.mapkeys.AThing - | a: boom - | b: - | _hint: co.blocke.scalajack.yaml.mapkeys.APart - | p: 2.3 - |: - _hint: co.blocke.scalajack.yaml.mapkeys.AThing - | a: yep - | b: - | _hint: co.blocke.scalajack.yaml.mapkeys.APart - | p: 4.5 - | - _hint: co.blocke.scalajack.yaml.mapkeys.AThing - | a: yikes - | b: - | _hint: co.blocke.scalajack.yaml.mapkeys.APart - | p: 6.7 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[Map[(Thing[String, Part[Double]], Thing[String, Part[Double]]), (Thing[String, Part[Double]], Thing[String, Part[Double]])]](yaml)) - } \ No newline at end of file diff --git a/src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/ValueClassKeys.scala b/src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/ValueClassKeys.scala deleted file mode 100644 index f8937334..00000000 --- a/src_old/test/scala/co.blocke.scalajack/yaml/mapkeys/ValueClassKeys.scala +++ /dev/null @@ -1,569 +0,0 @@ -package co.blocke.scalajack -package yaml -package mapkeys - -import TestUtil._ -import munit._ -import munit.internal.console -import java.util.UUID - -class ValueClassKeys() extends FunSuite: - - val sj = ScalaJack(YamlFlavor()) - - test("Value class of BigDecimal") { - describe( - "-------------------------------------\n: ValueClass Map Key Tests (YAML) :\n-------------------------------------", Console.BLUE - ) - describe("+++ Positive Primitive Tests +++") - val inst = SampleVCBigDecimal( - Map( - VCBigDecimal(BigDecimal(12.34)) -> VCBigDecimal(BigDecimal(56.78)) - ) - ) - val yaml = sj.render(inst) - val comparison = """m: - | 12.34: 56.78 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleVCBigDecimal](yaml)) - } - - test("Value class of BigInt") { - val inst = - SampleVCBigInt(Map(VCBigInt(BigInt(12)) -> VCBigInt(BigInt(56)))) - val yaml = sj.render(inst) - val comparison = """m: - | 12: 56 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleVCBigInt](yaml)) - } - - test("Value class of Byte") { - val inst = SampleVCByte( - Map(VCByte(12.asInstanceOf[Byte]) -> VCByte(56.asInstanceOf[Byte])) - ) - val yaml = sj.render(inst) - val comparison = """m: - | 12: 56 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleVCByte](yaml)) - } - - test("Value class of Boolean") { - val inst = SampleVCBoolean(Map(VCBoolean(true) -> VCBoolean(false))) - val yaml = sj.render(inst) - val comparison = """m: - | true: false - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleVCBoolean](yaml)) - } - - test("Value class of Char") { - val inst = SampleVCChar(Map(VCChar('a') -> VCChar('A'))) - val yaml = sj.render(inst) - val comparison = """m: - | a: A - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleVCChar](yaml)) - } - - test("Value class of Double") { - val inst = SampleVCDouble(Map(VCDouble(12.34) -> VCDouble(56.78))) - val yaml = sj.render(inst) - val comparison = """m: - | 12.34: 56.78 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleVCDouble](yaml)) - } - - test("Value class of Enumeration") { - val inst = SampleVCEnumeration( - Map(VCEnumeration(Food.Veggies) -> VCEnumeration(Food.Meat)) - ) - val yaml = sj.render(inst) - val comparison = """m: - | Veggies: Meat - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleVCEnumeration](yaml)) - } - - test("Value class of Float") { - val inst = SampleVCFloat(Map(VCFloat(12.34F) -> VCFloat(56.2F))) - val yaml = sj.render(inst) - val comparison = """m: - | 12.34: 56.2 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleVCFloat](yaml)) - } - - test("Value class of Int") { - val inst = SampleVCInt(Map(VCInt(12) -> VCInt(56))) - val yaml = sj.render(inst) - val comparison = """m: - | 12: 56 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleVCInt](yaml)) - } - - test("Value class of Long") { - val inst = SampleVCLong(Map(VCLong(12L) -> VCLong(56L))) - val yaml = sj.render(inst) - val comparison = """m: - | 12: 56 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleVCLong](yaml)) - } - - test("Value class of Short") { - val inst = SampleVCShort( - Map( - VCShort(12.asInstanceOf[Short]) -> VCShort(56.asInstanceOf[Short]) - ) - ) - val yaml = sj.render(inst) - val comparison = """m: - | 12: 56 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleVCShort](yaml)) - } - - test("Value class of String") { - val inst = SampleVCString(Map(VCString("A") -> VCString("B"))) - val yaml = sj.render(inst) - val comparison = """m: - | A: B - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleVCString](yaml)) - } - - test("Value class of UUID") { - val inst = SampleVCUUID( - Map( - VCUUID(UUID.fromString("54cab778-7b9e-4b07-9d37-87b97a011e56")) -> VCUUID( - UUID.fromString("54cab778-7b9e-4b07-9d37-87b97a011e55") - ) - ) - ) - val yaml = sj.render(inst) - val comparison = """m: - | 54cab778-7b9e-4b07-9d37-87b97a011e56: 54cab778-7b9e-4b07-9d37-87b97a011e55 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleVCUUID](yaml)) - } - - test("Value class of List") { - describe("+++ Positive Collection Tests +++") - val inst = - SampleVCList(Map(VCList(List(1, 2, 3)) -> VCList(List(4, 5, 6)))) - val yaml = sj.render(inst) - val comparison = """m: - | ? [1, 2, 3] - | : [4, 5, 6] - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleVCList](yaml)) - } - - test("Value class of List (empty List)") { - val inst = - SampleVCList(Map(VCList(List.empty[Int]) -> VCList(List.empty[Int]))) - val yaml = sj.render(inst) - val comparison = """m: - | []: [] - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleVCList](yaml)) - } - - test("Value class of Map") { - val inst = SampleVCMap(Map(VCMap(Map(1 -> 2)) -> VCMap(Map(3 -> 4)))) - val yaml = sj.render(inst) - val comparison = """m: - | ? 1: 2 - | : 3: 4 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleVCMap](yaml)) - } - - test("Value class of Map (empty Map") { - val inst = SampleVCMap( - Map(VCMap(Map.empty[Int, Int]) -> VCMap(Map.empty[Int, Int])) - ) - val yaml = sj.render(inst) - val comparison = """m: - | {}: {} - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleVCMap](yaml)) - } - - test("Value class of Tupple") { - val inst = SampleVCTuple( - Map(VCTuple((1, "one", true)) -> VCTuple((2, "two", false))) - ) - val yaml = sj.render(inst) - val comparison = """m: - | ? [1, one, true] - | : [2, two, false] - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleVCTuple](yaml)) - } - - test("Value class of Case Class") { - describe("+++ Positive Complex Tests +++") - val a = SimpleClass("Larry", 32, isOk = true, "golf") - val b = SimpleClass("Mike", 27, isOk = false, 125) - val c1 = ComplexClass( - UUID.fromString("1e6c2b31-4dfe-4bf6-a0a0-882caaff0e9c"), - a, - allDone = true - ) - val c2 = ComplexClass( - UUID.fromString("1e6c2b31-4dfe-4bf6-a0a0-882caaff0e9d"), - b, - allDone = false - ) - val inst = SampleVCClass(Map(VCClass(c1) -> VCClass(c2))) - val yaml = sj.render(inst) - val comparison = """m: - | ? id: 1e6c2b31-4dfe-4bf6-a0a0-882caaff0e9c - | simple: - | name: Larry - | age: 32 - | isOk: true - | favorite: golf - | allDone: true - | : id: 1e6c2b31-4dfe-4bf6-a0a0-882caaff0e9d - | simple: - | name: Mike - | age: 27 - | isOk: false - | favorite: 125 - | allDone: false - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleVCClass](yaml)) - } - - test("Value class of Trait") { - val a: Pet = FishPet("Flipper", Food.Veggies, 74.33) - val b: Pet = DogPet("Fido", Food.Meat, 3) - val inst = SampleVCTrait(Map(VCTrait(a) -> VCTrait(b))) - val yaml = sj.render(inst) - val comparison = """m: - | ? _hint: co.blocke.scalajack.yaml.mapkeys.FishPet - | name: Flipper - | food: Veggies - | waterTemp: 74.33 - | : _hint: co.blocke.scalajack.yaml.mapkeys.DogPet - | name: Fido - | food: Meat - | numLegs: 3 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleVCTrait](yaml)) - } - - test("Value class of Parameterized Case Class") { - val a = AThing(5, "wow") - val b = AThing(6, "zoom") - val inst = SampleVCParamClass(Map(VCParamClass(a) -> VCParamClass(b))) - val yaml = sj.render(inst) - val comparison = """m: - | ? a: 5 - | b: wow - | : a: 6 - | b: zoom - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleVCParamClass[String, Int]](yaml)) - } - - test("Value class of Parameterized Trait") { - val a: Thing[Int, String] = AThing(5, "wow") - val b: Thing[Int, String] = AThing(6, "zoom") - val inst = SampleVCParamTrait(Map(VCParamTrait(a) -> VCParamTrait(b))) - val yaml = sj.render(inst) - val comparison = """m: - | ? _hint: co.blocke.scalajack.yaml.mapkeys.AThing - | a: 5 - | b: wow - | : _hint: co.blocke.scalajack.yaml.mapkeys.AThing - | a: 6 - | b: zoom - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleVCParamTrait[Int, String]](yaml)) - } - - test("Value class of Option") { - val inst = - SampleVCOption(Map(VCOption(Some("here")) -> VCOption(Some("there")))) - val yaml = sj.render(inst) - val comparison = """m: - | here: there - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleVCOption](yaml)) - } - - test("Value class of nested collection") { - val a = VCNested(List(Map("a" -> "b"), Map("c" -> "d"))) - val b = VCNested(List(Map("t" -> "u"), Map("x" -> "y"))) - val inst = SampleVCNested(Map(a -> b)) - val yaml = sj.render(inst) - val comparison = """m: - | ? - a: b - | - c: d - | : - t: u - | - x: y - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[SampleVCNested](yaml)) - } - - test("Bad BigDecimal Key") { - describe("--- Negative Primitive Tests ---") - val yaml = """m: - | true: 56.78""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 1: Expected a Number value here: =VAL :true"){ - sj.read[SampleVCBigDecimal](yaml) - } - } - - test("Bad BigInt Key") { - val yaml = """m: - | "12.5": 56""".stripMargin.asInstanceOf[YAML] - interceptMessage[java.lang.NumberFormatException]("For input string: \"12.5\""){ - sj.read[SampleVCBigInt](yaml) - } - } - - test("Bad Boolean Key") { - val yaml = """m: - | 1: false""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 1: Expected a Boolean value here: =VAL :1"){ - sj.read[SampleVCBoolean](yaml) - } - } - - test("Bad Char Key") { - val yaml = """m: - | null: A""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 1: A Char typed value cannot be null"){ - sj.read[SampleVCChar](yaml) - } - } - - test("Bad Double Key") { - val yaml = """m: - | false: 56.78""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 1: Expected a Number value here: =VAL :false"){ - sj.read[SampleVCDouble](yaml) - } - } - - test("Bad Enumeration Key") { - val yaml = """m: - | Bogus: Meat""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 1: No value found in enumeration co.blocke.scalajack.yaml.mapkeys.Food$ for Bogus"){ - sj.read[SampleVCEnumeration](yaml) - } - } - - test("Bad Float Key") { - val yaml = """m: - | hey: 56.2""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 1: Expected a Number value here: =VAL :hey"){ - sj.read[SampleVCFloat](yaml) - } - } - - test("Bad Int Key") { - val yaml = """m: - | "12.5": 56""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 1: Cannot parse an Int from value"){ - sj.read[SampleVCInt](yaml) - } - } - - test("Bad Long Key") { - val yaml = """m: - | "12.5": 56""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 1: Cannot parse an Long from value"){ - sj.read[SampleVCLong](yaml) - } - } - - test("Bad Short Key") { - val yaml = """m: - | "12.5": 56""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 1: Cannot parse an Short from value"){ - sj.read[SampleVCShort](yaml) - } - } - - test("Bad String Key") { - val yaml = """m: - | [1,2]: B""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 1: Expected a String here: +SEQ"){ - sj.read[SampleVCString](yaml) - } - } - - test("Bad UUID Key") { - val yaml = """m: - | bogus: "54cab778-7b9e-4b07-9d37-87b97a011e55"""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 1: Failed to create UUID value from parsed text bogus"){ - sj.read[SampleVCUUID](yaml) - } - } - - test("Bad List Key") { - describe("--- Negative Collection Tests ---") - val yaml = - """m: - | [1,2,"a"]: [4,5,6]""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 1: Expected a Number value here: =VAL \"a"){ - sj.read[SampleVCList](yaml) - } - } - - test("Bad Map Key") { - val yaml = - """m: - | ? - | true: 2 - | : - | 3: 4""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 2: Expected a Number value here: =VAL :true"){ - sj.read[SampleVCMap](yaml) - } - } - - test("Bad Tuple Key") { - val yaml = - """m: - | ? - | [1,one,true,1] - | : [2,two,false]""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 2: Expected a String here: -SEQ"){ - sj.read[SampleVCTuple](yaml) - } - } - - test("Bad Case Class Key") { - describe("--- Negative Complex Tests ---") - val yaml = - """m: - | ? - | id: "1e6c2b31-4dfe-4bf6-a0a0-882caaff0e9c" - | simple: - | bogus: Larry - | age: 32 - | isOk: true - | favorite: golf - | allDone: true - | : - | id: "1e6c2b31-4dfe-4bf6-a0a0-882caaff0e9d" - | simple: - | name: Mike - | age: 27 - | isOk: false - | favorite: 125 - | allDone: false""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 8: Class co.blocke.scalajack.yaml.mapkeys.SimpleClass missing required fields: name"){ - sj.read[SampleVCClass](yaml) - } - } - - test("Bad Trait Key") { - val yaml = - """m: - | ? - | _hint: "co.blocke.scalajack.yaml.mapkeys.Bogus" - | name: Flipper - | food: Veggies - | waterTemp: 74.33 - | : - | _hint: "co.blocke.scalajack.yaml.mapkeys.DogPet" - | name: Fido - | food: Meat - | numLegs: 3""".stripMargin.asInstanceOf[YAML] - interceptMessage[java.lang.ClassNotFoundException]("co.blocke.scalajack.yaml.mapkeys.Bogus"){ - sj.read[SampleVCTrait](yaml) - } - } - - test("Bad Parameterized Case Class Key") { - val yaml = - """m: - | ? - | a: 5.5 - | b: wow - | : - | a: 6 - | b: zoom""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 2: Cannot parse an Int from value"){ - sj.read[SampleVCParamClass[String, Int]](yaml) - } - } - - test("Bad Parameterized Trait Key") { - val yaml = - """m: - | ? - | _hint: co.blocke.scalajack.yaml.mapkeys.ZThing - | a: 5 - | b: wow - | : - | _hint: "co.blocke.scalajack.mapkeys.AThing" - | a: 6 - | b: zoom""".stripMargin.asInstanceOf[YAML] - interceptMessage[java.lang.ClassNotFoundException]("co.blocke.scalajack.yaml.mapkeys.ZThing"){ - sj.read[SampleVCParamTrait[Int, String]](yaml) - } - - } - - test("Bad Option Key") { - val yaml = - """m: - | [1,2]: there""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 1: Expected a String here: +SEQ"){ - sj.read[SampleVCOption](yaml) - } - } - - test("Bad Nested Collection Key") { - val yaml = - """m: - | ? - | - - | a: b - | - - | c: [1,2] - | : - | - - | t: u - | - - | x: y""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 5: Expected a String here: +SEQ"){ - sj.read[SampleVCNested](yaml) - } - } \ No newline at end of file diff --git a/src_old/test/scala/co.blocke.scalajack/yaml/misc/ReadWriterSpec.scala b/src_old/test/scala/co.blocke.scalajack/yaml/misc/ReadWriterSpec.scala deleted file mode 100644 index 7da266b2..00000000 --- a/src_old/test/scala/co.blocke.scalajack/yaml/misc/ReadWriterSpec.scala +++ /dev/null @@ -1,95 +0,0 @@ -package co.blocke.scalajack -package yaml -package misc - -import TestUtil._ -import munit._ -import munit.internal.console -import scala.math.BigInt - -trait Human -case class Person(name: String, age: Int) extends Human -case class Typey[T](thing: T) { - type foom = T -} - -class ReadWriterSpec extends FunSuite: - - val sj = ScalaJack(YamlFlavor()) - - test("Multiline string | and >") { - describe( - "-----------------------------\n: YamlReader Tests (YAML) :\n-----------------------------", Console.BLUE - ) - val yaml = - """a: | - | This - | is - | a - | test - |""".stripMargin.asInstanceOf[YAML] - assertEquals(sj.read[Map[String, String]](yaml), Map("a" -> "This\nis\na\ntest")) - val yaml2 = - """a: > - | This - | is - | a - | test - |""".stripMargin.asInstanceOf[YAML] - assertEquals(sj.read[Map[String, String]](yaml2), Map("a" -> "This is a test")) - } - - test("expectCollection nextText fails") { - val yaml = """a: foo""".asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 0: Expected a List here: =VAL :foo"){ - sj.read[Map[String, List[Int]]](yaml) - } - } - - test("Scan for hint on non-object") { - interceptMessage[ScalaJackError]("Line 0: Expected an Object here: =VAL :12"){ - sj.read[Person]("12".asInstanceOf[YAML]) - } - } - - test("Type hint not found in scan") { - val yaml = - """name: Fred - |age: 35 - |""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 2: Type hint '_hint' not found"){ - sj.read[Human](yaml) - } - } - - test("Resovle type members on non-object") { - interceptMessage[ScalaJackError]("Line 0: Expected an Object here: =VAL :12"){ - sj.read[Typey[Person]]("12".asInstanceOf[YAML]) - } - } - - test("Null Array") { - describe( - "-----------------------------\n: YamlWriter Tests (YAML) :\n-----------------------------", Console.BLUE - ) - val yaml = - """one: [1,2,3] - |two: null - |""".stripMargin.asInstanceOf[YAML] - assertEquals(sj.read[Map[String, List[Int]]](yaml), Map("one" -> List(1, 2, 3), "two" -> null)) - } - - test("Null BigInt") { - val yaml = - """one: 123 - |two: null - |""".stripMargin.asInstanceOf[YAML] - assertEquals(sj.read[Map[String, BigInt]](yaml), Map("one" -> BigInt(123), "two" -> null)) - } - - test("Empty YamlBuilder") { - val b = YamlBuilder() - interceptMessage[ScalaJackError]("No value set for internal yaml builder"){ - b.result() - } - } \ No newline at end of file diff --git a/src_old/test/scala/co.blocke.scalajack/yaml/misc/TypeMembers.scala b/src_old/test/scala/co.blocke.scalajack/yaml/misc/TypeMembers.scala deleted file mode 100644 index acf07ffd..00000000 --- a/src_old/test/scala/co.blocke.scalajack/yaml/misc/TypeMembers.scala +++ /dev/null @@ -1,164 +0,0 @@ -package co.blocke.scalajack -package yaml -package misc - -import TestUtil._ -import munit._ -import munit.internal.console -import co.blocke.scala_reflection.RType -import model._ - - -trait Body -case class FancyBody(message: String) extends Body -case class DefaultBody(message: String = "Unknown body") extends Body -case class AnyBody(stuff: Any) extends Body - -trait Hobby -case class InsideHobby(desc: String) extends Hobby - -case class Envelope[T <: Body](id: String, body: T) { - type Giraffe = T -} - -// Type member X should be ignored! Only used internally -case class BigEnvelope[T <: Body, H <: Hobby, X](id: String, body: T, hobby: H) { - type Giraffe = T - type Hippo = H - type IgnoreMe = X - - val x: IgnoreMe = null.asInstanceOf[IgnoreMe] -} - -case class Bigger(foo: Int, env: Envelope[FancyBody]) - -class TypeMembers extends FunSuite: - - val sj = ScalaJack(YamlFlavor()) - val sj2 = ScalaJack(YamlFlavor()).parseOrElse((RType.of[Body] -> RType.of[DefaultBody])) - - test("Read and match") { - describe( - "------------------------------------\n: Externalized Type Tests (YAML) :\n------------------------------------", Console.BLUE - ) - val yaml = - """Giraffe: co.blocke.scalajack.yaml.misc.FancyBody - |id: ABC - |body: - | message: Hello""".stripMargin.asInstanceOf[YAML] - val expected: Envelope[Body] = Envelope("ABC", FancyBody("Hello")) - assertEquals((expected, 1), { - val x = sj.read[Envelope[Body]](yaml) - // Test match functionality - val num = x.body match { - case _: FancyBody => 1 - case _ => 2 - } - (x, num) - }) - } - - test("Write -- Concrete T value") { - val value: Envelope[FancyBody] = Envelope("DEF", FancyBody("BOO")) - val expected = - """Giraffe: co.blocke.scalajack.yaml.misc.FancyBody - |id: DEF - |body: - | message: BOO - |""".stripMargin.asInstanceOf[YAML] - assertEquals(expected, sj.render[Envelope[FancyBody]](value)) - } - - test("Write -- Trait T value") { - val value: Envelope[Body] = Envelope("DEF", FancyBody("BOO")) - val expected = - """Giraffe: co.blocke.scalajack.yaml.misc.FancyBody - |id: DEF - |body: - | message: BOO - |""".stripMargin.asInstanceOf[YAML] - assertEquals(expected, sj.render[Envelope[Body]](value)) - } - - test("Wrapped") { - val inst = Bigger(25, Envelope("abc", FancyBody("msg here"))) - val yaml = sj.render(inst) - assertEquals( - """foo: 25 - |env: - | Giraffe: co.blocke.scalajack.yaml.misc.FancyBody - | id: abc - | body: - | message: msg here - |""".stripMargin.asInstanceOf[YAML], yaml) - assertEquals(inst, sj.read[Bigger](yaml)) - } - - test("Type modifier works") { - val sjm = sj.withTypeValueModifier( - ClassNameHintModifier( - (hint: String) => "co.blocke.scalajack.yaml.misc." + hint, - (cname: String) => cname.split('.').last - ) - ) - val value: Envelope[Body] = Envelope("DEF", FancyBody("BOO")) - val yaml = sjm.render[Envelope[Body]](value) - assertEquals( - """Giraffe: FancyBody - |id: DEF - |body: - | message: BOO - |""".stripMargin.asInstanceOf[YAML], yaml) - assertEquals(value, sjm.read[Envelope[Body]](yaml)) - } - - test("Handles mutliple externalized types (bonus: with modifier)") { - val sjm = sj.withTypeValueModifier( - ClassNameHintModifier( - (hint: String) => "co.blocke.scalajack.yaml.misc." + hint, - (cname: String) => cname.split('.').last - ) - ) - val value: BigEnvelope[Body, Hobby, Int] = - BigEnvelope("DEF", FancyBody("BOO"), InsideHobby("stamps")) - val yaml = sjm.render[BigEnvelope[Body, Hobby, Int]](value) - assertEquals(Set.empty[String], - """hobby: - | desc: stamps - |body: - | message: BOO - |Hippo: InsideHobby - |Giraffe: FancyBody - |id: DEF - |""".stripMargin.linesIterator.toSet.diff(yaml.asInstanceOf[String].linesIterator.toSet) - ) - assertEquals(value, sjm.read[BigEnvelope[Body, Hobby, Int]](yaml)) - // Test out-of-order type hint--test skipping element logic in parser - val yaml2 = - """hobby: - | _hint: co.blocke.scalajack.yaml.misc.InsideHobby - | desc: stamps - |body: - | extra: - | - a - | - b - | - c: foom - | _hint: co.blocke.scalajack.yaml.misc.FancyBody - | message: BOO - |Hippo: Hobby - |Giraffe: Body - |id: DEF - |""".stripMargin.asInstanceOf[YAML] - assertEquals(value, sjm.read[BigEnvelope[Body, Hobby, Int]](yaml2)) - } - - test("Works with ParseOrElse") { - val yaml = - """Giraffe: co.blocke.scalajack.yaml.misc.FancyBody - |id: DEF - |body: - | bogus: BOO""".stripMargin.asInstanceOf[YAML] - val expected: Envelope[Body] = - Envelope("DEF", DefaultBody("Unknown body")) - assertEquals(expected, sj2.read[Envelope[Body]](yaml)) - } \ No newline at end of file diff --git a/src_old/test/scala/co.blocke.scalajack/yaml/misc/YamlFlavorSpec.scala b/src_old/test/scala/co.blocke.scalajack/yaml/misc/YamlFlavorSpec.scala deleted file mode 100644 index e71eff6b..00000000 --- a/src_old/test/scala/co.blocke.scalajack/yaml/misc/YamlFlavorSpec.scala +++ /dev/null @@ -1,89 +0,0 @@ -package co.blocke.scalajack -package yaml -package misc - -import TestUtil._ -import munit._ -import munit.internal.console - -object MyTypes { - type Phone = String -} -import MyTypes._ -import model._ - -import scala.collection.mutable -import co.blocke.scala_reflection.RType -import co.blocke.scala_reflection.info.AliasInfo - -//----------- -opaque type Phone >: Null = String - -case class PersonWithPhone(name: String, phone: Phone) - -// Override just Phone -object PhoneAdapter extends TypeAdapterFactory with TypeAdapter[Phone]: - def matches(concrete: RType): Boolean = - concrete match { - case a: AliasInfo if a.name == "Phone" => true - case _ => false - } - def makeTypeAdapter(concrete: RType)(implicit taCache: TypeAdapterCache): TypeAdapter[Phone] = this - val info = RType.of[Phone] - override def isStringish: Boolean = true - - def read(parser: Parser): Phone = - parser.expectString() match { - case null => null.asInstanceOf[Phone] - case s: String => s.replaceAll("-", "").asInstanceOf[Phone] - } - - def write[WIRE]( - t: Phone, - writer: Writer[WIRE], - out: mutable.Builder[WIRE, WIRE]): Unit = t match { - case null => writer.writeNull(out) - case _ => - writer.writeString( - "%s-%s-%s".format(t.toString.substring(0, 3), t.toString.substring(3, 6), t.toString.substring(6)), - out - ) - } - -class YamlFlavorSpec extends FunSuite: - - val sj = ScalaJack(YamlFlavor()) - - test("parse") { - describe( - "-----------------------------\n: YamlFlavor Tests (YAML) :\n-----------------------------", Console.BLUE - ) - val parser = sj.parse("Foo".asInstanceOf[YAML]) - assertEquals(parser.expectString(), "Foo") - } - - test("allowPermissivePrimitives") { - interceptMessage[ScalaJackError]("Not available for YAML encoding"){ - (sj.allowPermissivePrimitives()) - } - } - - test("withAdapters: Overrides type adapter for specific (given) type") { - val sj2 = sj.withAdapters(PhoneAdapter) - val inst = PersonWithPhone("Bartholomew", "5555555555".asInstanceOf[Phone]) - val yaml = sj2.render(inst) - assertEquals("""name: Bartholomew - |phone: 555-555-5555 - |""".stripMargin.asInstanceOf[YAML], yaml ) - assert(inst == sj2.read[PersonWithPhone](yaml)) - } - - test("withDefaultHint") { - val sj2 = sj.withDefaultHint("foom") - val yaml = - """foom: co.blocke.scalajack.yaml.misc.Person - |name: Fred - |age: 34 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(sj2.render[Human](Person("Fred", 34)), yaml) - } \ No newline at end of file diff --git a/src_old/test/scala/co.blocke.scalajack/yaml/parameters/ClassParams.scala b/src_old/test/scala/co.blocke.scalajack/yaml/parameters/ClassParams.scala deleted file mode 100644 index bf4905f6..00000000 --- a/src_old/test/scala/co.blocke.scalajack/yaml/parameters/ClassParams.scala +++ /dev/null @@ -1,170 +0,0 @@ -package co.blocke.scalajack -package yaml -package parameters - -import TestUtil._ -import munit._ -import munit.internal.console - -class ClassParams extends FunSuite: - - val sj = ScalaJack(YamlFlavor()) - - test("Simple parameters - Foo[A](x:A) where A -> simple type") { - describe( - "----------------------------------------\n: Class Paramterization Tests (YAML) :\n----------------------------------------", Console.BLUE - ) - describe("Basic Parameterized Case Class") - val inst = Foo1(false, 19) - val yaml = sj.render(inst) - val comparison = """x: false - |b: 19 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[Foo1[Boolean]](yaml)) - } - - test( - "Non-parameter case class as a parameter - Foo[A](x:A) where A -> Bar (case clas)" - ) { - val inst = Foo1(Bar1("Fred"), 19) - val yaml = sj.render(inst) - val comparison = """x: - | name: Fred - |b: 19 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[Foo1[Bar1]](yaml)) - } - - test( - "Parameterized case class as parameter - Foo[A](x:A) where A -> Bar[Int]" - ) { - describe("Advanced Parameterized Case Class") - val inst = Foo1(Bar2(123L), 19) - val yaml = sj.render(inst) - val comparison = """x: - | id: 123 - |b: 19 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[Foo1[Bar2[Long]]](yaml)) - } - - test("Value class as parameter - Foo[A](x:A) where A -> value class") { - val inst = Foo1(VC1("Wow"), 19) - val yaml = sj.render(inst) - val comparison = """x: Wow - |b: 19 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[Foo1[VC1]](yaml)) - } - - test("Parameterized case class as a parameter - Foo[A](x:Bar[A])") { - val inst = Foo2(Bar2(123L), 19) - val yaml = sj.render(inst) - val comparison = """x: - | id: 123 - |b: 19 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[Foo2[Long]](yaml)) - } - - test( - "Parameterized case class with parameterized another member - Foo[A](x:Bar[A], y:A)" - ) { - val inst = Foo3(Bar2(List(1, 2, 3)), List(4, 5, 6)) - val yaml = sj.render(inst) - val comparison = """x: - | id: [1, 2, 3] - |b: [4, 5, 6] - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[Foo3[List[Int]]](yaml)) - } - - test( - "Class with two parameters, one given one not - Foo[A](x:List[Bar[A, Boolean]])" - ) { - val inst = Foo4( - List( - Bar3(Map(4 -> "yes", 5 -> "no"), true), - Bar3(Map(8 -> "yellow", 9 -> "red"), false) - ), - Map(1 -> "wow", 2 -> "yup") - ) - val yaml = sj.render(inst) - val comparison = """x: - |- id: - | 4: yes - | 5: no - | isIt: true - |- id: - | 8: yellow - | 9: red - | isIt: false - |b: - | 1: wow - | 2: yup - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[Foo4[Map[Int, String]]](yaml)) - } - - test("Multiple parameters, in order - Foo[A,B](x:Bar[A,B], y:A)") { - describe("Very Advanced Parameterized Case Class") - val inst = Foo5( - List( - Bar3(Map(4 -> "yes", 5 -> "no"), true), - Bar3(Map(8 -> "yellow", 9 -> "red"), false) - ), - Map(1 -> "wow", 2 -> "yup") - ) - val yaml = sj.render(inst) - val comparison = """x: - |- id: - | 4: yes - | 5: no - | isIt: true - |- id: - | 8: yellow - | 9: red - | isIt: false - |b: - | 1: wow - | 2: yup - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[Foo5[Map[Int, String], Boolean]](yaml)) - } - - test("Multiple parameters, out of order - Foo[A,B,C,D](x:Bar[C,D,A], y:B)") { - val inst = Foo6(Bar4(5, 2.5, 'H'), "wow") - val yaml = sj.render(inst) - val comparison = """x: - | id: 5 - | thing1: 2.5 - | thing2: H - |y: wow - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[Foo6[Double, String, Int, Char]](yaml)) - } - - test( - "Nested multiple parameters, out of order - Foo[A,B,C,D](x:Bar[C,Blah[D,A]], y:B)" - ) { - val inst = Foo7(Bar5(5, Blah(2.5, 'H')), "wow") - val yaml = sj.render(inst) - val comparison = """x: - | id: 5 - | blah: - | t: 2.5 - | u: H - |y: wow - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[Foo7[Double, String, Char, Int]](yaml)) - } diff --git a/src_old/test/scala/co.blocke.scalajack/yaml/parameters/Model.scala b/src_old/test/scala/co.blocke.scalajack/yaml/parameters/Model.scala deleted file mode 100644 index 9f80fadc..00000000 --- a/src_old/test/scala/co.blocke.scalajack/yaml/parameters/Model.scala +++ /dev/null @@ -1,57 +0,0 @@ -package co.blocke.scalajack -package yaml -package parameters - -//--- Basic Parameterized Case Class -case class Foo1[A](x: A, b: Int) -case class Bar1(name: String) - -//--- Advanced Parameterized Case Class -case class Bar2[X](id: X) -case class VC1(s: String) extends AnyVal -case class Foo2[A](x: Bar2[A], b: Int) -case class Foo3[A](x: Bar2[A], b: A) -case class Bar3[X, Y](id: X, isIt: Y) -case class Foo4[A](x: List[Bar3[A, Boolean]], b: A) - -//--- Very Advanced Parameterized Case Class -case class Foo5[A, B](x: List[Bar3[A, B]], b: A) -case class Foo6[A, B, C, D](x: Bar4[C, D, A], y: B) -case class Bar4[X, Y, Z](id: X, thing1: Z, thing2: Y) -case class Blah[T, U](t: T, u: U) -case class Foo7[A, B, C, D](x: Bar5[C, D, A], y: B) -case class Bar5[X, Y, Z](id: Y, blah: Blah[Z, X]) - -//--- Basic Parameterized Trait -trait T1[X] { val x: X } -case class TFoo1[A](x: A, b: Int) extends T1[A] -trait T2 { val name: String } -case class TBar1(name: String) extends T2 - -//--- Advanced Parameterized Trait -trait T3[X] { val thing: X } -case class TBar2(thing: Boolean) extends T3[Boolean] -case class TBar3[T](thing: T) extends T3[T] -trait T4[X] { val x: TBar3[X] } -case class TFoo2[A](x: TBar3[A], b: A) extends T4[A] -trait T5[X, Y] { val thing1: X; val thing2: Y } -case class TBar4[T](thing1: T, thing2: String) extends T5[T, String] -trait T6[X] { val x: List[T5[X, String]] } -case class TFoo3[A](x: List[T5[A, String]]) extends T6[A] - -//--- Very Advanced Parameterized Trait -trait T7[X, Y] { val x: T5[X, Y]; val b: X } -case class TBar5[T, U](thing1: T, thing2: U) extends T5[T, U] -case class TFoo4[A, B](x: T5[A, B], b: A) extends T7[A, B] - -trait T8[W, X, Y, Z] { val x: T9[Y, Z, W]; val y: X } -trait T9[T, U, V] { val pi: T; val po: U; val pu: V } -case class TBar6[A, B, C](pi: A, po: B, pu: C) extends T9[A, B, C] -case class TFoo5[A, B, C, D](x: T9[C, D, A], y: B) extends T8[A, B, C, D] - -// Foo[A,B,C,D](x:Bar[C,Blah[D,A]], y:B) -trait T10[X, Y] { val x: X; val y: Y } -trait T11[W, Z] { val w: W; val z: Z } -case class TBlah1[A, B](w: A, z: B) extends T11[A, B] -case class TBar7[A, B](thing1: A, thing2: B) extends T5[A, B] -case class TFoo6[A, B, C, D](x: T11[C, T5[D, A]], y: B) extends T10[T11[C, T5[D, A]], B] \ No newline at end of file diff --git a/src_old/test/scala/co.blocke.scalajack/yaml/parameters/TraitParams.scala b/src_old/test/scala/co.blocke.scalajack/yaml/parameters/TraitParams.scala deleted file mode 100644 index afc429a0..00000000 --- a/src_old/test/scala/co.blocke.scalajack/yaml/parameters/TraitParams.scala +++ /dev/null @@ -1,161 +0,0 @@ -package co.blocke.scalajack -package yaml -package parameters - -import TestUtil._ -import munit._ -import munit.internal.console - -class TraitParams extends FunSuite: - - val sj = ScalaJack(YamlFlavor()) - - test("Simple parameters - Foo[A](x:A) where A -> simple type") { - describe( - "----------------------------------------\n: Trait Paramterization Tests (YAML) :\n----------------------------------------", Console.BLUE - ) - describe("Basic Parameterized Trait") - val inst: T1[Boolean] = TFoo1(false, 19) - val yaml = sj.render[T1[Boolean]](inst) - val comparison = """_hint: co.blocke.scalajack.yaml.parameters.TFoo1 - |x: false - |b: 19 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[T1[Boolean]](yaml)) - } - - test( - "Non-parameter trait as a parameter - Foo[A](x:A) where A -> Bar (case clas)" - ) { - val inst: T1[T2] = TFoo1(TBar1("Fred"), 19) - val yaml = sj.render[T1[T2]](inst) - val comparison = """_hint: co.blocke.scalajack.yaml.parameters.TFoo1 - |x: - | _hint: co.blocke.scalajack.yaml.parameters.TBar1 - | name: Fred - |b: 19 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assert(inst == sj.read[TFoo1[TBar1]](yaml)) - } - - test("Parameterized trait as parameter - Foo[A](x:A) where A -> Bar[Int]") { - describe("Advanced Parameterized trait") - val inst: T1[TBar2] = TFoo1(TBar2(true), 19) - val yaml = sj.render(inst) - val comparison = """_hint: co.blocke.scalajack.yaml.parameters.TFoo1 - |x: - | thing: true - |b: 19 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[T1[TBar2]](yaml)) - } - - test("Value class as parameter - Foo[A](x:A) where A -> value class") { - val inst: T1[VC1] = TFoo1(VC1("Wow"), 19) - val yaml = sj.render(inst) - val comparison = """_hint: co.blocke.scalajack.yaml.parameters.TFoo1 - |x: Wow - |b: 19 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[T1[VC1]](yaml)) - } - - test("Parameterized trait as a parameter - Foo[A](x:Bar[A])") { - val inst: T1[T3[Boolean]] = TFoo1(TBar3(false), 19) - val yaml = sj.render(inst) - val comparison = """_hint: co.blocke.scalajack.yaml.parameters.TFoo1 - |x: - | _hint: co.blocke.scalajack.yaml.parameters.TBar3 - | thing: false - |b: 19 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[T1[T3[Boolean]]](yaml)) - } - - test( - "Parameterized trait with parameterized another member - Foo[A](x:Bar[A], y:A)" - ) { - val inst: T4[Boolean] = TFoo2(TBar3(false), true) - val yaml = sj.render(inst) - val comparison = """_hint: co.blocke.scalajack.yaml.parameters.TFoo2 - |x: - | thing: false - |b: true - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[T4[Boolean]](yaml)) - } - - test( - "Trait with two parameters, one given one not - Foo[A](x:List[Bar[A, Boolean]])" - ) { - val inst: T6[Int] = TFoo3(List(TBar4(5, "five"), TBar4(6, "six"))) - val yaml = sj.render(inst) - val comparison = """_hint: co.blocke.scalajack.yaml.parameters.TFoo3 - |x: - |- _hint: co.blocke.scalajack.yaml.parameters.TBar4 - | thing1: 5 - | thing2: five - |- _hint: co.blocke.scalajack.yaml.parameters.TBar4 - | thing1: 6 - | thing2: six - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[T6[Int]](yaml)) - } - - test("Multiple parameters, in order - Foo[A,B](x:Bar[A,B], y:A)") { - describe("Very Advanced Parameterized Trait") - val inst: T7[Long, String] = TFoo4(TBar5(123L, "wow"), 456L) - val yaml = sj.render(inst) - val comparison = """_hint: co.blocke.scalajack.yaml.parameters.TFoo4 - |x: - | _hint: co.blocke.scalajack.yaml.parameters.TBar5 - | thing1: 123 - | thing2: wow - |b: 456 - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[T7[Long, String]](yaml)) - } - - test("Multiple parameters, out of order - Foo[A,B,C,D](x:Bar[C,D,A], y:B)") { - val inst: T8[Char, String, Int, Double] = - TFoo5(TBar6(5, 2.5, 'H'), "wow") - val yaml = sj.render(inst) - val comparison = """_hint: co.blocke.scalajack.yaml.parameters.TFoo5 - |x: - | _hint: co.blocke.scalajack.yaml.parameters.TBar6 - | pi: 5 - | po: 2.5 - | pu: H - |y: wow - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[T8[Char, String, Int, Double]](yaml)) - } - - test( - "Nested multiple parameters, out of order - Foo[A,B,C,D](x:Bar[C,Blah[D,A]], y:B)" - ) { - val inst: T10[T11[Int, T5[Double, Char]], String] = - TFoo6(TBlah1(5, TBar7(1.2, 'Z')), "wow") - val yaml = sj.render(inst) - val comparison = """_hint: co.blocke.scalajack.yaml.parameters.TFoo6 - |x: - | _hint: co.blocke.scalajack.yaml.parameters.TBlah1 - | w: 5 - | z: - | _hint: co.blocke.scalajack.yaml.parameters.TBar7 - | thing1: 1.2 - | thing2: Z - |y: wow - |""".stripMargin.asInstanceOf[YAML] - assertEquals(comparison, yaml ) - assertEquals(inst, sj.read[T10[T11[Int, T5[Double, Char]], String]](yaml)) - } \ No newline at end of file diff --git a/src_old/test/scala/co.blocke.scalajack/yaml/primitives.plain/Misc.scala b/src_old/test/scala/co.blocke.scalajack/yaml/primitives.plain/Misc.scala deleted file mode 100644 index afc144e1..00000000 --- a/src_old/test/scala/co.blocke.scalajack/yaml/primitives.plain/Misc.scala +++ /dev/null @@ -1,84 +0,0 @@ -package co.blocke.scalajack -package yaml -package primitives.plain - -import model.ClassNameHintModifier -import TestUtil._ -import munit._ -import munit.internal.console - -class Misc() extends FunSuite: - - val sj = ScalaJack(YamlFlavor()) - - test("Read/write null into object") { - describe( - "-------------------------------\n: Misc Tests (Plain - YAML) :\n-------------------------------", Console.BLUE - ) - assert(null == sj.read[PlayerMix]("null".asInstanceOf[YAML]) ) - assertEquals("""null - |""".stripMargin, sj.render[PlayerMix](null).asInstanceOf[String] ) - } - - test("Handles type members with modifier") { - val prependHintMod = ClassNameHintModifier( - (hint: String) => "co.blocke.scalajack.yaml.primitives.plain." + hint, - (cname: String) => cname.split('.').last - ) - val sj2 = sj.withTypeValueModifier(prependHintMod) - val yaml = - """flower: Flower - |rose: - | thing: 5 - | other: 6 - |""".stripMargin.asInstanceOf[YAML] - val inst = sj2.read[WrapTrait[TraitBase]](yaml) - assertEquals(inst.rose.isInstanceOf[Flower], true) - assertEquals(sj2.render(inst), yaml) - } - - test("Fails if no hint for type member") { - val yaml = - """rose: - | thing: 5 - | other: 6""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 0: Did not find required type member(s): flower"){ - sj.read[WrapTrait[TraitBase]](yaml) - } - } - - test("Must accept missing default constructor values") { - val yaml = - """foobar: 3 - |quatro: 4 - |dontForget: 1""".stripMargin.asInstanceOf[YAML] - val inst = sj.read[InheritSimpleBase](yaml) - assertEquals(inst.one, "blather") - } - - test("Must accept missing optional constructor values") { - val yaml = """{}""".asInstanceOf[YAML] - val inst = sj.read[OptConst](yaml) - assertEquals(inst.a, None) - assertEquals(inst.b, Some(3)) - } - - test("Must ignore unneeded type members") { - val inst = new UnneededType[String]() - inst.a = 9 - assertEquals(sj.render(inst).asInstanceOf[String], """a: 9 - |""".stripMargin) - } - - test("Must require Java classes to have an empty constructor") { - val inst = new Unsupported("Foo") - interceptMessage[ScalaJackError]("""ScalaJack does not support Java classes with a non-empty constructor."""){ - sj.render(inst) - } - } - - test("Must handle MapName on Java setter") { - val yaml = "dos: 9".asInstanceOf[YAML] - val inst = sj.read[OnSetter](yaml) - assertEquals(inst.getTwo, 9) - } \ No newline at end of file diff --git a/src_old/test/scala/co.blocke.scalajack/yaml/primitives.plain/Model.scala b/src_old/test/scala/co.blocke.scalajack/yaml/primitives.plain/Model.scala deleted file mode 100644 index 10297591..00000000 --- a/src_old/test/scala/co.blocke.scalajack/yaml/primitives.plain/Model.scala +++ /dev/null @@ -1,114 +0,0 @@ -package co.blocke.scalajack -package yaml.primitives.plain - -import scala.util._ - -class InheritSimpleBase( - @DBKey(index = 50) @Change(name = "bogus") val one: String = "blather" -) { - // Public data member - @DBKey(index = 1) @Change(name = "foobar") var two: Int = 5 - @Optional var three: Boolean = true - - // Private var or val - val notOne: Int = 2 - - @Ignore var dontseeme: Int = 90 - - // Scala-style getter/setter - private var _four: Double = 0.1 - @DBKey(index = 2) @Optional def four: Double = _four - @Change(name = "quatro") def four_=(a: Double): Unit = _four = a - - private var _dontForget: Int = 9 - def dontForget: Int = _dontForget - def dontForget_=(a: Int): Unit = _dontForget = a - - private var _unused: Double = 0.1 - @Ignore def unused: Double = _unused - def unused_=(a: Double): Unit = _unused = a -} - -class InheritSimpleChild(val extra: String, @DBKey @Change(name = "uno") override val one: String) extends InheritSimpleBase(one) { - @DBKey(index = 99) var foo: Int = 39 - @Ignore var bogus: String = "" - - private var _nada: Double = 0.1 - def nada: Double = _nada - @Ignore def nada_=(a: Double): Unit = _nada = a -} - -// --- - -class ParamBase[T](val thing: T) { - var item: T = null.asInstanceOf[T] - - private var _cosa: T = null.asInstanceOf[T] - def cosa: T = _cosa - def cosa_=(a: T): Unit = _cosa = a -} - -class ParamChild[T](override val thing: T) extends ParamBase[T](thing) - -// --- - -trait TraitBase { - val thing: Int - val other: Int -} - -class Flower(val thing: Int, val other: Int) extends TraitBase - -class WrapTrait[T <: TraitBase]() { - type flower = T - var rose: T = null.asInstanceOf[T] - // IMPORTANT! rose must be of type T, not "flower". flower is must the label for the external type in JSON -} - -// --- - -class OptConst(val a: Option[Int]) { - var b: Option[Int] = Some(3) -} - -class UnneededType[T]() { - type item = T - - val m: T = null.asInstanceOf[item] - var a: Int = 5 -} - -//------------------------------------------------------ -case class VCDouble(vc: Double) extends AnyVal -class PlayerMix() { - def someConfusingThing() = true - var name: String = "" // public var member - var maybe: Option[Int] = Some(1) // optional member - - @Ignore var bogus: String = "" - - private var _age: VCDouble = VCDouble(0.0) - @Optional def age: VCDouble = _age // getter/setter member - def age_=(a: VCDouble): Unit = _age = a -} - -class BigPlayer() extends PlayerMix { - var more: Int = 0 -} - -// class NotAllVals(val a: Int, b: Int, val c: Int) - -class Embed() { - var stuff: List[String] = List.empty[String] - var num: Int = 0 -} -class Boom() { - var name: String = "" - var other: Try[Embed] = Success(null) -} - -class Cap() extends SJCapture { - var name: String = "" -} - -case class CaseCap(name: String) extends SJCapture \ No newline at end of file diff --git a/src_old/test/scala/co.blocke.scalajack/yaml/primitives.plain/TryAndCapture.scala b/src_old/test/scala/co.blocke.scalajack/yaml/primitives.plain/TryAndCapture.scala deleted file mode 100644 index 1d81fce0..00000000 --- a/src_old/test/scala/co.blocke.scalajack/yaml/primitives.plain/TryAndCapture.scala +++ /dev/null @@ -1,119 +0,0 @@ -package co.blocke.scalajack -package yaml -package primitives.plain - -import TestUtil._ -import munit._ -import munit.internal.console - -import scala.util._ - -class TryAndCapture() extends FunSuite: - - val sj = ScalaJack(YamlFlavor()) - - test("Try sucess") { - describe( - "------------------------------------------\n: Try and Capture Tests (Plain - YAML) :\n------------------------------------------", Console.BLUE - ) - describe("Try:") - val yaml = - """name: Greg - |other: - | stuff: [a, b, c] - | num: 2""".stripMargin.asInstanceOf[YAML] - val obj = sj.read[Boom](yaml) - assertEquals(0, yaml.asInstanceOf[String].split("\n").toSet.diff(sj.render(obj).asInstanceOf[String].split("\n").toSet).size ) - assertEquals(true, - obj.name == "Greg" && obj.other - .asInstanceOf[Success[Embed]] - .get - .num == 2 - ) - } - - test("Try failure") { - val yaml = - """name: Greg - |other: [1, 2, 3]""".stripMargin.asInstanceOf[YAML] - val obj = sj.read[Boom](yaml) - assertEquals("Line 1: Expected an Object here: +SEQ", obj.other.asInstanceOf[Failure[_]].exception.getMessage ) - assertEquals("""other: - |- 1 - |- 2 - |- 3 - |name: Greg - |""".stripMargin.split("\n").toSet.diff(sj.render(obj).asInstanceOf[String].split("\n").toSet).size, 0) - } - - test("Try failure 2") { - val yaml = - """name: Greg - |other: -12.45 - |num: 2""".stripMargin.asInstanceOf[YAML] - val obj = sj.read[Boom](yaml) - assertEquals("Line 1: Expected an Object here: =VAL :-12.45", obj.other.asInstanceOf[Failure[_]].exception.getMessage ) - assertEquals("""other: -12.45 - |name: Greg - |""".stripMargin.split("\n").toSet.diff(sj.render(obj).asInstanceOf[String].split("\n").toSet).size, 0) - } - - test("Plain-class capture can write semantically equivalent JSON") { - describe("Capture:") - val yaml = - """name: Greg - |foo: - |- 1 - |- 2 - |- t - |zing: - | dot: - | age: 25 - | food: Pizza - |blather: wow - |boo: -29384.34 - |maybe: false - |""".stripMargin.asInstanceOf[YAML] - val h = sj.read[Cap](yaml) - assertEquals(h.name, "Greg") - val yaml2 = sj.render(h) - assertEquals(yaml.asInstanceOf[String].split("\n").toSet.diff(yaml2.asInstanceOf[String].split("\n").toSet).size, 0) - } - - test("Case class capture can write semantically equivalent JSON") { - val yaml = - """name: Greg - |foo: [1, 2, t] - |zing: - | _hint: a.b.com.Hey - | dot: - | age: 25 - | food: Pizza - | blather: wow - | boo: -29384.34 - | maybe: false - |""".stripMargin.asInstanceOf[YAML] - val h = sj.read[CaseCap](yaml) - assertEquals(h.name, "Greg") - val yaml2 = sj.render(h) - assertEquals(yaml.asInstanceOf[String].split("\n").toSet.diff(yaml2.asInstanceOf[String].split("\n").toSet).size, 0) - } - - test("Java class capture can write semantically equivalent JSON") { - val yaml = - """name: Greg - |foo: [1, 2, t] - |zing: - | _hint: a.b.com.Hey - | dot: - | age: 25 - | food: Pizza - | blather: wow - | boo: -29384.34 - | maybe: false - |""".stripMargin.asInstanceOf[YAML] - val h = sj.read[JavaCap](yaml) - assertEquals(h.getName, "Greg") - val yaml2 = sj.render(h) - assertEquals(yaml.asInstanceOf[String].split("\n").toSet.diff(yaml2.asInstanceOf[String].split("\n").toSet).size, 0) - } \ No newline at end of file diff --git a/src_old/test/scala/co.blocke.scalajack/yaml/primitives.plain/ValueClassPrim.scala b/src_old/test/scala/co.blocke.scalajack/yaml/primitives.plain/ValueClassPrim.scala deleted file mode 100644 index 171f9860..00000000 --- a/src_old/test/scala/co.blocke.scalajack/yaml/primitives.plain/ValueClassPrim.scala +++ /dev/null @@ -1,34 +0,0 @@ -package co.blocke.scalajack -package yaml -package primitives.plain - -import TestUtil._ -import munit._ -import munit.internal.console - -class ValueClassPrim() extends FunSuite: - - val sj = ScalaJack(YamlFlavor()) - - test("Value class of Double") { - describe( - "-----------------------------------------------\n: ValueClass DelimSpec Tests (Plain - YAML) :\n-----------------------------------------------", Console.BLUE - ) - val p1 = new PlayerMix() - p1.name = "Mike" - p1.age = VCDouble(BigDecimal("1.23").toDouble) - val yaml = sj.render(p1) - val comparison = """age: 1.23 - |maybe: 1 - |name: Mike - |""".stripMargin - assertEquals(yaml.asInstanceOf[String].split("\n").toSet.diff(comparison.split("\n").toSet).size, 0) - assertEquals(p1.name, { - val r = sj.read[PlayerMix](yaml) - r.name - }) - assertEquals(p1.age, { - val r = sj.read[PlayerMix](yaml) - r.age - }) - } diff --git a/src_old/test/scala/co.blocke.scalajack/yaml/primitives/AnyPrim.scala b/src_old/test/scala/co.blocke.scalajack/yaml/primitives/AnyPrim.scala deleted file mode 100644 index 901842d9..00000000 --- a/src_old/test/scala/co.blocke.scalajack/yaml/primitives/AnyPrim.scala +++ /dev/null @@ -1,163 +0,0 @@ -package co.blocke.scalajack -package yaml -package primitives - -import co.blocke.scalajack.model.JackFlavor -import TestUtil._ -import munit._ -import munit.internal.console - -class AnyPrim() extends FunSuite: - - val sj: JackFlavor[YAML] = ScalaJack(YamlFlavor()) - - test("null works") { - describe( - "--------------------------------\n: Any Primitive Tests (YAML) :\n--------------------------------", Console.BLUE - ) - describe("+++ Positive Tests +++") - val shell = AnyShell(null) - val yaml = sj.render(shell) - assertEquals("""a: null""", yaml.asInstanceOf[String].trim ) - assert(null == sj.read[AnyShell](yaml).a) - } - - test("BigDecimal works") { - val payload = BigDecimal("12345678901234567890.12345678901234567890") - val yaml = sj.render(AnyShell(payload)) - assertEquals("""a: 12345678901234567890.12345678901234567890""", yaml.asInstanceOf[String].trim) - assertEquals(true, { - val parsed = sj.read[AnyShell](yaml).a - (parsed == payload) && (parsed.getClass == payload.getClass) - }) - } - - test("BigInt works") { - val payload = BigInt("12345678901234567890") - val yaml = sj.render(AnyShell(payload)) - assertEquals("""a: 12345678901234567890""", yaml.asInstanceOf[String].trim ) - assertEquals(true, { - val parsed = sj.read[AnyShell](yaml).a - (parsed == payload) && parsed.isInstanceOf[BigInt] - }) - } - - test("Boolean works") { - val payload = true - val yaml = sj.render(AnyShell(payload)) - assertEquals("""a: true""", yaml.asInstanceOf[String].trim ) - assertEquals(true, { - val parsed = sj.read[AnyShell](yaml).a - (parsed == payload) && parsed - .isInstanceOf[Boolean] // boolean becomes Boolean - }) - } - - test("Byte works") { - val payload: Byte = 16 - val yaml = sj.render(AnyShell(payload)) - assertEquals("""a: 16""", yaml.asInstanceOf[String].trim ) - assertEquals(true, { - val parsed = sj.read[AnyShell](yaml).a - (parsed == payload) && parsed - .isInstanceOf[Integer] // byte becomes Integer - }) - } - - test("Char works") { - val payload: Char = 'Z' - val yaml = sj.render(AnyShell(payload)) - assertEquals("""a: Z""", yaml.asInstanceOf[String].trim ) - assertEquals(true, { - val parsed = sj.read[AnyShell](yaml).a - (parsed == payload.toString) && parsed - .isInstanceOf[String] // Char becomes String - }) - } - - test("Double works") { - val payload: Double = 1234.5678 - val yaml = sj.render(AnyShell(payload)) - assertEquals("""a: 1234.5678""", yaml.asInstanceOf[String].trim ) - assertEquals(true, { - val parsed = sj.read[AnyShell](yaml).a - (parsed == payload) && parsed - .isInstanceOf[Double] // double becomes Double - }) - } - - test("Enumeration works") { - val payload: Size.Value = Size.Small - val yaml = sj.render(AnyShell(payload)) - assertEquals("""a: Small""", yaml.asInstanceOf[String].trim ) - assertEquals(true, { - val parsed = sj.read[AnyShell](yaml).a - (parsed == payload.toString) && parsed - .isInstanceOf[String] // enum value becomes String - }) - } - - test("Float works") { - val payload: Float = 1234.5678F - val yaml = sj.render(AnyShell(payload)) - assertEquals("""a: 1234.5677""", yaml.asInstanceOf[String].trim ) - assertEquals(true, { - val parsed = sj.read[AnyShell](yaml).a - (parsed.toString == payload.toString) && parsed - .isInstanceOf[Double] // float becomes Double - }) - } - - test("Int works") { - val payload: Int = 1234567 - val yaml = sj.render(AnyShell(payload)) - assertEquals("""a: 1234567""", yaml.asInstanceOf[String].trim ) - assertEquals(true, { - val parsed = sj.read[AnyShell](yaml).a - (parsed == payload) && parsed.isInstanceOf[Int] // int becomes Int - }) - } - - test("Long works") { - val payload: Long = 123456789012345L - val yaml = sj.render(AnyShell(payload)) - assertEquals("""a: 123456789012345""", yaml.asInstanceOf[String].trim ) - assertEquals(true, { - val parsed = sj.read[AnyShell](yaml).a - (parsed == payload) && parsed - .isInstanceOf[java.lang.Long] // long becomes Long (Note this could become Integer if smaller number parsed) - }) - val payload2: Long = 123L - val yaml2 = sj.render(AnyShell(payload2)) - assertEquals("""a: 123""", yaml2.asInstanceOf[String].trim ) - assertEquals(true, { - val parsed = sj.read[AnyShell](yaml2).a - (parsed == payload2) && parsed - .isInstanceOf[Int] // long becomes Byte due to small number size - }) - } - - test("Short works") { - val payload: Short = 16234 - val yaml = sj.render(AnyShell(payload)) - assertEquals("""a: 16234""", yaml.asInstanceOf[String].trim ) - assertEquals(true, { - val parsed = sj.read[AnyShell](yaml).a - (parsed == payload) && parsed.isInstanceOf[Int] // short becomes Int - }) - } - - test("String works") { - val payload = "something" - val yaml = sj.render(AnyShell(payload)) - assertEquals("""a: something""", yaml.asInstanceOf[String].trim ) - assertEquals(true, { - val parsed = sj.read[AnyShell](yaml).a - (parsed == payload) && (parsed.getClass == payload.getClass) - }) - } - - // describe("--- Negative Tests ---") { - // No real negative tests yet... can't think of how to break Any primitives, given well-formed JSON input. - // It may not infer what you want/expect, but it should always infer something. - // } diff --git a/src_old/test/scala/co.blocke.scalajack/yaml/primitives/JavaPrim.scala b/src_old/test/scala/co.blocke.scalajack/yaml/primitives/JavaPrim.scala deleted file mode 100644 index 10d40b01..00000000 --- a/src_old/test/scala/co.blocke.scalajack/yaml/primitives/JavaPrim.scala +++ /dev/null @@ -1,400 +0,0 @@ -package co.blocke.scalajack -package yaml -package primitives - -import TestUtil._ -import munit._ -import munit.internal.console -import java.lang.{Boolean => JBoolean, Byte => JByte, Double => JDouble, Float => JFloat, Integer => JInt, Long => JLong, Short => JShort} -import java.math.{BigDecimal => JBigDecimal, BigInteger => JBigInteger} - -class JavaPrim() extends FunSuite: - - val sj = ScalaJack(YamlFlavor()) - - test("BigDecimal must work") { - describe( - "---------------------------------\n: Java Primitive Tests (YAML) :\n---------------------------------", Console.BLUE - ) - describe("+++ Positive Tests +++") - val inst = SampleJBigDecimal( - JBigDecimal.ZERO, - JBigDecimal.ONE, - JBigDecimal.TEN, - new JBigDecimal( - "0.1499999999999999944488848768742172978818416595458984375" - ), - JBigDecimal.ZERO - ) - val yaml = sj.render(inst) - val comparison = """bd5: !!float '0' - |bd1: !!float '0' - |bd3: !!float '10' - |bd4: 0.1499999999999999944488848768742172978818416595458984375 - |bd2: !!float '1'""".stripMargin - assertEquals(Set.empty[String], yaml.asInstanceOf[String].split("\n").toSet.diff(comparison.split("\n").toSet) ) - assertEquals(inst, sj.read[SampleJBigDecimal](yaml)) - } - - test("BigInteger must work") { - val inst = SampleJBigInteger( - JBigInteger.ZERO, - JBigInteger.ONE, - JBigInteger.TEN, - new JBigInteger("-90182736451928374653345"), - new JBigInteger("90182736451928374653345"), - new JBigInteger("0"), - JBigInteger.ZERO - ) - val yaml = sj.render(inst) - val comparison = """bi6: 0 - |bi2: 1 - |bi5: 90182736451928374653345 - |bi1: 0 - |bi7: 0 - |bi3: 10 - |bi4: -90182736451928374653345""".stripMargin - assertEquals(Set.empty[String], yaml.asInstanceOf[String].split("\n").toSet.diff(comparison.split("\n").toSet) ) - assertEquals(inst, sj.read[SampleJBigInteger](yaml)) - } - - test("Boolean must work") { - val inst = - SampleJBoolean(JBoolean.TRUE, JBoolean.FALSE, true, false, null) - val yaml = sj.render(inst) - val comparison = """bool5: null - |bool3: true - |bool4: false - |bool2: false - |bool1: true""".stripMargin - assertEquals(Set.empty[String], yaml.asInstanceOf[String].split("\n").toSet.diff(comparison.split("\n").toSet) ) - assertEquals(inst, sj.read[SampleJBoolean](yaml)) - } - - test("Byte must work") { - val inst = SampleJByte( - JByte.MAX_VALUE, - JByte.MIN_VALUE, - 0.asInstanceOf[Byte], - 64.asInstanceOf[Byte], - null - ) - val yaml = sj.render(inst) - val comparison = """b5: null - |b1: 127 - |b3: 0 - |b2: -128 - |b4: 64""".stripMargin - assertEquals(Set.empty[String], yaml.asInstanceOf[String].split("\n").toSet.diff(comparison.split("\n").toSet)) - assertEquals(inst, sj.read[SampleJByte](yaml)) - } - - test("Char must work") { - val inst = SampleJChar('Z', '\u20A0', null) - val yaml = sj.render(inst) - val comparison = """c1: Z - |c2: ₠ - |c3: null""".stripMargin - assertEquals(Set.empty[String], yaml.asInstanceOf[String].split("\n").toSet.diff(comparison.split("\n").toSet)) - assertEquals(inst, sj.read[SampleJChar](yaml)) - } - - test("Double must work") { - val inst = SampleJDouble( - JDouble.MAX_VALUE, - JDouble.MIN_VALUE, - 0.0, - -123.4567, - null - ) - val yaml = sj.render(inst) - val comparison = - """d5: null - |d3: 0.0 - |d4: -123.4567 - |d2: !!float '4.9E-324' - |d1: !!float '1.7976931348623157E308'""".stripMargin - assertEquals(Set.empty[String], yaml.asInstanceOf[String].split("\n").toSet.diff(comparison.split("\n").toSet)) - assertEquals(inst, sj.read[SampleJDouble](yaml)) - } - - test("Float must work") { - val inst = SampleJFloat( - JFloat.MAX_VALUE, - JFloat.MIN_VALUE, - 0.0F, - -123.4567F, - null - ) - val yaml = sj.render(inst) - val comparison = """f4: -123.4567 - |f5: null - |f3: 0.0 - |f2: !!float '1.4E-45' - |f1: !!float '3.4028235E38'""".stripMargin - assertEquals(Set.empty[String], yaml.asInstanceOf[String].split("\n").toSet.diff(comparison.split("\n").toSet) ) - assertEquals(inst, sj.read[SampleJFloat](yaml)) - } - - test("Int must work") { - val inst = SampleJInt(JInt.MAX_VALUE, JInt.MIN_VALUE, 0, 123, null) - val yaml = sj.render(inst) - val comparison = """i2: -2147483648 - |i4: 123 - |i3: 0 - |i1: 2147483647 - |i5: null""".stripMargin - assertEquals(Set.empty[String], yaml.asInstanceOf[String].split("\n").toSet.diff(comparison.split("\n").toSet) ) - assertEquals(inst, sj.read[SampleJInt](yaml)) - } - - test("Long must work") { - val inst = SampleJLong(JLong.MAX_VALUE, JLong.MIN_VALUE, 0L, 123L, null) - val yaml = sj.render(inst) - val comparison = """l2: -9223372036854775808 - |l1: 9223372036854775807 - |l4: 123 - |l3: 0 - |l5: null""".stripMargin - assertEquals(Set.empty[String], yaml.asInstanceOf[String].split("\n").toSet.diff(comparison.split("\n").toSet) ) - assertEquals(inst, sj.read[SampleJLong](yaml)) - } - - test("Number must work") { - val inst = SampleJNumber( - JByte.valueOf("-128"), - JByte.valueOf("127"), - JShort.valueOf("-32768"), - JShort.valueOf("32767"), - JInt.valueOf("-2147483648"), - JInt.valueOf("2147483647"), - JLong.valueOf("-9223372036854775808"), - JLong.valueOf("9223372036854755807"), - null, //new JBigInteger("9923372036854755810"), - JByte.valueOf("0"), - JFloat.valueOf("3.4e-038"), - JFloat.valueOf("3.4e+038"), - JDouble.valueOf("1.7e-308"), - JDouble.valueOf("1.7e+308"), - null, //new JBigDecimal("1.8e+308"), - JFloat.valueOf("0.0"), - null - ) - val yaml = sj.render(inst) - val comparison = """n10: 0 - |n4: 32767 - |n8: 9223372036854755807 - |n16: 0.0 - |n1: -128 - |n3: -32768 - |n15: null - |n14: !!float '1.7E308' - |n12: !!float '3.4E38' - |n13: !!float '1.7E-308' - |n6: 2147483647 - |n5: -2147483648 - |n9: null - |n7: -9223372036854775808 - |n11: !!float '3.4E-38' - |n2: 127 - |n17: null""".stripMargin - assertEquals(Set.empty[String], yaml.asInstanceOf[String].split("\n").toSet.diff(comparison.split("\n").toSet) ) - assertEquals(inst, sj.read[SampleJNumber](yaml)) - } - - test("Short must work") { - val inst = SampleJShort( - JShort.MAX_VALUE, - JShort.MIN_VALUE, - 0.asInstanceOf[Short], - 123.asInstanceOf[Short], - null - ) - val yaml = sj.render(inst) - val comparison = """s4: 123 - |s1: 32767 - |s5: null - |s2: -32768 - |s3: 0""".stripMargin - assertEquals(Set.empty[String], yaml.asInstanceOf[String].split("\n").toSet.diff(comparison.split("\n").toSet) ) - assertEquals(inst, sj.read[SampleJShort](yaml)) - } - - test("BigDecimal must break") { - describe("--- Negative Tests ---") - val yaml = - """bd1: 0 - |bd2: 1 - |bd3: 10 - |bd4: [a,b,c] - |bd5: null""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 3: Expected a Number value here: +SEQ"){ - sj.read[SampleBigDecimal](yaml) - } - } - - test("BigInteger must break") { - val yaml = - """bi1: [a,b] - |bi2: 1 - |bi3: 10 - |bi4: -90182736451928374653345 - |bi5: 90182736451928374653345 - |bi6: 0 - |bi7: null""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 0: Expected a Number value here: +SEQ"){ - sj.read[SampleJBigInteger](yaml) - } - } - - test("Boolean must break") { - val yaml = - """bool1: true - |bool2: false - |bool3: true - |bool4: [a,b] - |bool5: null""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 3: Expected a Boolean value here: +SEQ"){ - sj.read[SampleJBoolean](yaml) - } - } - - test("Byte must break") { - val yaml = - """b1: 127 - |b2: -128 - |b3: false - |b4: 64 - |b5: null""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 2: Expected a Number value here: =VAL :false"){ - sj.read[SampleJByte](yaml) - } - } - - test("Char must break") { - val yaml = - """c1: "Z" - |c2: [a,b] - |c3: null""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 1: Expected a String here: +SEQ"){ - sj.read[SampleJChar](yaml) - } - val yaml2 = - """c1: "Z" - |c2: - |c3: null""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 1: Tried to read a Character but empty string found"){ - sj.read[SampleJChar](yaml2) - } - } - - test("Double must break") { - val yaml = - """d1: 1.7976931348623157E308 - |d2: 4.9E-324 - |d3: fred - |d4: -123.4567 - |d5: null""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 2: Expected a Number value here: =VAL :fred"){ - sj.read[SampleJDouble](yaml) - } - } - - test("Float must break") { - val yaml = - """f1: 3.4028235E38 - |f2: fred - |f3: 0.0 - |f4: -123.4567 - |f5: null""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 1: Expected a Number value here: =VAL :fred"){ - sj.read[SampleJFloat](yaml) - } - } - - test("Int must break") { - val yaml = - """i1: 2147483647 - |i2: -2147483648 - |i3: false - |i4: 123 - |i5: null""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 2: Expected a Number value here: =VAL :false"){ - sj.read[SampleJInt](yaml) - } - val yaml2 = - """i1: 2147483647 - |i2: -2147483648 - |i3: 0.3 - |i4: 123 - |i5: null""".stripMargin.asInstanceOf[YAML] - interceptMessage[java.lang.NumberFormatException]("For input string: \"0.3\""){ - sj.read[SampleJInt](yaml2) - } - } - - test("Long must break") { - val yaml = - """l1: 9223372036854775807 - |l2: -9223372036854775808 - |l3: [a,b] - |l4: 123 - |l5: null""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 2: Expected a Number value here: +SEQ"){ - sj.read[SampleJLong](yaml) - } - val yaml2 = - """l1: 9223372036854775807 - |l2: -9223372036854775808 - |l3: 0.3 - |l4: 123 - |l5: null""".stripMargin.asInstanceOf[YAML] - interceptMessage[java.lang.NumberFormatException]("For input string: \"0.3\""){ - sj.read[SampleJLong](yaml2) - } - } - - test("Number must break") { - val yaml = - """n1: -128 - |n2: 127 - |n3: [a,b] - |n4: 32767 - |n5: -2147483648 - |n6: 2147483647 - |n7: -9223372036854775808 - |n8: 9223372036854755807 - |n9: 9923372036854755810 - |n10: 0 - |n11: 3.4E-38 - |n12: 3.4E38 - |n13: 1.7E-308 - |n14: 1.7E308 - |n15: 1.8E+308 - |n16: 0.0 - |n17: null""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 2: Expected a Number value here: +SEQ"){ - sj.read[SampleJNumber](yaml) - } - } - - test("Short must break") { - val yaml = - """s1: false - |s2: -32768 - |s3: 0 - |s4: 123 - |s5: null""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 0: Expected a Number value here: =VAL :false"){ - sj.read[SampleJShort](yaml) - } - val yaml2 = - """s1: 2.3 - |s2: -32768 - |s3: 0 - |s4: 123 - |s5: null""".stripMargin.asInstanceOf[YAML] - interceptMessage[java.lang.NumberFormatException]("For input string: \"2.3\""){ - sj.read[SampleJShort](yaml2) - } - } diff --git a/src_old/test/scala/co.blocke.scalajack/yaml/primitives/Model.scala b/src_old/test/scala/co.blocke.scalajack/yaml/primitives/Model.scala deleted file mode 100644 index 4e72e00b..00000000 --- a/src_old/test/scala/co.blocke.scalajack/yaml/primitives/Model.scala +++ /dev/null @@ -1,97 +0,0 @@ -package co.blocke.scalajack -package yaml.primitives - -import java.util.UUID -import java.lang.{ - Boolean => JBoolean, - Byte => JByte, - Character => JChar, - Double => JDouble, - Float => JFloat, - Integer => JInt, - Long => JLong, - Number => JNumber, - Short => JShort -} -import java.math.{BigDecimal => JBigDecimal, BigInteger => JBigInteger} -import java.time._ - -// === Scala -case class SampleBigDecimal(bd1: BigDecimal, bd2: BigDecimal, bd3: BigDecimal, bd4: BigDecimal, bd5: BigDecimal, bd6: BigDecimal) -case class SampleBigInt(bi1: BigInt, bi2: BigInt, bi3: BigInt, bi4: BigInt) -case class SampleBinary(b1: Array[Byte], b2: Array[Byte]) -case class SampleBoolean(bool1: Boolean, bool2: Boolean) -case class SampleByte(b1: Byte, b2: Byte, b3: Byte, b4: Byte) -case class SampleChar(c1: Char, c2: Char, c3: Char) -case class SampleDouble(d1: Double, d2: Double, d3: Double, d4: Double) - -object Size extends Enumeration { - val Small, Medium, Large = Value -} -case class SampleEnum(e1: Size.Value, e2: Size.Value, e3: Size.Value, e4: Size.Value, e5: Size.Value) - -case class SampleFloat(f1: Float, f2: Float, f3: Float, f4: Float) -case class SampleInt(i1: Int, i2: Int, i3: Int, i4: Int) -case class SampleLong(l1: Long, l2: Long, l3: Long, l4: Long) -case class SampleShort(s1: Short, s2: Short, s3: Short, s4: Short) -case class SampleString(s1: String, s2: String, s3: String) - -// === Java -case class SampleJBigDecimal(bd1: JBigDecimal, bd2: JBigDecimal, bd3: JBigDecimal, bd4: JBigDecimal, bd5: JBigDecimal) -case class SampleJBigInteger(bi1: JBigInteger, bi2: JBigInteger, bi3: JBigInteger, bi4: JBigInteger, bi5: JBigInteger, bi6: JBigInteger, bi7: JBigInteger) -case class SampleJBoolean(bool1: JBoolean, bool2: JBoolean, bool3: JBoolean, bool4: JBoolean, bool5: JBoolean) -case class SampleJByte(b1: JByte, b2: JByte, b3: JByte, b4: JByte, b5: JByte) -case class SampleJChar(c1: JChar, c2: JChar, c3: JChar) -case class SampleJDouble(d1: JDouble, d2: JDouble, d3: JDouble, d4: JDouble, d5: JDouble) -case class SampleJFloat(f1: JFloat, f2: JFloat, f3: JFloat, f4: JFloat, f5: JFloat) -case class SampleJInt(i1: JInt, i2: JInt, i3: JInt, i4: JInt, i5: JInt) -case class SampleJLong(l1: JLong, l2: JLong, l3: JLong, l4: JLong, l5: JLong) -case class SampleJNumber(n1: JNumber, - n2: JNumber, - n3: JNumber, - n4: JNumber, - n5: JNumber, - n6: JNumber, - n7: JNumber, - n8: JNumber, - n9: JNumber, - n10: JNumber, - n11: JNumber, - n12: JNumber, - n13: JNumber, - n14: JNumber, - n15: JNumber, - n16: JNumber, - n17: JNumber) -case class SampleJShort(s1: JShort, s2: JShort, s3: JShort, s4: JShort, s5: JShort) -case class SampleUUID(u1: UUID, u2: UUID) - -// === Java Time -case class SampleDuration(d1: Duration, d2: Duration, d3: Duration) -case class SampleInstant(i1: Instant, i2: Instant, i3: Instant, i4: Instant, i5: Instant) -case class SampleLocalDateTime(d1: LocalDateTime, d2: LocalDateTime, d3: LocalDateTime, d4: LocalDateTime) -case class SampleLocalDate(d1: LocalDate, d2: LocalDate, d3: LocalDate, d4: LocalDate) -case class SampleLocalTime(d1: LocalTime, d2: LocalTime, d3: LocalTime, d4: LocalTime, d5: LocalTime, d6: LocalTime) -case class SampleOffsetDateTime(o1: OffsetDateTime, o2: OffsetDateTime, o3: OffsetDateTime, o4: OffsetDateTime) -case class SampleOffsetTime(o1: OffsetTime, o2: OffsetTime, o3: OffsetTime, o4: OffsetTime) -case class SamplePeriod(p1: Period, p2: Period, p3: Period) -case class SampleZonedDateTime(o1: ZonedDateTime, o2: ZonedDateTime) - -// === Any primitives -case class AnyShell(a: Any) - -// === Value Classes -case class VCBigDecimal(vc: BigDecimal) extends AnyVal -case class VCBigInt(vc: BigInt) extends AnyVal -case class VCBoolean(vc: Boolean) extends AnyVal -case class VCByte(vc: Byte) extends AnyVal -case class VCChar(vc: Char) extends AnyVal -case class VCDouble(vc: Double) extends AnyVal -case class VCEnumeration(vc: Size.Value) extends AnyVal -case class VCFloat(vc: Float) extends AnyVal -case class VCInt(vc: Int) extends AnyVal -case class VCLong(vc: Long) extends AnyVal -case class VCShort(vc: Short) extends AnyVal -case class VCString(vc: String) extends AnyVal -case class VCUUID(vc: UUID) extends AnyVal -case class VCNumber(vc: Number) extends AnyVal \ No newline at end of file diff --git a/src_old/test/scala/co.blocke.scalajack/yaml/primitives/ScalaPrim.scala b/src_old/test/scala/co.blocke.scalajack/yaml/primitives/ScalaPrim.scala deleted file mode 100644 index 4e2e83da..00000000 --- a/src_old/test/scala/co.blocke.scalajack/yaml/primitives/ScalaPrim.scala +++ /dev/null @@ -1,424 +0,0 @@ -package co.blocke.scalajack -package yaml -package primitives - -import scala.math.BigDecimal -import java.util.UUID -import TestUtil._ -import munit._ -import munit.internal.console - -class ScalaPrim() extends FunSuite: - - val sj = ScalaJack(YamlFlavor()) - - test("BigDecimal must work") { - describe( - "----------------------------------\n: Scala Primitive Tests (YAML) :\n----------------------------------", Console.BLUE - ) - describe("+++ Positive Tests +++") - val inst = SampleBigDecimal( - BigDecimal(123L), - BigDecimal(1.23), - BigDecimal(0), - BigDecimal("123.456"), - BigDecimal( - "0.1499999999999999944488848768742172978818416595458984375" - ), - null - ) - val yaml = sj.render(inst) - val comparison = """bd1: !!float '123' - |bd2: 1.23 - |bd3: !!float '0' - |bd4: 123.456 - |bd5: 0.1499999999999999944488848768742172978818416595458984375 - |bd6: null""".stripMargin - assertEquals(Set.empty[String], yaml.asInstanceOf[String].split("\n").toSet.diff(comparison.split("\n").toSet)) - assertEquals(inst, sj.read[SampleBigDecimal](yaml)) - } - - test("BigInt must work") { - val inst = SampleBigInt( - BigInt("-90182736451928374653345"), - BigInt("90182736451928374653345"), - BigInt(0), - null - ) - val yaml = sj.render(inst) - val comparison = - """bi1: -90182736451928374653345 - |bi2: 90182736451928374653345 - |bi3: 0 - |bi4: null""".stripMargin - assertEquals(Set.empty[String], yaml.asInstanceOf[String].split("\n").toSet.diff(comparison.split("\n").toSet) ) - assertEquals(inst, sj.read[SampleBigInt](yaml)) - } - - test("Binary must work") { - val inst = SampleBinary( - null, - hexStringToByteArray("e04fd020ea3a6910a2d808002b30309d") - ) - val yaml = sj.render(inst) - val comparison = - """b1: null - |b2: 4E/QIOo6aRCi2AgAKzAwnQ==""".stripMargin - assertEquals(Set.empty[String], yaml.asInstanceOf[String].split("\n").toSet.diff(comparison.split("\n").toSet) ) - val inst2 = sj.read[SampleBinary](yaml) - assert(null == inst2.b1 ) - assertEquals(true, inst.b2.toList == inst2.b2.toList) - } - - test("Boolean must work (not nullable)") { - val inst = SampleBoolean(true, false) - val yaml = sj.render(inst) - val comparison = - """bool1: true - |bool2: false""".stripMargin - assertEquals(Set.empty[String], yaml.asInstanceOf[String].split("\n").toSet.diff(comparison.split("\n").toSet) ) - assertEquals(inst, sj.read[SampleBoolean](yaml)) - } - - test("Byte must work (not nullable)") { - val inst = SampleByte(Byte.MaxValue, Byte.MinValue, 0, 64) - val yaml = sj.render(inst) - val comparison = """b1: 127 - |b2: -128 - |b3: 0 - |b4: 64""".stripMargin - assertEquals(Set.empty[String], yaml.asInstanceOf[String].split("\n").toSet.diff(comparison.split("\n").toSet) ) - assertEquals(inst, sj.read[SampleByte](yaml)) - } - - test("Char must work (not nullable)") { - val inst = SampleChar(Char.MaxValue, 'Z', '\u20A0') - val yaml = sj.render(inst) - val insert = "\\uffff" - val comparison = s"""c1: "$insert" - |c2: Z - |c3: ₠""".stripMargin - assertEquals(Set.empty[String], yaml.asInstanceOf[String].split("\n").toSet.diff(comparison.split("\n").toSet) ) - assertEquals(inst,sj.read[SampleChar](yaml)) - } - - test("Double must work (not nullable)") { - val inst = - SampleDouble(Double.MaxValue, Double.MinValue, 0.0, -123.4567) - val yaml = sj.render(inst) - val comparison = """d1: !!float '1.7976931348623157E308' - |d2: !!float '-1.7976931348623157E308' - |d3: 0.0 - |d4: -123.4567""".stripMargin - assertEquals(Set.empty[String], yaml.asInstanceOf[String].split("\n").toSet.diff(comparison.split("\n").toSet) ) - assertEquals(inst, sj.read[SampleDouble](yaml)) - } - - test("Enumeration must work (not nullable)") { - val inst = - SampleEnum(Size.Small, Size.Medium, Size.Large, null, Size.Medium) - val yaml = sj.render(inst) - val comparison = """e1: Small - |e2: Medium - |e3: Large - |e4: null - |e5: Medium""".stripMargin - assertEquals(Set.empty[String], yaml.asInstanceOf[String].split("\n").toSet.diff(comparison.split("\n").toSet) ) - // mutate e5 into an ordinal... - val yaml2 = yaml.asInstanceOf[String].replace("e5: Medium", "e5: 1").asInstanceOf[YAML] - assertEquals(inst, sj.read[SampleEnum](yaml2)) - } - - test("Enumerations as Ints must work") { - val sj2 = sj.enumsAsInts() - val inst = - SampleEnum(Size.Small, Size.Medium, Size.Large, null, Size.Medium) - val yaml = sj2.render(inst) - val comparison = """e1: 0 - |e2: 1 - |e3: 2 - |e4: null - |e5: 1""".stripMargin - assertEquals(Set.empty[String], yaml.asInstanceOf[String].split("\n").toSet.diff(comparison.split("\n").toSet) ) - assertEquals(inst, sj2.read[SampleEnum](yaml)) - } - - test("Float must work") { - val inst = SampleFloat(Float.MaxValue, Float.MinValue, 0.0F, -123.4567F) - val yaml = sj.render(inst) - val comparison = """f1: !!float '3.4028235E38' - |f2: !!float '-3.4028235E38' - |f3: 0.0 - |f4: -123.4567""".stripMargin - assertEquals(Set.empty[String], yaml.asInstanceOf[String].split("\n").toSet.diff(comparison.split("\n").toSet) ) - assertEquals(inst, sj.read[SampleFloat](yaml)) - } - - test("Int must work (not nullable)") { - val inst = SampleInt(Int.MaxValue, Int.MinValue, 0, 123) - val yaml = sj.render(inst) - val comparison = """i1: 2147483647 - |i2: -2147483648 - |i3: 0 - |i4: 123""".stripMargin - assertEquals(Set.empty[String], yaml.asInstanceOf[String].split("\n").toSet.diff(comparison.split("\n").toSet) ) - assertEquals(inst, sj.read[SampleInt](yaml)) - } - - test("Long must work (not nullable)") { - val inst = SampleLong(Long.MaxValue, Long.MinValue, 0L, 123L) - val yaml = sj.render(inst) - val comparison = """l1: 9223372036854775807 - |l2: -9223372036854775808 - |l3: 0 - |l4: 123""".stripMargin - assertEquals(Set.empty[String], yaml.asInstanceOf[String].split("\n").toSet.diff(comparison.split("\n").toSet) ) - assertEquals(inst, sj.read[SampleLong](yaml)) - } - - test("Short must work (not nullable)") { - val inst = SampleShort(Short.MaxValue, Short.MinValue, 0, 123) - val yaml = sj.render(inst) - val comparison = """s1: 32767 - |s2: -32768 - |s3: 0 - |s4: 123""".stripMargin - assertEquals(Set.empty[String], yaml.asInstanceOf[String].split("\n").toSet.diff(comparison.split("\n").toSet) ) - assertEquals(inst, sj.read[SampleShort](yaml)) - } - - test("String must work") { - val inst = SampleString("something\b\n\f\r\t☆", "", null) - val yaml = sj.render(inst) - val comparison = """s1: "something\b\n\f\r\t☆" - |s2: '' - |s3: null""".stripMargin - assertEquals(Set.empty[String], yaml.asInstanceOf[String].split("\n").toSet.diff(comparison.split("\n").toSet) ) - assertEquals(inst, sj.read[SampleString](yaml)) - } - - test("UUID must work") { - val inst = SampleUUID( - null, - UUID.fromString("580afe0d-81c0-458f-9e09-4486c7af0fe9") - ) - val yaml = sj.render(inst) - val comparison = """u1: null - |u2: 580afe0d-81c0-458f-9e09-4486c7af0fe9""".stripMargin - assertEquals(Set.empty[String], yaml.asInstanceOf[String].split("\n").toSet.diff(comparison.split("\n").toSet) ) - assertEquals(inst, sj.read[SampleUUID](yaml)) - } - - test("BigDecimal must break") { - describe("--- Negative Tests ---") - val yaml = - """bd1: 123 - |bd2: 1.23 - |bd3: 0 - |bd4: 123.456 - |bd5: [1,2,3] - |bd6: null - |""".stripMargin.asInstanceOf[YAML] - interceptMessage[co.blocke.scalajack.ScalaJackError]("Line 4: Expected a Number value here: +SEQ"){ - sj.read[SampleBigDecimal](yaml) - } - } - - test("BigInt must break") { - val yaml = - """bi1: [1,2,3] - |bi2: 90182736451928374653345 - |bi3: 0 - |bi4: null - |""".stripMargin.asInstanceOf[YAML] - interceptMessage[co.blocke.scalajack.ScalaJackError]("Line 0: Expected a Number value here: +SEQ"){ - sj.read[SampleBigInt](yaml) - } - } - - test("Boolean must break") { - val yaml = - """bool1: true - |bool2: 15""".stripMargin.asInstanceOf[YAML] - interceptMessage[co.blocke.scalajack.ScalaJackError]("Line 1: Expected a Boolean value here: =VAL :15"){ - sj.read[SampleBoolean](yaml) - } - val yaml2 = - """bool1: true - |bool2: null""".stripMargin.asInstanceOf[YAML] - interceptMessage[co.blocke.scalajack.ScalaJackError]("Line 1: Expected a Boolean value here: =VAL :null"){ - sj.read[SampleBoolean](yaml2) - } - } - - test("Byte must break") { - val yaml = - """b1: true - |b2: -128 - |b3: 0 - |b4: 64""".stripMargin.asInstanceOf[YAML] - interceptMessage[co.blocke.scalajack.ScalaJackError]("Line 0: Expected a Number value here: =VAL :true"){ - sj.read[SampleByte](yaml) - } - val yaml2 = - """b1: 12 - |b2: -128 - |b3: 0 - |b4: null""".stripMargin.asInstanceOf[YAML] - interceptMessage[co.blocke.scalajack.ScalaJackError]("Line 3: Cannot parse an Byte from value"){ - sj.read[SampleByte](yaml2) - } - } - - test("Char must break") { - val yaml = - """c1: null - |c2: Y - |c3: Z""".stripMargin.asInstanceOf[YAML] - interceptMessage[co.blocke.scalajack.ScalaJackError]("Line 0: A Char typed value cannot be null"){ - sj.read[SampleChar](yaml) - } - val yaml2 = - """c1: G - |c2: - |c3: Z""".stripMargin.asInstanceOf[YAML] - interceptMessage[co.blocke.scalajack.ScalaJackError]("Line 1: Tried to read a Char but empty string found"){ - sj.read[SampleChar](yaml2) - } - val yaml3 = - """c1: G - |c2: - | - a - |c3: Z""".stripMargin.asInstanceOf[YAML] - interceptMessage[co.blocke.scalajack.ScalaJackError]("Line 2: Expected a String here: +SEQ"){ - sj.read[SampleChar](yaml3) - } - } - - test("Double must break") { - val yaml = - """d1: 1.79769313486E23157E308 - |d2: -1.7976931348623157E308 - |d3: 0.0 - |d4: -123.4567""".stripMargin.asInstanceOf[YAML] - interceptMessage[co.blocke.scalajack.ScalaJackError]("Line 0: Cannot parse an Double from value"){ - sj.read[SampleDouble](yaml) - } - } - - test("Enumeration must break") { - val yaml = - """e1: Small - |e2: Bogus - |e3: Large - |e4: null - |e5: Medium""".stripMargin.asInstanceOf[YAML] - interceptMessage[co.blocke.scalajack.ScalaJackError]( "Line 1: No value found in enumeration co.blocke.scalajack.yaml.primitives.Size$ for Bogus"){ - sj.read[SampleEnum](yaml) - } - val yaml2 = - """e1: Small - |e2: Medium - |e3: Large - |e4: null - |e5: 9""".stripMargin.asInstanceOf[YAML] - interceptMessage[co.blocke.scalajack.ScalaJackError]("Line 4: No value found in enumeration co.blocke.scalajack.yaml.primitives.Size$ for 9"){ - sj.read[SampleEnum](yaml2) - } - val yaml3 = - """e1: Small - |e2: Medium - |e3: Large - |e4: null - |e5: false""".stripMargin.asInstanceOf[YAML] - interceptMessage[co.blocke.scalajack.ScalaJackError]("Line 4: No value found in enumeration co.blocke.scalajack.yaml.primitives.Size$ for false"){ - sj.read[SampleEnum](yaml3) - } - } - - test("Float must break") { - val yaml = - """f1: 3.4028235E38 - |f2: [a,b] - |f3: 0.0 - |f4: -123.4567""".stripMargin.asInstanceOf[YAML] - interceptMessage[co.blocke.scalajack.ScalaJackError]("Line 1: Expected a Number value here: +SEQ"){ - sj.read[SampleFloat](yaml) - } - } - - test("Int must break") { - val yaml = - """i1: 2147483647 - |i2: -2147483648 - |i3: [a,b] - |i4: 123""".stripMargin.asInstanceOf[YAML] - interceptMessage[co.blocke.scalajack.ScalaJackError]( "Line 2: Expected a Number value here: +SEQ"){ - sj.read[SampleInt](yaml) - } - val yaml2 = - """i1: 2147483647 - |i2: -2147483648 - |i3: 2.3 - |i4: 123""".stripMargin.asInstanceOf[YAML] - interceptMessage[co.blocke.scalajack.ScalaJackError]("Line 2: Cannot parse an Int from value"){ - sj.read[SampleInt](yaml2) - } - } - - test("Long must break") { - val yaml = - """l1: 9223372036854775807 - |l2: -9223372036854775808 - |l3: true - |l4: 123""".stripMargin.asInstanceOf[YAML] - interceptMessage[co.blocke.scalajack.ScalaJackError]("Line 2: Expected a Number value here: =VAL :true"){ - sj.read[SampleLong](yaml) - } - val yaml2 = - """l1: 9223372036854775807 - |l2: -9223372036854775808 - |l3: 0.3 - |l4: 123""".stripMargin.asInstanceOf[YAML] - interceptMessage[co.blocke.scalajack.ScalaJackError]("Line 2: Cannot parse an Long from value"){ - sj.read[SampleLong](yaml2) - } - } - - test("Short must break") { - val yaml = - """s1: 32767 - |s2: true - |s3: 0 - |s4: 123""".stripMargin.asInstanceOf[YAML] - interceptMessage[co.blocke.scalajack.ScalaJackError]("Line 1: Expected a Number value here: =VAL :true"){ - sj.read[SampleShort](yaml) - } - val yaml2 = - """s1: 32767 - |s2: 3.4 - |s3: 0 - |s4: 123""".stripMargin.asInstanceOf[YAML] - interceptMessage[co.blocke.scalajack.ScalaJackError]("Line 1: Cannot parse an Short from value"){ - sj.read[SampleShort](yaml2) - } - } - - test("String must break") { - val yaml = - """s1: something - |s2: [a,b] - |s3: null""".stripMargin.asInstanceOf[YAML] - interceptMessage[co.blocke.scalajack.ScalaJackError]("Line 1: Expected a String here: +SEQ"){ - sj.read[SampleString](yaml) - } - } - - test("UUID must break") { - val yaml = - """u1: bogus - |u2: 580afe0d-81c0-458f-9e09-4486c7af0fe9""".stripMargin.asInstanceOf[YAML] - interceptMessage[co.blocke.scalajack.ScalaJackError]("Line 0: Failed to create UUID value from parsed text bogus"){ - sj.read[SampleUUID](yaml) - } - } diff --git a/src_old/test/scala/co.blocke.scalajack/yaml/primitives/TimePrim.scala b/src_old/test/scala/co.blocke.scalajack/yaml/primitives/TimePrim.scala deleted file mode 100644 index 13d67d08..00000000 --- a/src_old/test/scala/co.blocke.scalajack/yaml/primitives/TimePrim.scala +++ /dev/null @@ -1,321 +0,0 @@ -package co.blocke.scalajack -package yaml -package primitives - -import TestUtil._ -import munit._ -import munit.internal.console -import java.time._ - -class TimePrim() extends FunSuite: - - val sj = ScalaJack(YamlFlavor()) - - test("Duration must work") { - describe( - "---------------------------------\n: Time Primitive Tests (YAML) :\n---------------------------------", Console.BLUE - ) - describe("+++ Positive Tests +++") - val inst = - SampleDuration(Duration.ZERO, Duration.parse("P2DT3H4M"), null) - val yaml = sj.render(inst) - val comparison = """d1: PT0S - |d2: PT51H4M - |d3: null""".stripMargin - assertEquals(Set.empty[String], yaml.asInstanceOf[String].split("\n").toSet.diff(comparison.split("\n").toSet) ) - assertEquals(inst, sj.read[SampleDuration](yaml)) - } - - test("Instant must work") { - val inst = SampleInstant( - Instant.EPOCH, - Instant.MAX, - Instant.MIN, - Instant.parse("2007-12-03T10:15:30.00Z"), - null - ) - val yaml = sj.render(inst) - val comparison = """i1: 1970-01-01T00:00:00Z - |i5: null - |i2: +1000000000-12-31T23:59:59.999999999Z - |i3: -1000000000-01-01T00:00:00Z - |i4: 2007-12-03T10:15:30Z""".stripMargin - assertEquals(Set.empty[String], yaml.asInstanceOf[String].split("\n").toSet.diff(comparison.split("\n").toSet) ) - assertEquals(inst, sj.read[SampleInstant](yaml)) - } - - test("LocalDateTime must work") { - val inst = SampleLocalDateTime( - LocalDateTime.MAX, - LocalDateTime.MIN, - LocalDateTime.parse("2007-12-03T10:15:30"), - null - ) - val yaml = sj.render(inst) - val comparison = """d1: +999999999-12-31T23:59:59.999999999 - |d2: -999999999-01-01T00:00:00 - |d3: 2007-12-03T10:15:30 - |d4: null""".stripMargin - assertEquals(Set.empty[String], yaml.asInstanceOf[String].split("\n").toSet.diff(comparison.split("\n").toSet) ) - assertEquals(inst, sj.read[SampleLocalDateTime](yaml)) - } - - test("LocalDate must work") { - val inst = SampleLocalDate( - LocalDate.MAX, - LocalDate.MIN, - LocalDate.parse("2007-12-03"), - null - ) - val yaml = sj.render(inst) - val comparison = """d1: +999999999-12-31 - |d2: -999999999-01-01 - |d3: 2007-12-03 - |d4: null""".stripMargin - assertEquals(Set.empty[String], yaml.asInstanceOf[String].split("\n").toSet.diff(comparison.split("\n").toSet) ) - assertEquals(inst, sj.read[SampleLocalDate](yaml)) - } - - test("LocalTime must work") { - val inst = SampleLocalTime( - LocalTime.MAX, - LocalTime.MIN, - LocalTime.MIDNIGHT, - LocalTime.NOON, - LocalTime.parse("10:15:30"), - null - ) - val yaml = sj.render(inst) - val comparison = """d1: 23:59:59.999999999 - |d6: null - |d2: 00:00:00 - |d5: 10:15:30 - |d3: 00:00:00 - |d4: 12:00:00""".stripMargin - assertEquals(Set.empty[String], yaml.asInstanceOf[String].split("\n").toSet.diff(comparison.split("\n").toSet) ) - assertEquals(inst, sj.read[SampleLocalTime](yaml)) - } - - test("OffsetDateTime must work") { - val inst = SampleOffsetDateTime( - OffsetDateTime.MAX, - OffsetDateTime.MIN, - OffsetDateTime.parse("2007-12-03T10:15:30+01:00"), - null - ) - val yaml = sj.render(inst) - val comparison = """o1: +999999999-12-31T23:59:59.999999999-18:00 - |o2: -999999999-01-01T00:00:00+18:00 - |o3: 2007-12-03T10:15:30+01:00 - |o4: null""".stripMargin - assertEquals(Set.empty[String], yaml.asInstanceOf[String].split("\n").toSet.diff(comparison.split("\n").toSet) ) - assertEquals(inst, sj.read[SampleOffsetDateTime](yaml)) - } - - test("OffsetTime must work") { - val inst = SampleOffsetTime( - OffsetTime.MAX, - OffsetTime.MIN, - OffsetTime.parse("10:15:30+01:00"), - null - ) - val yaml = sj.render(inst) - val comparison = """o1: 23:59:59.999999999-18:00 - |o2: 00:00:00+18:00 - |o3: 10:15:30+01:00 - |o4: null""".stripMargin - assertEquals(Set.empty[String], yaml.asInstanceOf[String].split("\n").toSet.diff(comparison.split("\n").toSet) ) - assertEquals(inst, sj.read[SampleOffsetTime](yaml)) - } - - test("Period must work") { - val inst = SamplePeriod(Period.ZERO, Period.parse("P1Y2M3D"), null) - val yaml = sj.render(inst) - val comparison = """p1: P0D - |p2: P1Y2M3D - |p3: null""".stripMargin - assertEquals(Set.empty[String], yaml.asInstanceOf[String].split("\n").toSet.diff(comparison.split("\n").toSet) ) - assertEquals(inst, sj.read[SamplePeriod](yaml)) - } - - test("ZonedDateTime must work") { - val inst = SampleZonedDateTime( - ZonedDateTime.parse("2007-12-03T10:15:30+01:00[Europe/Paris]"), - null - ) - val yaml = sj.render(inst) - val comparison = """o1: 2007-12-03T10:15:30+01:00[Europe/Paris] - |o2: null""".stripMargin - assertEquals(Set.empty[String], yaml.asInstanceOf[String].split("\n").toSet.diff(comparison.split("\n").toSet) ) - assertEquals(inst, sj.read[SampleZonedDateTime](yaml)) - } - - test("Duration must break") { - describe("--- Negative Tests ---") - val yaml = - """d1: PT0S - |d2: 21 - |d3: null""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 1: Failed to parse Duration from input '21'"){ - sj.read[SampleDuration](yaml) - } - val yaml2 = - """d1: PT0S - |d2: bogus - |d3: null""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 1: Failed to parse Duration from input 'bogus'"){ - sj.read[SampleDuration](yaml2) - } - } - - test("Instant must break") { - val yaml = - """i1: 1970-01-01T00:00:00Z - |i2: false - |i3: -1000000000-01-01T00:00:00Z - |i4: 2007-12-03T10:15:30Z - |i5: null""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 1: Failed to parse Instant from input 'false'"){ - sj.read[SampleInstant](yaml) - } - val yaml2 = - """i1: 1970-01-01T00:00:00Z - |i2: bogus - |i3: -1000000000-01-01T00:00:00Z - |i4: 2007-12-03T10:15:30Z - |i5: null""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 1: Failed to parse Instant from input 'bogus'"){ - sj.read[SampleInstant](yaml2) - } - } - - test("LocalDateTime must break") { - val yaml = - """d1: -1 - |d2: "-999999999-01-01T00:00:00" - |d3: 2007-12-03T10:15:30 - |d4: null""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 0: Failed to parse LocalDateTime from input '-1'"){ - sj.read[SampleLocalDateTime](yaml) - } - val yaml2 = - """d1: bogus - |d2: "-999999999-01-01T00:00:00" - |d3: 2007-12-03T10:15:30 - |d4: null""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 0: Failed to parse LocalDateTime from input 'bogus'"){ - sj.read[SampleLocalDateTime](yaml2) - } - } - - test("LocalDate must break") { - val yaml = - """d1: -1 - |d2: "-999999999-01-01" - |d3: "2007-12-03" - |d4: null""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 0: Failed to parse LocalDate from input '-1'"){ - sj.read[SampleLocalDate](yaml) - } - val yaml2 = - """d1: bogus - |d2: "-999999999-01-01" - |d3: "2007-12-03" - |d4: null""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 0: Failed to parse LocalDate from input 'bogus'"){ - sj.read[SampleLocalDate](yaml2) - } - } - - test("LocalTime must break") { - val yaml = - """d1: "23:59:59.999999999" - |d2: "00:00:00" - |d3: "00:00:00" - |d4: "12:00:00" - |d5: false - |d6: null""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 4: Failed to parse LocalTime from input 'false'"){ - sj.read[SampleLocalTime](yaml) - } - val yaml2 = - """d1: "23:59:59.999999999" - |d2: "00:00:00" - |d3: "00:00:00" - |d4: "12:00:00" - |d5: bogus - |d6: null""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 4: Failed to parse LocalTime from input 'bogus'"){ - sj.read[SampleLocalTime](yaml2) - } - } - - test("OffsetDateTime must break") { - val yaml = - """o1: +999999999-12-31T23:59:59.999999999-18:00 - |o2: 2 - |o3: 2007-12-03T10:15:30+01:00 - |o4: null""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 1: Failed to parse OffsetDateTime from input '2'"){ - sj.read[SampleOffsetDateTime](yaml) - } - val yaml2 = - """o1: +999999999-12-31T23:59:59.999999999-18:00 - |o2: -999999999-01T00:00:00+18:00 - |o3: 2007-12-03T10:15:30+01:00 - |o4: null""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 1: Failed to parse OffsetDateTime from input '-999999999-01T00:00:00+18:00'"){ - sj.read[SampleOffsetDateTime](yaml2) - } - } - - test("OffsetTime must break") { - val yaml = - """o1: "23:59:59.999999999-18:00" - |o2: false - |o3: "10:15:30+01:00" - |o4: null""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 1: Failed to parse OffsetTime from input 'false'"){ - sj.read[SampleOffsetTime](yaml) - } - val yaml2 = - """o1: "23:59:59.999999999-18:00" - |o2: 00:00:00:00+18:00 - |o3: "10:15:30+01:00" - |o4: null""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 1: Failed to parse OffsetTime from input '00:00:00:00+18:00'"){ - sj.read[SampleOffsetTime](yaml2) - } - } - - test("Period must break") { - val yaml = - """p1: P0D - |p2: 5 - |p3: null""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 1: Failed to parse Period from input '5'"){ - sj.read[SamplePeriod](yaml) - } - val yaml2 = - """p1: P0D - |p2: bogus - |p3: null""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 1: Failed to parse Period from input 'bogus'"){ - sj.read[SamplePeriod](yaml2) - } - } - - test("ZonedDateTime must break") { - val yaml = - """o1: true - |o2: null""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 0: Failed to parse ZonedDateTime from input 'true'"){ - sj.read[SampleZonedDateTime](yaml) - } - val yaml2 = - """o1: "2007-12-03T10:15:30+01:00 Earth" - |o2: null""".stripMargin.asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 0: Failed to parse ZonedDateTime from input '2007-12-03T10:15:30+01:00 Earth'"){ - sj.read[SampleZonedDateTime](yaml2) - } - } \ No newline at end of file diff --git a/src_old/test/scala/co.blocke.scalajack/yaml/primitives/ValueClassPrim.scala b/src_old/test/scala/co.blocke.scalajack/yaml/primitives/ValueClassPrim.scala deleted file mode 100644 index b1d66cdc..00000000 --- a/src_old/test/scala/co.blocke.scalajack/yaml/primitives/ValueClassPrim.scala +++ /dev/null @@ -1,161 +0,0 @@ -package co.blocke.scalajack -package yaml -package primitives - -import TestUtil._ -import munit._ -import munit.internal.console -import java.util.UUID - -class ValueClassPrim() extends FunSuite: - - val sj = ScalaJack(YamlFlavor()) - - test("Value class of BigDecimal") { - describe( - "---------------------------------------\n: ValueClass Primitive Tests (YAML) :\n---------------------------------------", Console.BLUE - ) - describe("+++ Positive Tests +++") - val inst = VCBigDecimal(BigDecimal(12.34)) - val yaml = sj.render(inst) - assertEquals("""12.34""", yaml.asInstanceOf[String].trim ) - assertEquals(inst, sj.read[VCBigDecimal](yaml)) - } - - test("Value class of BigDecimal with null") { - val inst = VCBigDecimal(null) - val yaml = sj.render(inst) - assertEquals("""null""", yaml.asInstanceOf[String].trim ) - assertEquals(inst, sj.read[VCBigDecimal](yaml)) - } - - test("Value class of BigInt") { - val inst = VCBigInt(BigInt(1)) - val yaml = sj.render(inst) - assertEquals("""1""", yaml.asInstanceOf[String].trim ) - assertEquals(inst, sj.read[VCBigInt](yaml)) - } - - test("Value class of BigInt with null") { - val inst = VCBigInt(null) - val yaml = sj.render(inst) - assertEquals("""null""", yaml.asInstanceOf[String].trim ) - assertEquals(inst, sj.read[VCBigInt](yaml)) - } - - test("Value class of Byte") { - val inst = VCByte(100.asInstanceOf[Byte]) - val yaml = sj.render(inst) - assertEquals("""100""", yaml.asInstanceOf[String].trim ) - assertEquals(inst, sj.read[VCByte](yaml)) - } - - test("Value class of Boolean") { - val inst = VCBoolean(false) - val yaml = sj.render(inst) - assertEquals("""false""", yaml.asInstanceOf[String].trim ) - assertEquals(inst, sj.read[VCBoolean](yaml)) - } - - test("Value class of Char") { - val inst = VCChar('Z') - val yaml = sj.render(inst) - assertEquals("""Z""", yaml.asInstanceOf[String].trim ) - assertEquals(inst, sj.read[VCChar](yaml)) - } - - test("Value class of Double") { - val inst = VCDouble(100.5) - val yaml = sj.render(inst) - assertEquals("""100.5""", yaml.asInstanceOf[String].trim ) - assertEquals(inst, sj.read[VCDouble](yaml)) - } - - test("Value class of Enumeration") { - val inst = VCEnumeration(Size.Medium) - val yaml = sj.render(inst) - assertEquals("""Medium""", yaml.asInstanceOf[String].trim ) - assertEquals(inst, sj.read[VCEnumeration](yaml)) - } - - test("Value class of Enumeration with null") { - val inst = VCEnumeration(null) - val yaml = sj.render(inst) - assertEquals("""null""", yaml.asInstanceOf[String].trim ) - assertEquals(inst, sj.read[VCEnumeration](yaml)) - } - - test("Value class of Float") { - val inst = VCFloat(100.5F) - val yaml = sj.render(inst) - assertEquals("""100.5""", yaml.asInstanceOf[String].trim ) - assertEquals(inst, sj.read[VCFloat](yaml)) - } - - test("Value class of Int") { - val inst = VCInt(100) - val yaml = sj.render(inst) - assertEquals("""100""", yaml.asInstanceOf[String].trim ) - assertEquals(inst, sj.read[VCInt](yaml)) - } - - test("Value class of Long") { - val inst = VCLong(100L) - val yaml = sj.render(inst) - assertEquals("""100""", yaml.asInstanceOf[String].trim ) - assertEquals(inst, sj.read[VCLong](yaml)) - } - - test("Value class of Short") { - val inst = VCShort(100.asInstanceOf[Short]) - val yaml = sj.render(inst) - assertEquals("""100""", yaml.asInstanceOf[String].trim ) - assertEquals(inst, sj.read[VCShort](yaml)) - } - - test("Value class of String") { - val inst = VCString("foo") - val yaml = sj.render(inst) - assertEquals("""foo""", yaml.asInstanceOf[String].trim ) - assertEquals(inst, sj.read[VCString](yaml)) - } - - test("Value class of String with null") { - val inst = VCString(null) - val yaml = sj.render(inst) - assertEquals("""null""", yaml.asInstanceOf[String].trim ) - assertEquals(inst, sj.read[VCString](yaml)) - } - - test("Value class of UUID") { - val inst = - VCUUID(UUID.fromString("54cab778-7b9e-4b07-9d37-87b97a011e55")) - val yaml = sj.render(inst) - assertEquals("""54cab778-7b9e-4b07-9d37-87b97a011e55""", yaml.asInstanceOf[String].trim ) - assertEquals(inst, sj.read[VCUUID](yaml)) - } - - test("Value class of UUID with null") { - val inst = VCUUID(null) - val yaml = sj.render(inst) - assertEquals("""null""", yaml.asInstanceOf[String].trim ) - assertEquals(inst, sj.read[VCUUID](yaml)) - } - - test("Value class of Number") { - val inst = VCNumber(25) - val yaml = sj.render(inst) - assertEquals("""25""", yaml.asInstanceOf[String].trim ) - assertEquals((inst, true), { - val r = sj.read[VCNumber](yaml) - (r, r.vc.isInstanceOf[Byte]) - }) - } - - test("Wrong YAML for wrapped type") { - describe("--- Negative Tests ---") - val yaml = """100.25""".asInstanceOf[YAML] - interceptMessage[ScalaJackError]("Line 0: Cannot parse an Short from value"){ - sj.read[VCShort](yaml) - } - } From 6b882c60f7a734b1e60f0875e44bd962d9138f93 Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Fri, 3 May 2024 20:19:42 -0500 Subject: [PATCH 58/65] docs and fixes --- README.md | 15 ++-- build.sbt | 2 +- doc/any.md | 60 ++++++------- doc/classesAndTraits.md | 85 ++++++++++++------- doc/dynamo.md | 41 --------- doc/json4s.md | 10 --- doc/mapname.md | 36 ++------ doc/mongo.md | 47 ---------- doc/noncase.md | 60 +++---------- doc/parameterized.md | 46 ++++++---- doc/valueClass.md | 22 ++--- doc/viewSplice.md | 38 --------- doc/yaml.md | 21 ----- .../json/JsonCodecMaker.scala | 14 --- .../co.blocke.scalajack/json/JsonConfig.scala | 13 --- .../scala/co.blocke.scalajack/run/Play.scala | 36 -------- .../scala/co.blocke.scalajack/run/Play.scalax | 32 +++++++ .../run/{Record.scala => Record.scalax} | 23 +++++ .../json/classes/Model.scala | 18 ++++ .../json/classes/TraitSpec.scala | 7 ++ .../json/collections/SeqSetArraySpec.scala | 60 ++++++------- 21 files changed, 263 insertions(+), 423 deletions(-) delete mode 100644 doc/dynamo.md delete mode 100644 doc/json4s.md delete mode 100644 doc/mongo.md delete mode 100644 doc/viewSplice.md delete mode 100644 doc/yaml.md delete mode 100644 src/main/scala/co.blocke.scalajack/run/Play.scala create mode 100644 src/main/scala/co.blocke.scalajack/run/Play.scalax rename src/main/scala/co.blocke.scalajack/run/{Record.scala => Record.scalax} (84%) diff --git a/README.md b/README.md index e59c8e00..aa6d7383 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,11 @@ [![Maven Central](https://maven-badges.herokuapp.com/maven-central/co.blocke/scalajack_3/badge.svg)](https://search.maven.org/artifact/co.blocke/scalajack_3/8.0.0/jar) -ScalaJack 8 is an all-new ScalaJack implemenation built on Scala 3. For Scala 2.13 ScalaJack, please use (frozen) version 6.2.0. ScalaJack 8 is built using Scala 3.4.1 on JDK 21 LTS version. +ScalaJack 8 is an all-new ScalaJack serializer implemenation built on Scala 3. For Scala 2.13 ScalaJack, please use the frozen version 6.2.0. ScalaJack 8 is built +using Scala 3.4.1 on JDK 21 LTS version. -ScalaJack is a very fast, seamless serialization engine for JSON designed to require a bare minimum of extra code to serialize a class. +ScalaJack is a very fast, seamless serialization engine for unstructured data types (JSON, HOCON, Yaml, and MsgPack) designed to require a bare minimum of extra code +to serialize a class. Advanced Features: @@ -34,7 +36,7 @@ given sjPerson: ScalaJack[Person] = sjCodecOf[Person] // create a re-usable Pers ... val inst = Person("Mike",34) val js = sjPerson.toJson(inst) // """{"name":"Mike","age":34}""" -val inst = sjPerson.fromJson(js) // re-constitutes original Person +sjPerson.fromJson(js) // re-constitutes original Person ``` Couldn't be simpler! @@ -42,7 +44,8 @@ Couldn't be simpler! Compared to pre-8.0 ScalaJack, which used Scala 2.x runtime reflection, ScalaJack is dramatically faster in almost every case. How's this work? ScalaJack 8 uses macros, that at compile-time generate all the serialization code for you (the codecs). It's very much like writing hand-tooled, field-by-field serialization code yourself, except ScalaJack does it at compile-time. Wherever you see ```sjCodecOf``` is where the compiler will generate all the serialization code. **(That also means try not to use sjCodecOf more than once for any given class or you'll generate a lot of redundant code!)** -You only need to worry about generating codecs for your top-most level classes. Some serialization libraries require all classes to be specifically called out. ScalaJack doesn't require this. For example: +You only need to worry about generating codecs for your top-most level classes. Some serialization libraries require all nested classes in an object hierarchy to be +specifically called out for code generation. ScalaJack doesn't require this. For example: ```scala case class Dog(name: String, numLegs: Int) @@ -70,7 +73,7 @@ In a non-macro program (e.g. something using Scala 2 runtime reflection) if you That's **not** necessarily what happens with macros! Remember, the macro code is run at compile-time. File2.scala needs to be re-compiled because the macro needs to be re-run to pick up your changes to Foo class in File1.scala. **Unfortunately sbt doesn't pick up this dependency!** If you don't know any better you'll just re-run your program after a change to File1.scala, like normal, and get a **spectacular exception with exotic errors** that won't mean much to you. The simple, but non-intuitive, solution is you need to also recompile File2.scala. -This means you will be doing more re-compiling with macro-based code than you would without the macros. It's an unfortunate cost of inconvenience and time, but the payoff is a *dramatic* gain in speed at runtime, and in the case of reflection in Scala 3, using macros is really the only way to accomplish reflection. +This means you will be doing more re-compiling with macro-based code than you would without the macros. It's an unfortunate cost of inconvenience, but the payoff is a *dramatic* gain in speed at runtime, and in the case of reflection in Scala 3, using macros is the only way to accomplish reflection, so there really isn't an alternative. ## Features * [Case Classes and Traits](doc/classesAndTraits.md) @@ -85,7 +88,7 @@ This means you will be doing more re-compiling with macro-based code than you wo * [ParseOrElse and Cascading Fallback Parsing](doc/parseOrElse.md) * [Null and None treatment](doc/nullAndNone.md) * [Externalized Type Hints](doc/externalTypes.md) -* [View/SpliceInto](doc/viewSplice.md) +* [NeoType Support](doc/neotype.md) * [Filter](doc/filter.md) * [Union type](doc/union.md) * [Converters](doc/map.md) diff --git a/build.sbt b/build.sbt index 63d10e11..3d67a941 100644 --- a/build.sbt +++ b/build.sbt @@ -35,7 +35,7 @@ lazy val root = project Test / parallelExecution := false, scalafmtOnCompile := !isCI, libraryDependencies ++= Seq( - "co.blocke" %% "scala-reflection" % "fixOptionUnit_7632aa", //"2.0.3", + "co.blocke" %% "scala-reflection" % "applyPath_bug_17e082", "org.apache.commons" % "commons-text" % "1.11.0", "io.github.kitlangton" %% "neotype" % "0.0.9", "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core" % "2.24.5-SNAPSHOT", diff --git a/doc/any.md b/doc/any.md index 9a74feb7..f5d4a675 100644 --- a/doc/any.md +++ b/doc/any.md @@ -1,18 +1,23 @@ + ## Any Support -Scala has the concept of Any. ScalaJack supports Any but you should be aware of its special needs and limitations. +Scala has the Any type. ScalaJack supports Any but you should be aware of its special needs and limitations. -A value of type Any means ScalaJack has no specific idea what the type should be, and must therefore infer the type as best it can. This is necessarily an imperfect process but we can describe the process it uses here. The main takeaway is this: Virtually everywhere else, if you render an object with ScalaJack to JSON, then read that object back in, you should get the original object. This isn't often true with Any-typed data, and we'll see examples of this. +A value of type Any means ScalaJack has no specific idea what the type should be, and must therefore infer the type as best it can. This is necessarily an imperfect process, but we can describe the assumptions it uses here. The main takeaway is this: Virtually everywhere else, if you render an object with ScalaJack to JSON, then read that object back in, you should get the original object. This is often untrue with Any-typed data, and we'll see examples of this. Let's use a simple sample: - +**File1.scala** (Remember: Class definitions must be in a separate file than where the ScalaJack macros are called!) ```scala package com.me -case class Amorphous(thing: Any) -case class Small(num:Int) +case class Amorphous(thing: Any) +case class Small(num:Int) +``` +**File2.scala** +```scala +given sjAmorphousList: ScalaJack[List[Amorphous]] = sjCodecOf[List[Amorphous]] -val all = List( +val all = List( Amorphous(true), Amorphous("blather"), Amorphous(1.234), @@ -20,13 +25,12 @@ val all = List( Amorphous(Map("a"->1,"b"->2)), Amorphous(null), Amorphous(Small(99)) - ) +) -sj.render(all) -``` +sjAmorphousList.toJson(all) +``` This renders: - ```JSON [ {"thing":true}, @@ -35,30 +39,28 @@ This renders: {"thing":[1,2,3]}, {"thing":{"a":1,"b":2}}, {"thing":null}, - {"thing":{"_hint":"com.me.Small","num":99}} + {"thing":{"num":99}} ] ``` -So far, so good, right? It gets a bit more complicated... +So far, so good, right? All the values seem to be rendered well. Now it gets a bit more complicated... ### Classes and Maps -You can see from the sample above that when an Any-typed value is populated with an object, it is rendered like a trait, with its type hint (only the default type hint "_hint" is supported for now). This is so ScalaJack knows what class to materialize upon reading this JSON. -Without a type hint, the JSON object will be inferred to be just a key/value Map in Scala. +You can see from the sample above that when an Any-typed value is populated with an object, it is rendered like any class. However there's no way for ScalaJack to know what class to reconstruct when parsing an Any value. Any JSON value in braces '{ }' will be presumed to be a Map, regardless of what they actually were when the JSON was rendered. + +### Options -**Note:** Option[] values cannot be inferred as an Any value. Rendering Some(thing) would always be read as thing. ScalaJack would never be able to infer Some(thing) vs thing from JSON. +Option[] values cannot be inferred as an Any value. Rendering Some(thing) would always be read as simply 'thing'. ScalaJack has no information this value was originally an Option, in order to wrap it in Some(). + ### Numbers -When reading a numerical value, ScalaJack must infer what kind of numerical type to use. There's no right/wrong answer here, so ScalaJack uses a simple fitting mechanism. The fittings are shown in the table below, but one important thing to keep in mind: If you render an Any numerical value and read it back in, the value read in may be a different type than you rendered! ScalaJack takes great pains to try to ensure a read object matches the rendered original, but for Any this promise is not always possible to keep. - -|Scala render() Type for Any|ScalaJack read() Type for Any| -|-------|-------| -|Byte |Long -|Short |Long -|Int |Long -|Long |Long -|BigInt |Long if it fits, else BigInt -|Float |Double -|Double |Double -|BigDecimal |Double if it fits, else BigDecimal - ->**Remember that when processing Any, there is no wrong answer--any returned value, in any type, is an Any! There's just expected and unexpected on your part.** + +When reading a numerical value, ScalaJack must infer what kind of numerical type to use. There's no right/wrong answer here, so ScalaJack uses a simple fitting mechanism. If ScalaJack's parser detects a numerical value for Any, it reads it in as a BigDecimal value, then attempts to "fit" the number in this order: + +1. Int if value is a valid Int +2. Long if value is a valid Long +3. Double if value is a valid Double +4. BigInt if value is valid BigInt +5. otherwise leave it as BigDecimal + +>**Remember that when processing Any, there is no wrong answer--any returned value, in any type, is a valid Any value. There's just expected and unexpected on your part.** \ No newline at end of file diff --git a/doc/classesAndTraits.md b/doc/classesAndTraits.md index 1199d5fc..a20507d7 100644 --- a/doc/classesAndTraits.md +++ b/doc/classesAndTraits.md @@ -1,12 +1,16 @@ + ## Case Classes and Traits -Parsing and serializing case classes is as easy as it gets with ScalaJack. Serializing is simply a call to render(): +Parsing and serializing case classes is vey straightforward with ScalaJack. Serializing is simply a call to toJson(): ```scala -case class Person(name:String, age:Int) -val sj = ScalaJack() -val js = sj.render(Person("Mike",32)) +// Classes must be defined in separate file. Sorry--it's a macro thing +// case class Person(name:String, age:Int) + +given sjPerson: ScalaJack[Person] = sjCodecOf[Person] + +val js = sjPerson.toJson(Person("Mike",32)) // renders {"name":"Mike","age":32} ``` @@ -15,66 +19,87 @@ All basic Scala primitive data types are supported in addition to Java primitive Of course you can nest collections and case classes as you like: ```scala -case class Hobby(desc:String, cost:Double) -case class Person(name:String, age:Int, hobbies:List[Hobby]) -sj.render(Person("Mike",32,List(Hobby("surfing",1000.0)))) +// case class Hobby(desc:String, cost:Double) +// case class Person(name:String, age:Int, hobbies:List[Hobby]) + +given sjPerson: ScalaJack[Person] = sjCodecOf[Person] + +val js = sjPerson.toJson(Person("Mike",32,List(Hobby("surfing",1000.0)))) // renders {"name":"Mike","age":32,"hobbies":[{"desc":"surfing","cost":1000.0}]} ``` -Parsing JSON into a case class is similarly easy, with the addition of a given type parameter to tell ScalaJack what kind of class you're trying to construct: +Parsing JSON into a case class is also simple: ```scala val js = """{"name":"Mike","age":32}""" -val person = sj.read[Person](js) +val person = sjPerson.fromJson(js) ``` This works for collections too: ```scala +given sjListOfPerson: ScalaJack[List[Person]] = sjCodecOf[List[Person]] +given sjMapOfPerson: ScalaJack[Map[Person]] = sjCodecOf[Map[Person]] + val js = """[ {"name":"Mike","age":32}, {"name":"Sarah","age":29} ]""" -val person = sj.read[List[Person]](js) +val persons = sjListOfPersion.fromJson(js) val js2 = """{ "surfers":[{"name":"Mike","age":32}] }""" -val byHobby = sj.read[Map[String,List[Person]]](js) +val byHobby = sjMapOfPersion.fromJson(js) ``` So you can see the combinations can be as complex as you need. ### Traits -Traits work the same way but with the small addition of a type hint. This is to instruct ScalaJack what concrete class to construct from a given trait type. Consider this example: - +ScalaJack 8 works with sealed traits only. + +> This is a significant departure from previous versions of ScalaJack, +> which handled any trait, sealed or not. ScalaJack 8 is almost entirely +> a macro, which means everything about serialization must be known at +> compile-time. For a non-sealed trait, we can add a type hint to +> rendered JSON, but upon parsing that JSON we would have to retrieve +> the type hint and materialize the concrete class given by the hint. +> Problem is: we won't know what class that is until runtime, so +> ScalaJack 8's macro infrastructure has nothing to work with at +> compile-time. In order to support non-sealed traits, like older +> ScalaJack, we would need to create an entire complimentary mirror of +> ScalaJack 8 that ran only at runtime. At this time, we're not +> convinced the level of work required to accomplish this would be worth +> the use case. + +Example of sealed trait use: + +**Pet.scala** ```scala -package com.me - -trait Pet{ val name:String; val numLegs:Int } +sealed trait Pet{ val name:String; val numLegs:Int } case class Dog(name:String, numLegs:Int) extends Pet +case class Cat(name:String, numLegs:Int, numberOfNaps: Int) extends Pet +``` + +**MyProg.scala** +```scala +given sjDog: ScalaJack[Dog] = sjCodecOf[Dog] // not needed to serialize Pet--only here to show difference! +given sjPet: ScalaJack[Pet] = sjCodecOf[Pet] // render trait Pet (any of its sealed children: Cat or Dog) val inst:Pet = Dog("Fido",4) -// Render as a case class -val jsDog = sj.render(inst) // renders {"name":"Fido","numLegs":4} -sj.read[Dog](jsDog) +// Render Dog as a case class +val jsDog = sjDog.toJson(inst) // renders {"name":"Fido","numLegs":4} +sjDog.fromJson(jsDog) // Render as a trait -val jsPet = sj.render[Pet](inst) // renders {"_hint":"com.me.Dog","name":"Fido","numLegs":4} -sj.read[Pet](jsPet) +val jsPet = sjPet.toJson(inst) // renders {"_hint":"Dog","name":"Fido","numLegs":4} +sjPet.fromJson(jsPet) // Oops! -sj.read[Pet](jsDog) // Explodes! No type hint means ScalaJack doesn't know which Pet to make +sjDog.fromJson(jsDog) // Fails. No type hint means ScalaJack doesn't know which Pet to make ``` -OK, there's a few things going on here. Let's work backwards. Notice I can render my instance either as a case class (Dog) or a trait (Pet). I "force" the trait rendering by providing the type of the trait, Pet, in the render() call, which tells ScalaJack to add a type hint field in the rendered output. That's because you rendered a Pet, not a Dog. Without the hint, ScalaJack has no idea that this Pet should, in fact, be a Dog when read back in, so we have to leave it a hint. - -Reading a properly serialized trait back in is simple: -```scala -val js = """{"_hint":"com.me.Dog","name":"Fido","numLegs":4}""" -sj.read[Pet](js) -``` The given type hint will allow ScalaJack to construct a Dog object and return it as a Pet, which is what you want. -We'll see in a later section how you can customize both the type hint label and the the hint value in your JSON. +We'll see in a later section how you can customize both the type hint label and the the hint value in your JSON. \ No newline at end of file diff --git a/doc/dynamo.md b/doc/dynamo.md deleted file mode 100644 index b74ccfa5..00000000 --- a/doc/dynamo.md +++ /dev/null @@ -1,41 +0,0 @@ -## DynamoDB Support -ScalaJack provides some limited support for DynamoDB. To be very clear, ScalaJack is a serialization product, and does not involve itself in database CRUD calls, i.e. it isn't a Dynamo library or wrapper. What ScalaJack does do is try to make the process of calling Dynamo easier and contain less boilerplate by using its powerful reflection abilities to create appropriate objects for Dynamo. - -First of all you'll need to include a separate scalajack_dynamo library as shown in the main README file. (We separated it so non-Dynamo users won't have to include Dynamo library dependencies.) - -#### Table Creation -DynamoDB table creation is accomplished (in Dynamo's native Java API) via method calls accepting a CreateTableRequest object. ScalaJack provides a feature to create a CreateTableRequest. - -```scala -case class Misc(wow: Double, bing: String) - -@Collection(name = "people") -case class Person( - @DBKey(index = 1) name:String, - @DBKey(index = 0) age:Int, - likes: List[String], - stuff: Misc, - foo: Option[Boolean] = None -) - -val sj = ScalaJack(DynamoFlavor()) -val req = sj.asInstanceOf[DynamoFlavor].createTableRequest[PersonOneKey](new ProvisionedThroughput(12L, 5L)) -``` -First let's look at the Person class. Notice the annotations. The @Collection annotation marks the class with a given table name in Dynamo. The two @DBKey annotations mark the primary key and sort key. Notice the index values in the @DBKey annotation. Index 0 is the primary key and index 1 is the sort key. Just like in Dynamo, the sort key is optional. - -Note that Dynamo's rules apply, meaning a key must be either String or Number. - -Once constructed, you can use the CreateTableRequest as you usually would, adding more to it via its built-in methods, then using it to actually create the table. - -**NOTE:** Do you see the .asInstanceOf in the code above? We need that. createTableRequest is not a method on stock ScalaJack--only the Dynamo flavor so we need to cast it first. - -#### Item -The other feature ScalaJack is very helpful for in Dynamo is Item. Rendering an Item (or reading from it) is exactly like ScalaJack for JSON, except instead of JSON you have a Dynamo Item object: - -```scala -val inst: Person = Person("Greg", 50, List("Woodworking", "Diet Coke"), Misc(1.23, "boom")) -val item:Item = sj.render(inst) -// { Item: {name=Greg, age=50, likes=[Woodworking, Diet Coke], stuff={wow=1.23, bing=boom}} } - -sj.read[Person](item) // == original Person object -``` diff --git a/doc/json4s.md b/doc/json4s.md deleted file mode 100644 index c110ae1c..00000000 --- a/doc/json4s.md +++ /dev/null @@ -1,10 +0,0 @@ -## Json4s Support -Json4s is a common and convenient way to query and manipulate raw JSON elements. ScalaJack includes support for Json4s by creating a flavor for it, meaning Json4s is a target serialization format. - -```scala -import co.blocke.scalajack.json4s._ -val sj = ScalaJack(Json4sFlavor()) -``` -Then you can use all the usual ScalaJack capabilities. - -Json4sFlavor is very handy if you want to do more than straight object serialization/deserialization, for instance re-process parsed JSON before materializing it into a Scala object. diff --git a/doc/mapname.md b/doc/mapname.md index a26b3496..ba6233ee 100644 --- a/doc/mapname.md +++ b/doc/mapname.md @@ -1,42 +1,18 @@ -## Change Field Names - -If you are using ScalaJack with 3rd party JSON or Mongo documents, you may be in a situation where you don't own or control the names of the fields. You may wish the field names of your classes to be different than the names in the 3rd party format. -ScalaJack provides the @Change annotation. Marking a field with @Change in your case class allows you to map its name to a different target name in JSON or Mongo. +## Change Field Names +If you are using ScalaJack with 3rd party JSON you may be in a situation where you don't own or control the names of fields. You may wish the field names of your classes to be different than the names in the 3rd party format. ScalaJack provides the Marking a field with a @Change annotation in your class allows you to change the names of fields in-flight to/from JSON. + ```scala -case class MapFactor( +case class MapFactor( @Change(name = "foo_bar") fooBar:String, - @Change(name = "a_b") thingy: Long, - count: Int, + @Change(name = "a_b") thingy: Long, + count: Int, @Change(name = "big_mac") bigMac:String ) ``` - If you serialize an instance of this class to JSON you'd get something like: - ```JSON {"foo_bar":"hey","a_b":25,"count":3,"big_mac":"hungry"} ``` - Notice that several of these field names are re-mapped to new values, presumably to match the format required by a 3rd party JSON provider. - -This works in Mongo too, including mixing it up with the @DBKey annotation: - -```scala -case class MapFactorId2( - @DBKey @Change(name = "foo_bar") fooBar:String, - @DBKey @Change(name = "a_b") thingy: Long, - @DBKey hey: Int, - count: Int, - @Change(name = "big_mac") bigMac: String -) -``` - -A serialized instance of this class might look like this: - -```json -{ "_id" : { "foo_bar" : "wonder", "a_b" : { "$numberLong" : "25" }, "hey" : 1 }, "count" : 3, "big_mac" : "hungry" } -``` - -Again you can see the @Change annotated fields had their name re-mapped to the specified values. diff --git a/doc/mongo.md b/doc/mongo.md deleted file mode 100644 index 9d9534a9..00000000 --- a/doc/mongo.md +++ /dev/null @@ -1,47 +0,0 @@ -## MongoDB Support - -ScalaJack doesn't wrap the MongoDB persistence libraries--that's not its mission. It does provide a way to convert classes to/from Mongo BsonDocuments. You'll need to include the mongo support package as shown in the Use section of the main Readme. Then you need to create a Mongo-specific ScalaJack object: - -```scala - import co.blocke.scalajack._ - import mongo._ - - val sjMongo = ScalaJack(MongoFlavor()) // produce a Mongo-flavored ScalaJack - val mydbo = sjMongo.render( myCaseClass ) - val myCaseClass = sjMongo.read[MyClass]( mydbo ) -``` - -The trait type hint label and value modifiers (.withAdapters, .withDefaultHint, .withHints) work like they do for JSON documents. - -### Keys - -You must specify your Mongo collection's keys using annotations in your Scala classes. Both single and compound keys are supported. - -```scala - case class OneKey( - @DBKey customerNum: String, - address: Address - ) - - case class TwoKeys( - @DBKey customerNum: String, - @DBKey countyCode: Int, - address: Address - ) - - case class UsingOID( - @DBKey myKey: ObjectID, - otherInfo: String - ) -``` - -ScalaJack will generate the appropriate key field(s) for Mongo using the DBKey annotation. - -### Documents - -ScalaJack's MongoDB module produces BsonDocument objects upon render -- specifically org.bson.BsonDocument. This BsonDocument type is also expected for read operations. - -> **Note:** Previous versions of ScalaJack produced the Scala driver's Document class, not the BsonDocument class. If you prefer, you can implement an implicit conversion like this: -> ```scala -> implicit def BsonDocument2Document(x: BsonValue) = new Document(x.asInstanceOf[BsonDocument]) -> ``` diff --git a/doc/noncase.md b/doc/noncase.md index 8490ad53..c948f5da 100644 --- a/doc/noncase.md +++ b/doc/noncase.md @@ -1,11 +1,12 @@ + ## Non-Case and Java Classes -In the last section we saw how easily ScalaJack handles case classes and Scala traits. The Scala case class is a fantastic thing, and does a lot behind the scenes on your behalf, but if you follow certain conventions, ScalaJack can work with non-case and Java classes too. +In the last section we saw how easily ScalaJack handles case classes and Scala traits. If you follow certain conventions, ScalaJack can work with non-case and Java classes too. -The big challenge for ScalaJack is to know what the fields of a class are. In a case class this is easy: they're given in the constructor. In a non-case class they *may* be in the constructor--or not. We can't rely on this, so... +An important challenge for ScalaJack is to know what the fields of a class are. In a case class this is easy: they're given in the constructor. In a non-case class they *may* be in the constructor--or not. We can't rely on this, so... -### Scala Non-Case Class val constructor (most preferred method) +### Scala Non-Case Class val constructor If you have a non-case Scala class, and you specify 'val' for each of the constructor arguments, ScalaJack will treat it just as a case class. But... **all** the arguments but be specified with val! ```scala @@ -17,8 +18,8 @@ class Employer( name:String, val est:java.time.LocalDate ) ``` -### Scala Non-Case Class with getters and setters (ok method) -Another way ScalaJack can detect your class fields using standard Scala getter/setter format with a private var field: +### Scala Non-Case Class with getters and setters +ScalaJack can optionally detect your non-case class fields using standard Scala getter/setter format with a private var field: ```scala class Employer() { @@ -31,31 +32,10 @@ class Employer() { def est_=(e:LocalDate) = _est = e } ``` +When these getter/setter fields are detected they will be serialized, and when such a class is instantiated these fields will be set to their original values by calling the setters. -If you have any fields with either a getter or setter that you do *not* want serialized, put an @Ignore annotation on either the getter or setter and ScalaJack will ignore that field. - -### Scala Non-Case Class with public var (least preferred method) -A final way to specify your Scala class fields is to define them as public var fields, which is bad for the obvious reason of violating data hiding. ScalaJack finds these fields and treats them as class fields. - -```scala -class Employer() { - var name:String = "" - var est:LocalDate = LocalDate.now() - var profitMargin:Double = 21.0 -} -``` - -ScalaJack's auto-detection of all var fields means that *all* public var fields are treated as class members! This may not be what you want, so ScalaJack provides an @Ignore annotation to let you ignore var fields you don't want detected and treated as class members: - -```scala -class Employer() { - var name:String = "" - var est:LocalDate = LocalDate.now() - @Ignore var profitMargin:Double = 21.0 -} -``` - -As you know, exposing all var fields like this is very poor practice, so we strongly recommend against using the public vars method of field detection. +> If you have any fields with either a getter or setter that you do +> *not* want serialized, put an @Ignore annotation on either the getter or setter and ScalaJack will ignore that field. ### Default Values and Option With any of these methods it is possible to supply default values @@ -69,27 +49,9 @@ class Employer( val label:String = "ACME" ) { @Optional var num: Int = 5 } ``` -Here you can see the defaults "ACME", "Fred", and 5 supplied as defaults to the detectable class members. But what's that @Optional annotation? Scala requires a value be provided for non-constructor vars (or the class would be abstract). ScalaJack assumes all fields detected are required, so if a parsed class is missing a field, say name, it wouldn't know whether that's a missing-field error, or whether it should just use "Fred". The @Optional annotation tells ScalaJack that the field is optional in the parsed input, and if it's missing, just use the given value. - -So why not just declare @Optional fields with Scala Option? What happens if you have Option type value with a default? - -```scala - private var _name:Option[String] = Some("Fred") - def name: Option[String] = _name - def name_=(n:Option[String]) = _name = n -``` -In this case, if name was missing from the parsed input, ScalaJack would always read its value as None. What if I want the default Some("Fred")? Using @Optional will work: - -``` -```scala - private var _name:Option[String] = Some("Fred") - @Optional def name: Option[String] = _name - def name_=(n:Option[String]) = _name = n -``` -@Optional gives you more control over how fields are parsed. ### Java Class Support -ScalaJack's support for Java classes is more limited due to Java's looser handling of types and constructors. In order for Scala to detect your class fields your Java class must fit standard JavaBean notation for getters and setters (BeanInfo is used inside ScalaJack to detect your getters and setters): +ScalaJack's support for Java classes is more limited due to Java's looser handling of types and constructors. In order for Scala to detect your class fields your Java class must fit standard JavaBean notation for getters and setters (BeanInfo is used inside ScalaJack to detect your getters and setters). Your class must also specify a zero-argument constructor. ```java public class PlayerJava { @@ -102,4 +64,4 @@ public class PlayerJava { public void setAge(int a) { age = a; } } ``` -As with Scala fields, you can also use the @Ignore annotation on either a getter or setter if you don't want a JavaBean field serialized. +As with Scala fields, you can also use the @Ignore annotation on either a getter or setter if you don't want a JavaBean field serialized. \ No newline at end of file diff --git a/doc/parameterized.md b/doc/parameterized.md index 18e4db0b..59b99ad7 100644 --- a/doc/parameterized.md +++ b/doc/parameterized.md @@ -1,43 +1,55 @@ + ## Parameterized Classes ScalaJack is able to handle parameterized classes and traits seamlessly: +**File1.scala** ```scala package com.me - -trait Vehicle[T]{ val transported:T } +sealed trait Vehicle[T]{ val transported:T } case class Car(transported:Person) extends Vehicle[Person] +``` -println(sj.render[Vehicle[Person]](Car(Person("Fred",25)))) -// {"_hint":"com.me.Car","transported":{"name":"Fred","age":25}} +**File2.scala** +```scala +given sjVehicle: ScalaJack[Vehicle[Person]] = sjCodecOf[Vehicle[Person]] +println(sjVehicle.toJson(Car(Person("Fred",25)))) +// {"_hint":"Car","transported":{"name":"Fred","age":25}} ``` Ok, now let's make it interesting.. +**File1.scala** ```scala -package com.me - object VehicleClass extends Enumeration { type VehicleClass = Value val Land, Air, Sea = Value } -import VehicleClass._ +import VehicleClass.* -trait Vehicle[K <: VehicleClass] { val kind: K } -case class Car(passengers: Int) extends Vehicle[Land.type] { val kind: Land.type = Land } +sealed trait Vehicle { val kind: VehicleClass } +case class Car(passengers: Int) extends Vehicle { val kind: Land.type = Land } -trait Hobby[X, Y] { val thing1: X; val thing2: Y } -trait Artist[W, Z] { val instrument: W; val effort: Z } -trait Person[X, Y] { val who: X; val org: Y } +sealed trait Hobby[X, Y] { val thing1: X; val thing2: Y } +sealed trait Artist[W, Z] { val instrument: W; val effort: Z } +sealed trait Person[X, Y] { val who: X; val org: Y } case class Sports[A, B](thing1: A, thing2: B) extends Hobby[A, B] case class Painter[A, B](instrument: A, effort: B) extends Artist[A, B] case class Employee[A, B, C, D](who: Artist[C, Hobby[D, A]], org: B) extends Person[Artist[C, Hobby[D, A]], B] +type ComplexPerson = Person[Artist[Int, Hobby[Double, Char]], Vehicle] +``` + +**File2.scala** +```scala -val inst: Person[Artist[Int, Hobby[Double, Char]], Vehicle[_ <: VehicleClass]] = Employee(Painter(5, Sports(1.2, 'Z')), "wow") -val js = sj.render(inst) -// {"_hint":"com.me.Employee","who":{"_hint":"com.me.Painter","instrument":5,"effort":{"_hint":"com.me.Sports","thing1":1.2,"thing2":"Z"}},"org":{"_hint":"com.me.Car","passengers":4}} +given sjPerson: ScalaJack[ComplexPerson] = sjCodecOf[ComplexPerson] +val inst: ComplexPerson = Employee(Painter(5, Sports(1.2, 'Z')), Car(4)) +val js = sjPerson.toJson(inst) -val orig = sj.read[Person[Artist[Int, Hobby[Double, Char]], Vehicle[_ <: VehicleClass]]](js) +// {"_hint":"Employee","who":{"_hint":"Painter","instrument":5,"effort":{"_hint":"Sports","thing1":1.2,"thing2":"Z"}},"org":{"_hint":"Car","passengers":4}} + +sjPerson.fromJson(js) // re-constitutes inst ``` -Clearly this is a contrived (and forced) example, but it does show that complex nesting relationships between parameterized types work just fine, even when we threw in some traits and an Enumeration for good measure. Remember that you always have control of ScalaJack's reading and rendering. You can force a more general trait (will generate type hints) or use a specific concrete class (won't need type hints). + +Clearly this is a contrived example, but it does show that complex nesting relationships between parameterized types work just fine, even when we threw in some traits and an Enumeration for good measure. Remember that you always have control of ScalaJack's reading and rendering. You can force a more general trait (will generate type hints) or use a specific concrete class (won't need type hints). \ No newline at end of file diff --git a/doc/valueClass.md b/doc/valueClass.md index 8f0217c6..2c0d9d97 100644 --- a/doc/valueClass.md +++ b/doc/valueClass.md @@ -1,19 +1,19 @@ + ## Value Class Support ScalaJack supports value classes seamlessly. + **File1.scala** ```scala -case class UUID_VC(underlying: UUID) extends AnyVal - -val u = UUID_VC(UUID.randomUUID) -val js = sj.render(u) -println(js) -// prints: "85c80eb0-5973-4938-abb7-b29b962531ca" - -println(sj.read[UUID_VC](js)) -// prints: UUID_VC("85c80eb0-5973-4938-abb7-b29b962531ca") +case class UUID_VC(underlying: UUID) extends AnyVal ``` +**File2.scala** +```scala +given sjUUID: ScalaJack[UUID_VC] = sjCodecOf[UUID_VC] +val u = UUID_VC(UUID.randomUUID) +val js = sjUUID.toJson(u) +println(js) // prints: "85c80eb0-5973-4938-abb7-b29b962531ca" +println(sjUUID.fromJson(js)) // prints: UUID_VC("85c80eb0-5973-4938-abb7-b29b962531ca") +``` You can see here that the wrapping/unwrapping of the value class is handled by ScalaJack and from a JSON perspective the value is treated as though it was never wrapped at all. - -Note that if you want custom read/render handling for your underlying type, the process for customization is the same as for the naked type. In other words the fact that the type is wrapped in a value class is immaterial to the customized handling. diff --git a/doc/viewSplice.md b/doc/viewSplice.md deleted file mode 100644 index a14adcdb..00000000 --- a/doc/viewSplice.md +++ /dev/null @@ -1,38 +0,0 @@ -## View / SpliceInto - -The use case for view/spliceInto is where you have a master class, which may contain some system-private information. We may also need a 'lite' version of the master class for transport to a UI, but the lite version must not contain any of the private information. We want the lite version to be a "view" (projection) of the master class. If the UI modifies the lite version, we want to splice its changes back into the master. - -First the classes: -```scala -case class Master( - name: String, - id: Long, - underwearSize: Char, - favorites: List[String] - ) - -case class Subset( - name: String, - id: Long, - favorites: List[String] - ) -``` - -Here we have a Subset class that doesn't include the person's underwearSize. Now let's see how to project that view: - -```scala -val master = Master("Fred",123L, 'M', List("music","football")) -val subset = sj.view[Subset](master) -// subset = Subset("Fred", 123L, List("music","football")) -``` - -We now have a Subset object that's "safe" to transport. - -Now let's assume that something has modified Subset and we want to recombine it back with Master. We can do it this way: - -```scala -val newMaster = sj.spliceInto(modifiedSubset, master) -``` - -Easy, right? - diff --git a/doc/yaml.md b/doc/yaml.md deleted file mode 100644 index 7f3e6f8b..00000000 --- a/doc/yaml.md +++ /dev/null @@ -1,21 +0,0 @@ -## YAML Support - -YAML is now supported as a flavor for ScalaJack, so you can serialize your Scala artifacts directly to/from YAML documents. - -All the normal trait-handling, type hints, modifiers, etc. that apply in JSON apply in YAML too, so you lose no flexibility. - -With YAML you gain the ability to natively support non-String (i.e. complex) Map keys. ScalaJack's JSON flavor allowed this too, but required serializing the non-String key into a String, which frankly is a bit messy. No need for that anymore with YAML! - -One thing that is different for YAML is there are no permissive primitives settings (.withPermissivePrimitives), for example "true" = true, "123" = 123. In YAML, permissive primitive behavior is default unless you quote your values. - -Usage is as you'd expect: -```scala -import co.blocke.scalajack.yaml._ - -case class Person(...) - -val sj = ScalaJack(YamlFlavor()) -sj.render(Person("Fred",34)) -val myPerson = sj.read[Person](someYaml) -``` -One limitation: ScalaJack's YAML parser does not handle multi-document YAML input, so basically one top-level class is parsed. If that is a huge need, open an issue and we'll see what we can do. \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala b/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala index b2d97909..54379541 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala @@ -353,20 +353,6 @@ object JsonCodecMaker: f.fieldRef.refType match case '[z] => val fieldValue = Select.unique(in.asTerm, f.name).asExprOf[z] - // val fieldValue = - // f.fieldRef match - // case e: ScalaEnumerationRef[?] => - // try - // val tE = Select.unique(in.asTerm, f.name).asExpr // .asExprOf[z] - // '{ $tE.asInstanceOf[z] } - // catch { - // case t: Throwable => - // println("BOOM: " + f.name) - // println("Type: " + f.fieldRef.refType) - // throw t - // } - // case _ => - // Select.unique(in.asTerm, f.name).asExprOf[z] val fieldName = changeFieldName(f) maybeWrite[z](fieldName, fieldValue, f.fieldRef.asInstanceOf[RTypeRef[z]], out, cfg) } diff --git a/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala b/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala index 2f88ea26..6757be12 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala @@ -10,7 +10,6 @@ class JsonConfig private[scalajack] ( val noneAsNull: Boolean, val tryFailureHandling: TryPolicy, val eitherLeftHandling: EitherLeftPolicy, - // val undefinedFieldHandling: UndefinedValueOption = UndefinedValueOption.THROW_EXCEPTION, // -------------------------- val typeHintLabel: String, val typeHintPolicy: TypeHintPolicy, @@ -58,9 +57,6 @@ enum TryPolicy: enum EitherLeftPolicy: case AS_VALUE, AS_NULL, ERR_MSG_STRING, THROW_EXCEPTION -// enum UndefinedValueOption: -// case AS_NULL, AS_SYMBOL, THROW_EXCEPTION - enum TypeHintPolicy: case SIMPLE_CLASSNAME, SCRAMBLE_CLASSNAME, USE_ANNOTATION @@ -206,15 +202,6 @@ object JsonConfig case '{ EitherLeftPolicy.THROW_EXCEPTION } => Some(EitherLeftPolicy.THROW_EXCEPTION) } - // private[scalajack] given FromExpr[UndefinedValueOption] with { - // def unapply(x: Expr[UndefinedValueOption])(using Quotes): Option[UndefinedValueOption] = - // import quotes.reflect.* - // x match - // case '{ UndefinedValueOption.AS_NULL } => Some(UndefinedValueOption.AS_NULL) - // case '{ UndefinedValueOption.AS_SYMBOL } => Some(UndefinedValueOption.AS_SYMBOL) - // case '{ UndefinedValueOption.THROW_EXCEPTION } => Some(UndefinedValueOption.THROW_EXCEPTION) - // } - private[scalajack] given FromExpr[TypeHintPolicy] with { def unapply(x: Expr[TypeHintPolicy])(using Quotes): Option[TypeHintPolicy] = import quotes.reflect.* diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala deleted file mode 100644 index e552a65f..00000000 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ /dev/null @@ -1,36 +0,0 @@ -package co.blocke.scalajack -package json -package run - -case class Well(repo: scala.collection.mutable.Map[String, Int] = scala.collection.mutable.Map.empty[String, Int]) - -object RunMe extends App: - - import ScalaJack.* - import co.blocke.scalajack.json.run.Record - - // given sjPerson: ScalaJack[Person] = sjCodecOf[Person] - // println(sjPerson.toJson(Person())) - - println(sjCodecOf[Person].toJson(Person())) - - given Well() - - summon[Well].repo.put("a", 1) - summon[Well].repo.put("b", 2) - println(summon[Well]) - - - /* - - given ScalaJack() - - sj[Foo].toJson(myFoo) - - Macro: - 1. Summons SJWell (error if not found) - 2. Looks up codec for Foo. If found, emits a call to it. - 3. If not found, create codec and populate in SJWell - 4. Subsequent calls to sj[Foo] should *NOT* re-gen the macro expansion - - */ \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scalax b/src/main/scala/co.blocke.scalajack/run/Play.scalax new file mode 100644 index 00000000..b313ca6f --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/run/Play.scalax @@ -0,0 +1,32 @@ +package co.blocke.scalajack +package json +package run + +case class Well(repo: scala.collection.mutable.Map[String, Int] = scala.collection.mutable.Map.empty[String, Int]) + +object RunMe extends App: + + import ScalaJack.* + import co.blocke.scalajack.json.run.Record + import co.blocke.scala_reflection.* + + given sjPerson: ScalaJack[Person] = sjCodecOf[Person] + given sjAm: ScalaJack[Amorphous] = sjCodecOf[Amorphous] + + println(sjPerson.toJson(Person())) + + println(sjAm.toJson(Amorphous(Person()))) + + import VehicleClass.* + // type ComplexPerson = PersonX[Artist[Int, Hobby[Double, Char]], Vehicle[? <: VehicleClass]] + // given sjPersonX: ScalaJack[ComplexPerson] = sjCodecOf[ComplexPerson] + // val inst: ComplexPerson = Employee(Painter(5, Sports(1.2, 'Z')), Car(2)) + // val js = sjPersonX.toJson(inst) + // println(js) + + println(RType.of[ComplexPerson].pretty) + given sjPersonX: ScalaJack[ComplexPerson] = sjCodecOf[ComplexPerson] + val inst: ComplexPerson = Employee(Painter(5, Sports(1.2, 'Z')), Car(4)) + val js = sjPersonX.toJson(inst) + println(js) + println(sjPersonX.fromJson(js)) diff --git a/src/main/scala/co.blocke.scalajack/run/Record.scala b/src/main/scala/co.blocke.scalajack/run/Record.scalax similarity index 84% rename from src/main/scala/co.blocke.scalajack/run/Record.scala rename to src/main/scala/co.blocke.scalajack/run/Record.scalax index 350f92b2..8980d5f5 100644 --- a/src/main/scala/co.blocke.scalajack/run/Record.scala +++ b/src/main/scala/co.blocke.scalajack/run/Record.scalax @@ -18,6 +18,8 @@ case class Person( is_employed: Boolean = true ) +case class Amorphous(thing: Any) + case class Address( street: String, city: String, @@ -181,3 +183,24 @@ val record = Record( List(Friend("Jane Smith", 28, "jane.smith@example.com"), Friend("Bob Johnson", 32, "bob.johnson@example.com")), List(Pet("Fido", "Dog", 5), Pet("Whiskers", "Cat", 3)) ) + +//----- + +object VehicleClass extends Enumeration { + type VehicleClass = Value + val Land, Air, Sea = Value +} +import VehicleClass.* + +sealed trait Vehicle { val kind: VehicleClass } +case class Car(passengers: Int) extends Vehicle { val kind: Land.type = Land } + +sealed trait Hobby[X, Y] { val thing1: X; val thing2: Y } +sealed trait Artist[W, Z] { val instrument: W; val effort: Z } +sealed trait PersonX[X, Y] { val who: X; val org: Y } + +case class Sports[A, B](thing1: A, thing2: B) extends Hobby[A, B] +case class Painter[A, B](instrument: A, effort: B) extends Artist[A, B] +case class Employee[A, B, C, D](who: Artist[C, Hobby[D, A]], org: B) extends PersonX[Artist[C, Hobby[D, A]], B] +type ComplexPerson = PersonX[Artist[Int, Hobby[Double, Char]], Vehicle] +// println(RType.of[ComplexPerson].pretty) diff --git a/src/test/scala/co.blocke.scalajack/json/classes/Model.scala b/src/test/scala/co.blocke.scalajack/json/classes/Model.scala index 8d11cfbf..8401e133 100644 --- a/src/test/scala/co.blocke.scalajack/json/classes/Model.scala +++ b/src/test/scala/co.blocke.scalajack/json/classes/Model.scala @@ -70,3 +70,21 @@ case class AbstractClassHolder(a: Command2, b: Animal2, c: City2) case class AbstractClassHolder2[P](a: AThing[P]) case class Empl[T](id: String, data: T, boss: Empl[T], coworkers: List[Empl[T]]) + +object VehicleClass extends Enumeration { + type VehicleClass = Value + val Land, Air, Sea = Value +} +import VehicleClass.* + +sealed trait Vehicle { val kind: VehicleClass } +case class Car(passengers: Int) extends Vehicle { val kind: Land.type = Land } + +sealed trait Hobby[X, Y] { val thing1: X; val thing2: Y } +sealed trait Artist[W, Z] { val instrument: W; val effort: Z } +sealed trait PersonX[X, Y] { val who: X; val org: Y } + +case class Sports[A, B](thing1: A, thing2: B) extends Hobby[A, B] +case class Painter[A, B](instrument: A, effort: B) extends Artist[A, B] +case class Employee[A, B, C, D](who: Artist[C, Hobby[D, A]], org: B) extends PersonX[Artist[C, Hobby[D, A]], B] +type ComplexPerson = PersonX[Artist[Int, Hobby[Double, Char]], Vehicle] diff --git a/src/test/scala/co.blocke.scalajack/json/classes/TraitSpec.scala b/src/test/scala/co.blocke.scalajack/json/classes/TraitSpec.scala index 15942806..27bc6506 100644 --- a/src/test/scala/co.blocke.scalajack/json/classes/TraitSpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/classes/TraitSpec.scala @@ -62,4 +62,11 @@ class TraitSpec() extends AnyFunSpec with JsonMatchers: (re.c.asInstanceOf[Miami].temp == inst.c.asInstanceOf[Miami].temp) shouldEqual (true) (re.d.asInstanceOf[CityRoute].numStreets == inst.d.asInstanceOf[CityRoute].numStreets) shouldEqual (true) } + it("Complex trait relationships must work") { + val inst: ComplexPerson = Employee(Painter(5, Sports(1.2, 'Z')), Car(4)) + val sj = sjCodecOf[ComplexPerson] + val js = sj.toJson(inst) + js should matchJson("""{"_hint":"Employee","who":{"_hint":"Painter","instrument":5,"effort":{"_hint":"Sports","thing1":1.2,"thing2":"Z"}},"org":{"_hint":"Car","passengers":4}}""") + sj.fromJson(js) shouldEqual (inst) + } } diff --git a/src/test/scala/co.blocke.scalajack/json/collections/SeqSetArraySpec.scala b/src/test/scala/co.blocke.scalajack/json/collections/SeqSetArraySpec.scala index f42d218d..fddc90eb 100644 --- a/src/test/scala/co.blocke.scalajack/json/collections/SeqSetArraySpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/collections/SeqSetArraySpec.scala @@ -20,70 +20,70 @@ class SeqSetArraySpec() extends AnyFunSpec with JsonMatchers: val sj = sjCodecOf[SeqHolder[Int]] val js = sj.toJson(inst) js should matchJson("""{"a":null}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Seq of numeric must work") { val inst = SeqHolder[Int](List(1, 2, 3)) val sj = sjCodecOf[SeqHolder[Int]] val js = sj.toJson(inst) js should matchJson("""{"a":[1,2,3]}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Seq of string must work") { val inst = SeqHolder[String](List("a", "b", "c")) val sj = sjCodecOf[SeqHolder[String]] val js = sj.toJson(inst) js should matchJson("""{"a":["a","b","c"]}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Seq of boolean must work") { val inst = SeqHolder[Boolean](List(true, false, true)) val sj = sjCodecOf[SeqHolder[Boolean]] val js = sj.toJson(inst) js should matchJson("""{"a":[true,false,true]}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Seq of seq (nested) must work") { val inst = SeqHolder[List[Int]](List(List(1, 2), List(3, 4))) val sj = sjCodecOf[SeqHolder[List[Int]]] val js = sj.toJson(inst) js should matchJson("""{"a":[[1,2],[3,4]]}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Seq of either must work") { val inst = SeqHolder[Either[Int, Boolean]](List(Right(true), Left(15), Right(false))) val sj = sjCodecOf[SeqHolder[Either[Int, Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)) val js = sj.toJson(inst) js should matchJson("""{"a":[true,15,false]}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Seq of union must work") { val inst = SeqHolder[Int | Boolean](List(true, 15, false)) val sj = sjCodecOf[SeqHolder[Int | Boolean]] val js = sj.toJson(inst) js should matchJson("""{"a":[true,15,false]}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Seq of option must work") { val inst = SeqHolder[Option[Int]](List(Some(1), None, Some(3))) val sj = sjCodecOf[SeqHolder[Option[Int]]] val js = sj.toJson(inst) js should matchJson("""{"a":[1,3]}""") - sj.fromJson(js) shouldEqual(SeqHolder[Option[Int]](List(Some(1), Some(3)))) + sj.fromJson(js) shouldEqual (SeqHolder[Option[Int]](List(Some(1), Some(3)))) } it("Seq of map must work") { val inst = SeqHolder[Map[String, Int]](List(Map("a" -> 1, "b" -> 2), Map("c" -> 3, "d" -> 4))) val sj = sjCodecOf[SeqHolder[Map[String, Int]]] val js = sj.toJson(inst) js should matchJson("""{"a":[{"a":1,"b":2},{"c":3,"d":4}]}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Seq of class must work") { val inst = SeqHolder[Person](List(Person("Bob", 35), Person("Sally", 54))) val sj = sjCodecOf[SeqHolder[Person]] val js = sj.toJson(inst) js should matchJson("""{"a":[{"name":"Bob","age",35},{"name":"Sally","age",54}]}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Set is null must work") { @@ -91,70 +91,70 @@ class SeqSetArraySpec() extends AnyFunSpec with JsonMatchers: val sj = sjCodecOf[SetHolder[Int]] val js = sj.toJson(inst) js should matchJson("""{"a":null}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Set of numeric must work") { val inst = SetHolder[Int](HashSet(1, 2, 3)) val sj = sjCodecOf[SetHolder[Int]] val js = sj.toJson(inst) js should matchJson("""{"a":[1,2,3]}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Set of string must work") { val inst = SetHolder[String](HashSet("a", "b", "c")) val sj = sjCodecOf[SetHolder[String]] val js = sj.toJson(inst) js should matchJson("""{"a":["a","b","c"]}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Set of boolean must work") { val inst = SetHolder[Boolean](HashSet(true, false, true)) val sj = sjCodecOf[SetHolder[Boolean]] val js = sj.toJson(inst) js should matchJson("""{"a":[false,true]}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Set of Set (nested) must work") { val inst = SetHolder[HashSet[Int]](HashSet(HashSet(1, 2), HashSet(3, 4))) val sj = sjCodecOf[SetHolder[HashSet[Int]]] val js = sj.toJson(inst) js should matchJson("""{"a":[[3,4],[1,2]]}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Set of either must work") { val inst = SetHolder[Either[Int, Boolean]](HashSet(Right(true), Left(15), Right(false))) val sj = sjCodecOf[SetHolder[Either[Int, Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)) val js = sj.toJson(inst) js should matchJson("""{"a":[15,true,false]}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Set of union must work") { val inst = SetHolder[Int | Boolean](HashSet(true, 15, false)) val sj = sjCodecOf[SetHolder[Int | Boolean]] val js = sj.toJson(inst) js should matchJson("""{"a":[false,true,15]}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Set of option must work") { val inst = SetHolder[Option[Int]](HashSet(Some(1), None, Some(3))) val sj = sjCodecOf[SetHolder[Option[Int]]] val js = sj.toJson(inst) js should matchJson("""{"a":[3,1]}""") - sj.fromJson(js) shouldEqual(SetHolder[Option[Int]](HashSet(Some(1), Some(3)))) + sj.fromJson(js) shouldEqual (SetHolder[Option[Int]](HashSet(Some(1), Some(3)))) } it("Set of map must work") { val inst = SetHolder[Map[String, Int]](HashSet(Map("a" -> 1, "b" -> 2), Map("c" -> 3, "d" -> 4))) val sj = sjCodecOf[SetHolder[Map[String, Int]]] val js = sj.toJson(inst) js should matchJson("""{"a":[{"c":3,"d":4},{"a":1,"b":2}]}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Set of class must work") { val inst = SetHolder[Person](HashSet(Person("Bob", 35), Person("Sally", 54))) val sj = sjCodecOf[SetHolder[Person]] val js = sj.toJson(inst) js should matchJson("""{"a":[{"name":"Bob","age",35},{"name":"Sally","age",54}]}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Array is null must work") { @@ -162,70 +162,70 @@ class SeqSetArraySpec() extends AnyFunSpec with JsonMatchers: val sj = sjCodecOf[ArrayHolder[Int]] val js = sj.toJson(inst) js should matchJson("""{"a":null}""") - sj.fromJson(js) shouldEqual(inst) + sj.fromJson(js) shouldEqual (inst) } it("Array of numeric must work") { val inst = ArrayHolder[Int](Array(1, 2, 3)) val sj = sjCodecOf[ArrayHolder[Int]] val js = sj.toJson(inst) js should matchJson("""{"a":[1,2,3]}""") - sj.fromJson(js).a.toList shouldEqual(inst.a.toList) + sj.fromJson(js).a.toList shouldEqual (inst.a.toList) } it("Array of string must work") { val inst = ArrayHolder[String](Array("a", "b", "c")) val sj = sjCodecOf[ArrayHolder[String]] val js = sj.toJson(inst) js should matchJson("""{"a":["a","b","c"]}""") - sj.fromJson(js).a.toList shouldEqual(inst.a.toList) + sj.fromJson(js).a.toList shouldEqual (inst.a.toList) } it("Array of boolean must work") { val inst = ArrayHolder[Boolean](Array(true, false, true)) val sj = sjCodecOf[ArrayHolder[Boolean]] val js = sj.toJson(inst) js should matchJson("""{"a":[true,false,true]}""") - sj.fromJson(js).a.toList shouldEqual(inst.a.toList) + sj.fromJson(js).a.toList shouldEqual (inst.a.toList) } it("Array of Array (nested) must work") { val inst = ArrayHolder[Array[Int]](Array(Array(1, 2), Array(3, 4))) val sj = sjCodecOf[ArrayHolder[Array[Int]]] val js = sj.toJson(inst) js should matchJson("""{"a":[[1,2],[3,4]]}""") - sj.fromJson(js).a.map(_.toList).toList shouldEqual(inst.a.map(_.toList).toList) + sj.fromJson(js).a.map(_.toList).toList shouldEqual (inst.a.map(_.toList).toList) } it("Array of either must work") { val inst = ArrayHolder[Either[Int, Boolean]](Array(Right(true), Left(15), Right(false))) val sj = sjCodecOf[ArrayHolder[Either[Int, Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)) val js = sj.toJson(inst) js should matchJson("""{"a":[true,15,false]}""") - sj.fromJson(js).a.toList shouldEqual(inst.a.toList) + sj.fromJson(js).a.toList shouldEqual (inst.a.toList) } it("Array of union must work") { val inst = ArrayHolder[Int | Boolean](Array(true, 15, false)) val sj = sjCodecOf[ArrayHolder[Int | Boolean]] val js = sj.toJson(inst) js should matchJson("""{"a":[true,15,false]}""") - sj.fromJson(js).a.toList shouldEqual(inst.a.toList) + sj.fromJson(js).a.toList shouldEqual (inst.a.toList) } it("Array of option must work") { val inst = ArrayHolder[Option[Int]](Array(Some(1), None, Some(3))) val sj = sjCodecOf[ArrayHolder[Option[Int]]] val js = sj.toJson(inst) js should matchJson("""{"a":[1,3]}""") - sj.fromJson(js).a.toList shouldEqual(ArrayHolder[Option[Int]](Array(Some(1), Some(3))).a.toList) + sj.fromJson(js).a.toList shouldEqual (ArrayHolder[Option[Int]](Array(Some(1), Some(3))).a.toList) } it("Array of map must work") { val inst = ArrayHolder[Map[String, Int]](Array(Map("a" -> 1, "b" -> 2), Map("c" -> 3, "d" -> 4))) val sj = sjCodecOf[ArrayHolder[Map[String, Int]]] val js = sj.toJson(inst) js should matchJson("""{"a":[{"a":1,"b":2},{"c":3,"d":4}]}""") - sj.fromJson(js).a.toList shouldEqual(inst.a.toList) + sj.fromJson(js).a.toList shouldEqual (inst.a.toList) } it("Array of class must work") { val inst = ArrayHolder[Person](Array(Person("Bob", 35), Person("Sally", 54))) val sj = sjCodecOf[ArrayHolder[Person]] val js = sj.toJson(inst) js should matchJson("""{"a":[{"name":"Bob","age",35},{"name":"Sally","age",54}]}""") - sj.fromJson(js).a.toList shouldEqual(inst.a.toList) + sj.fromJson(js).a.toList shouldEqual (inst.a.toList) } } } From 2b2d4fdebf9efb2fecb55c1bb3408867615ca01b Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Sat, 4 May 2024 00:49:08 -0500 Subject: [PATCH 59/65] lots of docs and a refactor of SJConfig --- README.md | 9 +- build.sbt | 4 +- doc/config.md | 108 ------------------ doc/custom.md | 99 ---------------- doc/delimited.md | 44 ------- doc/externalTypes.md | 69 ----------- doc/filter.md | 30 ----- doc/map.md | 86 -------------- doc/neotype.md | 69 +++++++++++ doc/nullAndNone.md | 51 +++++++-- doc/parseOrElse.md | 63 ---------- doc/tryAndCapture.md | 74 ------------ doc/typeHint.md | 97 ++++++---------- doc/union.md | 34 +++--- .../{json/JsonConfig.scala => SJConfig.scala} | 66 +++++------ .../scala/co.blocke.scalajack/ScalaJack.scala | 10 +- .../json/JsonCodecMaker.scala | 12 +- .../co.blocke.scalajack/json/JsonError.scala | 1 - .../json/schema/JsonSchema.scala | 10 +- .../json/writing/AnyWriter.scala | 8 +- .../scala/co.blocke.scalajack/run/Play.scalax | 32 +++--- .../co.blocke.scalajack/run/Record.scalax | 22 +--- .../json/classes/ClassSpec.scala | 8 +- .../json/classes/TraitSpec.scala | 6 +- .../json/collections/JavaCollSpec.scala | 4 +- .../json/collections/SeqSetArraySpec.scala | 6 +- .../json/misc/AliasSpec.scala | 2 +- .../json/misc/EnumSpec.scala | 10 +- .../json/misc/LRSpec.scala | 12 +- .../json/misc/MiscSpec.scala | 4 +- .../json/misc/OptionSpec.scala | 8 +- .../json/misc/TrySpec.scala | 8 +- 32 files changed, 265 insertions(+), 801 deletions(-) delete mode 100644 doc/config.md delete mode 100644 doc/custom.md delete mode 100644 doc/delimited.md delete mode 100644 doc/externalTypes.md delete mode 100644 doc/filter.md delete mode 100644 doc/map.md create mode 100644 doc/neotype.md delete mode 100644 doc/parseOrElse.md delete mode 100644 doc/tryAndCapture.md rename src/main/scala/co.blocke.scalajack/{json/JsonConfig.scala => SJConfig.scala} (76%) diff --git a/README.md b/README.md index aa6d7383..d6e17850 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ libraryDependencies ++= Seq("co.blocke" %% "scalajack" % SJ_VERSION) ``` Now you're good to go! Let's use ScalaJack in your project to serialize/de-serialize a case class object into JSON: ```scala -import co.blocke.scalajack._ +import co.blocke.scalajack.* case class Person(name: String, age: Int) @@ -83,16 +83,9 @@ This means you will be doing more re-compiling with macro-based code than you wo * [Value Class Support](doc/valueClass.md) * [Parameterized Classes](doc/parameterized.md) * [Trait Type Hint Customization](doc/typeHint.md) -* [Custom Type Adapters (custom read/render)](doc/custom.md) -* [Try and Capture](doc/tryAndCapture.md) -* [ParseOrElse and Cascading Fallback Parsing](doc/parseOrElse.md) * [Null and None treatment](doc/nullAndNone.md) -* [Externalized Type Hints](doc/externalTypes.md) * [NeoType Support](doc/neotype.md) -* [Filter](doc/filter.md) * [Union type](doc/union.md) -* [Converters](doc/map.md) -* [ScalaJack Configuration](doc/config.md) * [Gimme Speed!](doc/speed.md) ### Notes: diff --git a/build.sbt b/build.sbt index 3d67a941..8a564953 100644 --- a/build.sbt +++ b/build.sbt @@ -35,10 +35,9 @@ lazy val root = project Test / parallelExecution := false, scalafmtOnCompile := !isCI, libraryDependencies ++= Seq( - "co.blocke" %% "scala-reflection" % "applyPath_bug_17e082", + "co.blocke" %% "scala-reflection" % "2.0.6", "org.apache.commons" % "commons-text" % "1.11.0", "io.github.kitlangton" %% "neotype" % "0.0.9", - "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core" % "2.24.5-SNAPSHOT", "org.scalatest" %% "scalatest" % "3.2.17" % Test, "org.json4s" %% "json4s-core" % "4.0.6" % Test, "org.json4s" %% "json4s-native" % "4.0.6" % Test @@ -83,4 +82,3 @@ lazy val compilerOptions = Seq( "utf8" ) -//enablePlugins(ScalaNativePlugin) diff --git a/doc/config.md b/doc/config.md deleted file mode 100644 index f1010e33..00000000 --- a/doc/config.md +++ /dev/null @@ -1,108 +0,0 @@ - -## ScalaJack Configuration and Usage - -### ScalaJack Instantiation -All ScalaJack usage starts with creating a ScalaJack instance. - -```scala -val sj = ScalaJack() -``` - -This instance is used for all serialization activities. - -### Flavors - -ScalaJack supports several "flavors": JSON, Delimited (e.g. CSV), MongoDB, and DynamoDB out of the box. Others may create new flavors by extending the pattern. - -Here's how to get the right flavor of ScalaJack for your needs: - -|Flavor |ScalaJack instantiation -|-----|-------- -|JSON | val sj = ScalaJack() // JSON is default if no flavor given -|Delimited | val sj = ScalaJack(DelimitedFlavor()) -|MongoDB | val sj = ScalaJack(MongoFlavor()) -|DynamoDB | val sj = ScalaJack(DynamoFlavor()) - -Note that MongoDB and DynamoDB require a separate library, and are not included in the core ScalaJack package. - -### ScalaJack Configuration Methods -ScalaJack can be configured using "chain" methods, i.e. you chain them together, for example: - -```scala -val sj = ScalaJack() - .parseOrElse((typeOf[Person],typeOf[DefaultPerson])) - .withDefaultHint("kind") -``` -Most of these configurations are JSON-only, as they don't make sense for the other formats. An exception will be thrown if a configuration method is used that isn't supported for a particular ScalaJack flavor. - -#### allowPermissivePrimitives() -Third party JSON can be pretty messy! They may make booleans and numbers into strings. allowPermissivePrimitives() allows ScalaJack to be a little less strict and try to accept "true" as true, for example. It's a little adaptation to an imperfect world. - -```scala -val js = """["true","false"] -val sj1 = ScalaJack() -val sj2 = ScalaJack().allowPermissivePrimitives() - -sj1.read[List[Boolean]](js) // explodes with error: expected Boolean not String - -sj2.read[List[Boolean]](js) // reads List(true,false) -``` - - -#### enumsAsInts() -You can think of an Enumeration as either a label (String) or an integer (position within the list of allowed values). When parsing, ScalaJack will try and accept either a String or an Int as a valid value for an Enumeration-typed field. By default, when rendering, ScalaJack renders the String label. If you want to render the Int, the set this configuration. - -```scala -object Size extends Enumeration { -val Small, Medium, Large = Value -} -case class SampleEnum(e1: Size.Value, e2: Size.Value) -import Size._ - -val sj1 = ScalaJack() -val sj2 = ScalaJack().enumsAsInts() -val inst = SampleEnum(Large, null) -sj1.render(inst) // renders {"e1":"Large","e2":null} -sj2.render(inst) // renders {"e1":2,"e2":null} -``` - -#### parseOrElse(poe: (Type, Type)*) -Configures a default object to be returned if any object of the particular given type can't be parsed successfully. This is a mapping, so multiple specifications are possible: (match_type, default_object_type) - -```scala -val sj = ScalaJack() - .parseOrElse( - (typeOf[Address], typeof[DefaultAddress]), - (typeOf[Command], typeOf[DoNothingCmd]) - ) -``` -So in this example, whenever ScalaJack fails to parse an Address-typed value it will substitute a DefaultAddress object in its place. - - -#### withAdapters(ta: TypeAdapterFactory*) -Register a list of custom TypeAdapters with ScalaJack. This is to allow you to offer custom serialization handling of your own types. - -[See an example.](custom.md) - -#### withDefaultHint(hint: String) -This simply changes the globally default type hint key strings (ScalaJack default hint key is "_hint"). This can be mixed with withHints(). Any type not specified in withHints will pick up your re-set default hint. - -#### withHints(h: (Type, String)*) -Specify per-type hints to override the global default type hint of "_hint". -__Tip:__ To get the type of a class, for example to use with this function, you can use: -```scala -typeOf[MyClass] -``` - -[See an example.](typeHint.md) - -#### withHintModifiers(hm: (Type, HintModifier)*) -Where withHints modifies the key String for a type hint, withHintModifiers modifies the hint's value. Particiularly handy for 3rd party JSON where you don't own/control the values and you want a function to line it up with your internal representation. - -[See an example.](typeHint.md) - -#### withTypeModifier(tm: HintModifier) -This is used to apply a hint value modifier (function) the string identifying the externalized type hint (type member) of a class. This is global, I'm afraid, so any modifier you specify here will be applied to all type members serialized with this instance of ScalaJack. - -[See an example.](externalTypes.md) - diff --git a/doc/custom.md b/doc/custom.md deleted file mode 100644 index 9e4e6841..00000000 --- a/doc/custom.md +++ /dev/null @@ -1,99 +0,0 @@ -## Custom Type Adapters - -ScalaJack does a great job of reading and rendering stock data types, but sometimes you just need something custom. Let's use an example. Imagine you have a phone number of type String that you want formatted like a US phone number (XXX-XXX-XXXX) but stored as a simple String (no dashes). - -To do this ScalaJack allows you to create a custom type adapter and link it into its own chain of adapters. Let's walk through the process step-by-step. - -### Step 1: Create a Type - -```scala -object MyTypes { - type Phone = String -} -import MyTypes._ -``` - -In this case we create a Phone type to differentiate Phone, which is a String, from any other String value. - -### Step 2: Create the TypeAdapter -There are 3 essential functional pieces to a ScalaJack TypeAdapter. -1. Something that matches the type we want -2. Something to read input of that type -3. Something to output an object of that type - -Let's look at a straightforward example then unpack some nuances. - -```scala -object PhoneAdapter extends TypeAdapter.===[Phone] with Stringish { - def read(parser: Parser): Phone = - parser.expectString() match { - case null => null - case s: String => s.replaceAll("-", "") - } - - def write[WIRE](t: Phone, writer: Writer[WIRE], out: Builder[WIRE, WIRE]): Unit = t match { - case null => writer.writeNull(out) - case _ => writer.writeString("%s-%s-%s".format(t.substring(0, 3), t.substring(3, 6), t.substring(6)), out) - } -} -``` -Here you'll see all three essential pieces. The type matching is accomplished with ```extends TypeAdapter.===[Phone]```. The read and write functions speak for themselves. Note the use of WIRE here (not, for example, JSON). This is because ScalaJack is a general-purpose serializer so we don't presume JSON. Each "flavor" of ScalaJack (JSON being one flavor) implements the Reader and Writer traits, which define a nice set of primitive operations we can use for our TypeAdapters. - -In this example we see that read() strips out all the dashes from the phone numbers, while write() re-inserts them in a US format. - -Phone numbers are Strings, so they're nullable, so we handle null for read/write as well. (Most of your TypeAdapters will also be nullable unless you are wrapping a non-nullable type like Int.) - -Also note the "Stringish" mixin in our TypeAdapter. This tells ScalaJack that your type is String-encoded. This is an oddment of Map key handling for WIRE formats like JSON. Map/Object keys in JSON must be Strings, yet Scala imposes no such limitation. Therefore ScalaJack must wrap some primitive types in String quotes when used as JSON map keys. String-encoded (Stringish) types, like Phone, require no such special handling so we notate that in the TypeAdapter to avoid double quoting. For example: - -```scala -sj.render(Map(true -> true, false -> false)) -// renders string-wrapped map keys: {"true":true,"false":false} -``` - -### Step 3: Create the PhoneAdapter - -```scala -// Override just Phone -object PhoneAdapter extends TypeAdapter.===[Phone] { - override val irTransceiver: IRTransceiver[Phone] = new PhoneIRTransceiver() -} -``` -You can see we specify our IRTransceiver. One thing that may not be clear is the Typeadapter.===[Phone] notation. This matches exactly on Phone type, so ScalaJack doesn't confuse other String values with Phone and try to serialize/deserialize them with your custom code. - -If what you want is to treat Phone and all subclasses as Phone (with your custom code), then extend TypeAdapter.=:=[Phone] instead. If we did that in this case, every String would be treated as a Phone, with likely dissastrous results. - - -### Step 3: Wire your PhoneAdapter into ScalaJack's list of type adapters - -To use your new type adapter, hook it into ScalaJack: - -```scala -val sj = ScalaJack().withAdapters(PhoneAdapter) -``` -Now anything you parse with a Phone in it will receive your specified special handling via your custom adapter. - -**TIP:** withAdapters() is varargs, so you can pass a chain of adapter: ScalaJack().withAdapters(a,b,c,...) - -### Nuance: Matching Types -When creating custom TypeAdapters in ScalaJack you have a number of options when deciding how to match Types. ScalaJack basically works like this: it looks for a give type (reading or writing) by iterating through a list of TypeAdapterFactories (with Type matchers) until one matches. *How* types are matched is the question. - -In the Phone example above we used a simple way to create a TypeAdapterFactory using: -```scala -object PhoneAdapter extends TypeAdapter.===[Phone] with Stringish { -.. -} -``` -ScalaJack allows 3 kinds of type comparisons when matching using this method: - -```scala -type Phone = String -trait Foo -class Bar() extends Foo -``` -|Comparator|Meaning -|-------|-------| -|A === B|Type A exactly matches another. Phone === String is false -|A =:= B|Type A can be implicitly converted to type B. Phone =:= String is true -|A <:< B|Type A is a subtype of type B. Bar <:< Foo is true - -These 3 comparators will likely provide most of your needed type matching, but if you have a really special case you can write your own custom TypeAdapterFactory. The TypeAdapterFactory class provides a number of matchings by implementing typeAdapterOf[T]. The CaseClassTypeAdapterFactory class in the ScalaJack code is a good example of usage. diff --git a/doc/delimited.md b/doc/delimited.md deleted file mode 100644 index 73bd661e..00000000 --- a/doc/delimited.md +++ /dev/null @@ -1,44 +0,0 @@ - -## Delimited Format (e.g. CSV) - -ScalaJack offers support for delimited serialization (e.g. CSV). It must be said clearly that there are structural limitations to the support available because delimited format can't support advanced nested structures. Here are some of the features and limitations imposed by ScalaJack on delimited-format serialization: - -* Each input string (presumably a line of a larger input file) shall be parsed to one given object -* Map structures are not supported (how would you do key-value in a flat format?) -* Lists are implemented by a nested field, escaped in quotes. This is handy for very simple lists, but can get messy quickly. Use with caution! -* Scala Either is supported but delimited input is very likely to be read as a String, which can mess up Left/Right of an Either if both are String-like -* Enumerations are supported -* Traits are **not** supported (where would you put the type hint in CSV format?) -* You must enclose a field with double-qutoes if it contains double quotes, the delimiter character ('comma), or line breaks. -* Don't try reading Any types! In delimited input, everything will be interpreted as a String, so if that's not what you want you'll be disappointed. -* Double quotes within a string/field must be escaped using double-double quotes, "" (not the more JSON style \") -```scala -val sj = ScalaJack(DelimitedFlavor) // CSV default -// val sj = ScalaJack(DelimitedFlavor('|')) <-- to set a non-comma delimiter character -val inst = StringHolder("Hey, you", "This \"\"life\"\"", "And now, \"\"Mike\"\" will sing.") -val csv = sj.render(inst) -// renders: "Hey, you","This "life"","And now, "Mike" will sing." -``` - -### Lists -Simple list support is provided. -```scala -val sj = ScalaJack(DelimitedFlavor) -case class Foo(a:Int, b:List[String]) -val f = Foo(1,List("a","b","c")) -sj.render(f) -// renders 1,"a,b,c" -``` -Note that while clever, list representation can get ugly fast. Consider the case where the strings in the list need to themselves be escaped because they contain a delimiter character. -```scala -val f = Foo(1,List("a","b,x","c")) -sj.render(f) -// renders 1,"a,""b,x"",c" -``` -Not awful, but you can see where the double-quotes could multiply very quickly for any more sophisticated structure. - -### Handling None and Null -Empty values in delimited input are read in as null in most cases, with the exception of an Optional field, where a null is read as None. If a case class field is read as empty, and the field has a defined default value, the default value is returned. - -### Takeaway -Generally it's best to treat delimited format as "fragile", meaning don't get too clever or cute with it. Use simple, flat or nearly-flat objects and you'll be fine. \ No newline at end of file diff --git a/doc/externalTypes.md b/doc/externalTypes.md deleted file mode 100644 index 0396e3b0..00000000 --- a/doc/externalTypes.md +++ /dev/null @@ -1,69 +0,0 @@ -## Externalized Types - -When parsing some JSON into a Scala trait we use a type hint to know what the concrete case class should be. Normally this type hint is self-contained within the JSON for the class: -```json - {"_hint":"com.me.Customer", "name":"Fred", "acct":"abc123"} -``` -What if the hint needs to be externalized, i.e. the type is specified *outside* the JSON block for the class? Perhaps we have a message router that knows how to parse a Message class containing a Payload (trait). Perhaps the router doesn't need to read the payloads but it does need to know their type to decide where to send the message. It would be very convenient to "promote" the payload type hint to the outer Message wrapper. - -Maybe we need something like this: -```scala - package com.me - - trait Command - case class FieldCommand(ping:String) extends Command - - case class Message[T <: Command](id:String, payload:T) -``` -Let's further assume the JSON we want would look like this: -```json - {"payloadKind":"com.me.FieldCommand", "id":"abc123", "payload":{"ping":"pong"}} -``` -Notice that the type hint is outside the object it describes (payload). How can we accomplish this? How can we move the type hint outside the described object? - -ScalaJack uses a type member in your class to specify a type hint of a contained object. Like this: -```scala - case class Message[T <: Command](id:String, payload:T) { - type payloadKind = T - } -``` -How would we use this feature? -```scala - val inst: Message[Command] = Message("abc123", FieldCommand("pong")) - sj.render(inst) - // {"payloadKind":"com.me.FieldCommand", "id":"abc123", "payload":{"ping":"pong"}} -``` -This is the same render usage as normal, however notice the special treatment of the type hint. Not only is it outside in the "outer" Message wrapper, but it is called payloadKind, not "_hint". For external type hints (Scala type members), ScalaJack uses the name of the type member field as the hint. - -#### Reading when you have all the class/jar files -Here's an example of reading using a match statement to switch on the kinds of payload: -```scala -val inbound = sj.read[Message[Command]](js) -inbound.payload match { - case fc:FieldCommand => // stuff here - case ac:AirCommand => // stuff here - case _ => // catch-all -} -``` -In this case ScalaJack actually materialized the payloads, meaning it needed the class files for those objects. What if we don't have those jar files? - -#### Reading when you *don't* have all the class/jar files -```scala -case class MsgWrapper(kind: String, id: String) extends SJCapture -val inbound = sj.read[MsgWrapper](js) -``` -Here we had to create a simplified Message wrapper, that doesn't have a payload field (because we don't have the class files to parse it). In this example inbound.kind now holds the class name for the payload's type, even though we don't have a corresponding class/jar file for this object. SJCapture holds all the "extra" payload information so we can safely re-render the whole original object if needed. - -__Bottom Line:__ If you specify a type member in your case class, ScalaJack will externalize that type hint, packaging it in the outer or wrapping class. Without a type member, ScalaJack will package the type hint normally inside the serialized class. - -### Custom Type Modifiers -Just like for type hints, we may receive 3rd party JSON where having the type value be a fully-qualified Scala class name may not be possible. We have a limited ability to use the same modifiers we use for type hints. -```scala -val sjm = ScalaJack().withTypeModifier(ClassNameHintModifier((hint: String) => "com.me." + hint, (cname: String) => cname.split('.').last)) -val inst: Message[Command] = Message("abc123", FieldCommand("pong")) -val js = sjm.render[Message[Command]](inst) -// {"payloadKind":"FieldCommand", "id":"abc123", "payload":{"ping":"pong"}} -``` -Note the class path has been modified and we now only see the trailing class name. The other out-of-the-box modifier, StringMatchHintModifier, works fine too, in case you need to completely divorce the value in the JSON from any notion of the class name. - -**WARNING:** There is a pretty big limitation on type member modifiers: You can only specify one type modifier globally, i.e. at this time they are not class-specific. That means the type modifier you specifiy will apply to *all* your type member values! Perhaps a future version of ScalaJack will support multiple modifiers and make them class/member specific. diff --git a/doc/filter.md b/doc/filter.md deleted file mode 100644 index b707528a..00000000 --- a/doc/filter.md +++ /dev/null @@ -1,30 +0,0 @@ -## Filter - -Earlier in this documentation we saw a number of ways to process input through ScalaJack, including ways using SJCapture and externalized type hints to build filtering capabilities. Wouldn't it be nice if there was a cleaner, Scala-like way of filtering on a type? - -```scala -trait Comm -case class Event(happening: Int) extends Comm -trait Command extends Comm { val goDo: String } -case class SimpleCommand(goDo: String, public: Boolean) extends Command -case class CommMessage[T <: Comm](id: Int, payload: T) { - type kind = T - } - -val js = "123" -val isMap = sj.filter[Map[String, Int]]() -val isInt = sj.filter[Int]() -val isCmd = sj.filter[CommMessage[Command]]("kind") -val isEvt = sj.filter[CommMessage[Event]]("kind") - -sj.parse(js) match { - case isMap(x) => println(x) - case isInt(x) => println("Int: " + x) - case isCmd(x) => println("Cmd: " + x) - case isEvt(x) => println("Evt: " + x) - case _ => - } -``` -sj.filter() returns a Filter object that can be used as a type-safe Scala extractor. - -Nice! diff --git a/doc/map.md b/doc/map.md deleted file mode 100644 index 0ecb0557..00000000 --- a/doc/map.md +++ /dev/null @@ -1,86 +0,0 @@ -## Converters - -As of version 6.2.0, ScalaJack includes a Converters package to add some syntax sugar making it easier to move between wire formats. - -### Case 1: Simple conversion between two wire formats -```scala -import co.blocke.scalajack.Converters._ -case class Person(name: String, age: Int) - - val js = """{"name":"Greg","age":53}""" - println(js.jsonToYaml) -``` - -Note: For Delimited converers (delimitedToJson, delimitedToJson4s, delimitedToYaml, jsonToDelimited, json4sToDelimted, yamlToDelimited), you must specify a type parameter. -This is because Delimited format is so representation-poor, it can't represent a Map required for the conversion. An example: - -```scala -import co.blocke.scalajack.Converters._ -case class Person(name: String, age: Int) - - val js = """{"name":"Greg","age":53}""" - println(js.jsonToDelimited[Person]) - // Greg,53 -``` - -### Case 2: Map serialized object across the same wire format -```scala -import co.blocke.scalajack.Converters._ -case class Person(name: String, age: Int) - - val js = """{"name":"Greg","age":53}""" - println(js.mapJson[Person](person => person.copy(age=35))) - // {"name":"Greg","age":35} -``` - -### Case 3: Convert between wire formats while modifying serialized object -```scala -import co.blocke.scalajack.Converters._ -case class Person(name: String, age: Int) - - val sjYaml = ScalaJack(YamlFlavor()) - val js = """{"name":"Greg","age":53}""" - println(js.mapJsonTo[Person,YAML](sjYaml)(person => person.copy(age=35))) - // name: Greg - // age: 35 -``` - -### *Bonus:* to/from convenience implicits - -In addition to the normal ScalaJack read/render functions, the Converters package provides a set of implicit convenience functions -to perform the same functionality, for those who perfer this style. - -```scala -import co.blocke.scalajack.Converters._ - - trait Human - case class Person(name: String, age: Int) extends Human - - val js = """{"name":"Greg","age":53}""" - - val person = js.fromJson[Person] - val yamlWithHint = person.toYaml[Human] // trait generates type hint - val yamlWithoutHint = person.toYaml[Person] -``` - -For these convenience functions you always need to supply the type of the serialized object. - -#### Configuration - -The Converters can be configured just like the normal ScalaJack flavors: -```scala -import co.blocke.scalajack.Converters._ - - trait Human - case class Person(name: String, age: Int) extends Human - - val js = """{"name":"Greg","age":53}""" - - // Chain together configuration directives like any other flavor - val config = Configuration().withDefaultHint("kind").enumsAsInt() - withConfig(config) - - // Now any subsequent Converters activity will use your provided configuration -``` - - diff --git a/doc/neotype.md b/doc/neotype.md new file mode 100644 index 00000000..53e32b42 --- /dev/null +++ b/doc/neotype.md @@ -0,0 +1,69 @@ + +## NeoType Support + +ScalaJack now supports Kit Langton's [excellent neotype library](https://github.com/kitlangton/neotype). Neotype allows you to define validated types that ensure the integrity of the values they contain. + +By way of example let's define 3 String-related neotypes: + +**File 1.scala** +```scala +//----- A String that must not be empty +type NonEmptyString = NonEmptyString.Type +given NonEmptyString: Newtype[String] with +override inline def validate(input: String): Boolean = +input.nonEmpty + +//----- A String that must be empty +type EmptyString = EmptyString.Type +given EmptyString: Newtype[String] with +override inline def validate(input: String): Boolean = +input.isEmpty + +//----- A List of String that must be non-empty and the first list element must be "x" +type XList = XList.Type +given XList: Newtype[List[String]] with +override inline def validate(input: List[String]): Boolean = +input.nonEmpty && input(0) == "x" + +//----- A class to exercise these neotypes +case class Validated(name: NonEmptyString, xspot: XList, nada: EmptyString) +``` + +For this example, we're going to focus on fromJson(). The toJson() is presumed to always work because the neotype library won't let you create a Validated() object having invalid neotype values. So the real test here is to see if ScalaJack will correctly detect JSON that violates the defined field validations. + +**File 2.scala** +```scala +given sjValidated: ScalaJack[Validated] = sjCodecOf[Validated] + +val sj_ok = """{"name":"Mike","xspot":["x","other"],"nada":""}""" +val sj_brokenName = """{"name":"","xspot":["x","other"],"nada":""}""" +val sj_brokenNada = """{"name":"Mike","xspot":["x","other"],"nada":"boom"}""" +val sj_xspot = """{"name":"Mike","xspot":["y","other"],"nada":"boom"}""" + +sjValidated.fromJson(sj_ok) +// materializes Validated("Mike",List("x", "other"),"") + +try( sjValidated.fromJson(sj_brokenName) ) +catch { + case jpe: JsonParseError => println(jpe.show) +} +// NeoType validation for NonEmptyString failed at position [9] +// {"name":"","xspot:["x","other"],"nada":""} +// ---------^ + +try( sjValidated.fromJson(sj_brokenNada) ) +catch { + case jpe: JsonParseError => println(jpe.show) +} +// NeoType validation for EmptyString failed at position [49] +// {"name":"Mike","xspot":["x","other"],"nada":"boom"} +// -------------------------------------------------^ + +try( sjValidated.fromJson(sj_xspot) ) +catch { + case jpe: JsonParseError => println(jpe.show) +} +// NeoType validation for XList failed at position [42] +// {"name":"Mike","xspot":["nintendo","other"],"nada":""} +// ------------------------------------------^ +``` \ No newline at end of file diff --git a/doc/nullAndNone.md b/doc/nullAndNone.md index 7510cc39..f3fc0f7d 100644 --- a/doc/nullAndNone.md +++ b/doc/nullAndNone.md @@ -1,24 +1,55 @@ + ## Null and None Handling -Representing nulls and None is problematic in JSON because while the spec provides for null it offers nothing to represent None. That forces us to sometimes make inconsistent, contextualized assumptions about how best to represent these concepts. Sadly, there is no perfect "right" answer here, so these are the compromises ScalaJack made. +Representing nulls and None is problematic in JSON because while the spec provides for null it offers nothing to represent None. That forces us to sometimes make inconsistent, contextualized assumptions about how best to represent these concepts. Sadly, there is no perfect "right" answer, so here we document the imperfect compromises ScalaJack made. -Note that in some cases ScalaJack's Null/None handling break the general promise that a read/render round-trip of a serialized object will result in the original object! +Note that in some cases ScalaJack's Null/None handling break the general promise that a read/render round-trip of a serialized object will result in the original object. This is because depending on the handling of Null/None, output may be skipped. -|Usage |Example|JSON Representation +|Usage |Example|JSON Representation |-------|-------|------------- -|Class member|```MyClass(None,3)```|{"age":3} *(eliminate None field)* +|Class member|```MyClass(None,3)```|{"age":3} *(eliminate None field)* |List|```List(Some(1),None,Some(3))```|[1,3] *(eliminate None value)* |Map value|```Map("a"->Some(1),"b"->None)```|{"a":1} *(eliminate None value)* |Map key|```Map(Some(1)->"a",None->"b",Some(2)->"c",None->"d")```|{"1":"a","2":"c"} *(eliminate None values)* |Tuple member|```(5,None,"hey")```|[5,null,"hey"] *(None converted to null)* -Tuples represent None as null as a compromise. We can't drop the value from the tuple because it has a fixed size that must be respected in the JSON. JSON can't handle an empty field, i.e. "[1,,3]", so the only choice left is to use null. - +Tuples represent None as null as a compromise. We can't drop the value from the tuple because it has a fixed size that must be respected in the JSON. JSON can't handle an empty field, i.e. "[1,\,3]", so the only choice left is to use null. + **Special Note** -If this isn't already confusing, it gets slightly more terrible. Parsing JSON null back into Scala is complicated by the type of field. -In a tuple, if the member's type is Option[], a null parses as None, otherwise it parses as null. +If this isn't already confusing, it gets slightly more terrible. Parsing JSON null back into Scala is complicated by the type of field. In a tuple, if the member's type is Option[], a null parses as None, otherwise it parses as null. Just be careful! You may not get exactly the object you expect. Clear as mud? + +**Option None and Null** +By default ScalaJack will omit None values in many contexts (see above chart). You can change that behavior and force None values to be converted to null by using a config policy: +```scala +given sjFoo: ScalaJack[Foo] = sjCodecOf[Foo](SJConfig().withNoneAsNull()) +``` + + +**Try Failure Value and Null** + +By default, ScalaJack will output null for a Try value of Failure. Alternative handling for Failure is possible via policy: + +Try Failure policies are: +* TryPolicy.AS_NULL -- output null upon Failure value (default behavior) +* TryPolicy.ERR_MSG_STRING -- put error string into JSON (**WARNING**: This may corrupt the data type, but is useful for debugging during development) +* TryPolicy.THROW_EXCEPTION -- throw an exception upon Failure value + +To set a different policy: +```scala +given sjFoo: ScalaJack[Foo] = sjCodecOf[Foo](SJConfig().withTryFailureHandling(TryPolicy.THROW_EXCEPTION)) +``` + +**Either Left Value and Null** +By default, Scalajack will output the actual value of an Either Left value. Alternative handling for Left is possible via policy: -Just be careful! You may not get exactly the object you expect. +Either Left policies are: +* EitherLeftPolicy.AS_VALUE -- output whatever the wrapped Left value is (default behavior) +* EitherLeftPolicy.AS_NULL - output null upon Left value +* EitherLeftPolicy.ERR_MSG_STRING -- put error string into JSON (**WARNING**: This may corrupt the data type, but is useful for debugging during development) +* EitherLeftPolicy.THROW_EXCEPTION -- throw an exception upon Failure value -Clear as mud? +To set a different policy: +```scala +given sjFoo: ScalaJack[Foo] = sjCodecOf[Foo](SJConfig().withEitherLeftHandling(EitherLeftPolicy.AS_NULL)) +``` diff --git a/doc/parseOrElse.md b/doc/parseOrElse.md deleted file mode 100644 index d53ae003..00000000 --- a/doc/parseOrElse.md +++ /dev/null @@ -1,63 +0,0 @@ - -## ParseOrElse and Cascading Fallback Parsing - -Sometimes you're not 100% sure if you can actually parse a given object or not. For example, let's imagine code you wrote is part of a large, distributed message-passing system. Your program listens for messages (each corresponding to a case class). Let's further imagine that all messages in this ecosystem derive from trait ActionMessage and that your code is designed to listen for SortAction messages. The problem is... you may receive other kinds of messages--messages you don't care about or know what to do with. You may not even have the jar file containing code to deserialize these other messages. - -How will you even deserialize an incoming message to determine if it's one of yours? That's a problem. - -This is the scenario parseOrElse() was designed for: attempt to parse JSON (presumably into a trait). If successful very well, but if you can't parse (perhaps because you don't have a class file for the given type) then return a given default object. - -```scala -package com.mycompany - -trait ActionMessage -case class Wrapped(id:Long, src:String, msg:ActionMessage) -case class SortAction(isDescending: Boolean) extends ActionMessage -case class DefaultAction() extends ActionMessage // some do-nothing ActionMessage we own - -val sj = ScalaJack().parseOrElse(typeOf[ActionMessage] -> typeOf[DefaultAction]) - -val js = """{"id":123,"src":"main","msg":{"_hint":"com.mycompany.SpecialAction","contact":"fred"}}""" -val myAction = sj.read[Wrapped](js) -``` - -Presuming we have no code for class SpecialAction, when this Wrapped object is read ScalaJack will consult the ParseOrElse lookup table and see that for an unknown ActionMessage it should return a DefaultAction object. - -parseOrElse takes a mapping Type->Type, where the first argument is the type of the trait to be parsed and the second argument is the type of the default object if the trait's type hint is unknown. - -### Cascading Fallback - -parseOrElse() can be cascaded as in this example: - -```scala -package com.mycompany - -trait ActionMessage { val priority: Int } -case class Wrapped(id: Long, src: String, msg: ActionMessage) - -case class SortAction(priority: Int, isDescending: Boolean) extends ActionMessage -case class DefaultAction(priority: Int) extends ActionMessage with SJCapture // some do-nothing ActionMessage we own -case class UnknownMessage() extends ActionMessage with SJCapture { - val priority = -1 -} - -val sj = ScalaJack() - .parseOrElse( - typeOf[ActionMessage] -> typeOf[DefaultAction], - typeOf[DefaultAction] -> typeOf[UnknownMessage] - ) - -val js = - """{"id":123,"src":"main","msg":{"_hint":"com.mycompany.SpecialAction","priority":2,"contact":"fred"}}""" -val myAction = sj.read[Wrapped](js) // Produces DefaultAction - -val js2 = // (missing required priority field for ActionMessage) - """{"id":123,"src":"main","msg":{"_hint":"com.mycompany.SpecialAction","contact":"fred"}}""" -val myAction2 = sj.read[Wrapped](js2) // Produces UnknownMessage -``` - -In this case, as before, we attempt to deserialize a class, SpecialAction, for which we have no jar file. As before we attempt to fall back to deserializing a DefaultAction, but in this case that fails too, because the required 'priority' field is missing from the second input (js2). In this case we fall back again and create an UnknownMessage. - -As an added flourish we've mixed in SJCapture so that all the data sent in the JSON is at least captured, even if we don't known what to do with it. That's purely optional, of course, if you need that data. - -The intention of parseOrElse() along with the cascading fallback pattern is that you shouldn't need a nest of Exception handlers. You should be able to always have your parse produce something rational and useful. \ No newline at end of file diff --git a/doc/tryAndCapture.md b/doc/tryAndCapture.md deleted file mode 100644 index 44fe4701..00000000 --- a/doc/tryAndCapture.md +++ /dev/null @@ -1,74 +0,0 @@ -## Try and Capture - -There are times when you just don't care. Try and Capture support is designed to let you control how much you care about the input you parse. - -### Case 1: I care about some fields and not at all about the rest! -In this case, simply don't represent JSON fields you never care about in your class. Parsing will harmlessly ignore them. This also has the benefit of being very flexible if the source JSON changes--as long as they don't change the fields you care about nothing in your system breaks. -```scala -val js = """{"one":"Thing","two":"Another thing","three":"Last thing"}""" -case class ThingsICareAbout(one:String, three:String) // field two is ignored -``` -Note that fields ignored on read, because they're not present in your class, will not be output upon render. (If you don't care about certain fields, but don't want to lose them on render ScalaJack has a mechanism to preserve this knowledge we'll see shortly.) - -### Case 2: I have an optional field that I care about if it exists. -In this case, represent optionally-present fields as Option type in your class. If they're present in the JSON, they'll be materialized as Some of something, and None if not. -```scala -val js = """{"one":"Thing","three":"Last thing"}""" -case class ThingsICareAbout(one:String, two:Option[String], three:String) -// materializes: ThingsICareAbout("Thing",None,"LastThing") -``` -In this case Option fields who's value is None will not be rendered, but if their value is Some(something) then output will be rendered. - -### Case 3: There's a field that may or may not parse but I want to keep it -This case is for when you get unreliable JSON, likely from a 3rd party. In that JSON is a field you expect and require to be present (i.e. it's not optional) but you're not 100% sure of its content or proper formatting. If it can parse you want the value, but if not... you want to keep the original JSON intact as-given, because you have a requirement to re-generate the original message format. - -An example of this case is if your code is some kind of proxy that receives JSON, makes certain changes and re-emits a modified version of the original JSON. - -In this instance we support a Try: - -```scala -val js = """{"one":"Thing","two":"another thing","three":"Last thing"}""" - -case class ThingsICareAbout(one:String, two:Try[String], three:String) // <-- Note the Try! - -// materializes: ThingsICareAbout("Thing",Success("another thing"),"LastThing") - -val js2 = """{"one":"Thing","two":true,"three":"Last thing"}""" -val myObj = sj.read[ThingsICareAbout](js2) - -/* -Oops! Materializes: -ThingsICareAbout(Thing,Failure(co.blocke.scalajack.typeadapter.ValueBackedException: [$.two]: Expected String here but found Boolean -{"one":"Thing","two":true,"three":"Last thing"} -------------------------^),Last thing) -*/ - -// -// but... -val rerendered = sj.render(myObj) -// emits: {"one":"Thing","two":true,"three":"Last thing"} -``` - -Wow! Did you catch that? Parsing a Try captured, but didn't throw, the ValueBackedException (we fed a boolean value into a String). Behind the scenes, ScalaJack also captured the original JSON, so that even though we couldn't parse it into our class, when the object was rendered, the original JSON was put back into place! - -The assumption here is that we're getting 3rd party JSON. Maybe types change unexpectedly and we can't read them but we don't want to screw things up for others in the ecosystem that perhaps do expect the given type. - -### Case 4: I don't care about any "extra" fields in the JSON, but please don't lose them! -This case is along the lines of Case 3 except that here we don't care about any fields we're not going to use. For the same reasons (maybe our code is a proxy or pass-through) we may need to re-render all those "don't-care" fields in their original format. - -We accomplish this behavior like this: - -```scala -case class ThingsICareAbout(one:String) extends SJCapture // <-- Note the extends SJCapture - -val js = """{"one":"thing","id":1234,"isOK":true}""" -val myObj = sj.read[ThingsICareAbout](js) -// myObj = ThingsICareAbout("thing") - -println(sj.render(myObj)) -// prints: {"one":"thing","id":1234,"isOK":true} -``` - -Because your class extends SJCapture, all the "extra" incoming JSON fields are quietly captured and stored until the object is rendered. - -This is ideal for pass-through applications where the JSON formats are unstable or you are only concerned about a subset of fields as you pass through the object. Your code won't break if changes happen to any of the extra, captured fields. diff --git a/doc/typeHint.md b/doc/typeHint.md index 71c6a7c3..31d0cfce 100644 --- a/doc/typeHint.md +++ b/doc/typeHint.md @@ -1,91 +1,58 @@ -## Trait Hint Type Customization -By default, ScalaJack uses a simple type hint strategy to record concrete types for traits: it inserts a type hint into the rendered JSON object with key "_hint" and value equal to the fully-qualified class name. +## Trait Hint Type Customization -This is a pretty good and straightforward way to handle type hints, but there are occasions when you may want something else. For example when you're dealing with 3rd party JSON. In those cases you may not control, or wish to share, the full class name of your objects. +By default, ScalaJack uses a simple type hint strategy to record concrete types for sealed traits: it inserts a type hint into the rendered JSON object with key "_hint" and value equal to the class simple class name. -Here are 3 ways you can customize trait type hint handling in ScalaJack. +This is a pretty good and straightforward way to handle type hints, but there are occasions when you may want something else. Here are several ways you can customize trait type hint handling in ScalaJack. ### Change the Default Hint Label + You can change the global default hint label from "_hint" to whatever else you want using a ScalaJack configuration. +**File1.scala** ```scala -package com.me - -trait Foo{ val bar:Int } -case class Blather(bar:Int) extends Foo - -val sj = ScalaJack().withDefaultHint("kind") //<<-- Note the config here - -println(sj.render[Foo](Blather(2))) -// {"kind":"com.me.Blather","bar":2} //<<-- Type hint is now "kind" +sealed trait Foo{ val bar:Int } +case class Blather(bar:Int) extends Foo ``` -You'll see the normal _hint is replaced by "kind". - -### Class-Specific Hint Label Customization -This is a more granular control of the first case, which allows you to change the type hint on a class-by-class basis (you can still override the default case for classes not mapped using this method): +**File2.scala** ```scala -package com.me -import scala.reflect.runtime.universe.typeOf - -trait Foo{ val a:Int } -trait Bar{ val b:Int; val c:Foo } -case class One(a:Int) extends Foo -case class Two(b:Int, c:Foo) extends Bar - -val sj = ScalaJack().withHints((typeOf[Foo] -> "mark")).withDefaultHint("kind") - -val inst:Bar = Two(3, One(2)) -println(sj.render(inst)) -// {"kind":"com.me.Two","b":3,"c":{"mark":"com.me.One","a":2}} +given sjFoo: ScalaJack[Foo] = sjCodecOf[Foo](SJConfig().withTypeHintLabel("kind")) +println(sjFoo.toJson(Blather(2))) +// {"kind":"Blather","bar":2} //<<-- Type hint is now "kind" and not "_hint" ``` -Here we've shown a type-specific change to the type hint and a general override of the default type hint. - -(Hint: typeOf[SomeClass] is an easy way to get the type of something. This works for parameterized types too, e.g. typeOf[Vehicle[Person]].) ### Type Hint Value Customization -We've seen how to change the type hint label, but what if you want to modify the behavior of using the fully-qualified class name to use another value instead? This is ideal when you don't want to expose the class name in JSON or 3rd party consumers of your output expect something else. ScalaJack has the concept of a HintValueModifier for this purpose. -ScalaJack suppies two pre-built HintValueModifiers out of-the-box: ClassNameHintModifier and StringMatchHintModifier. (You are certainly not limited to just these two--you can write your own HintValueModifiers to do whatever you want, but these cover some common use cases.) - -The ClassNameHintModifier re-writes the class name itself. You pass in 2 functions: the first accepts a simple hint string and your function produces a fully-qualified class name, and the second accepts the fully-qualified class name and produces the simple hint string. +We've seen how to change the type hint label, but what if you want something different than the class' simple name as the value? There are two possible use cases for wanting to change the hint value. First, having the class name in the JSON message might be more information than you'd like to share. ScalaJack provides a way to encode the class name into a value that's not so obvious. This isn't security, by any means, but it is obfuscation and should at least deter curious idiots. (If what you need is true security, you should be encrypting the entire JSON message anyway.). +**File1.scala** ```scala -val prependHintMod = model.ClassNameHintModifier((hint: String) => "com.me." + hint, (cname: String) => cname.split('.').last) -val sj = ScalaJack().withHintModifiers((typeOf[Foo], prependHintMod)) -val inst:Bar = Two(3, One(2)) -println(sj.render(inst)) -// {"_hint":"com.me.Two","b":3,"c":{"_hint":"One","a":2}} +sealed trait Foo{ val bar:Int } +case class Blather(bar:Int) extends Foo ``` -We've first used ClassNameHintModifier to specify 2 functions: value->classname, classname->value. In our case it adds/strips the packages from the classname leaving just the last part of the class path, or "One" in this case. Reading re-inserts the path: "One" -> "com.me.One". Then we've associated that modifier to a particular type, Foo in our case. - -The other provided HintValueModifier is StringMatchHintModifier: - +**File2.scala** ```scala -val strMatchHintMod = model.StringMatchHintModifier(Map("Yes" -> typeOf[One])) -val sj = ScalaJack().withHintModifiers((typeOf[Foo], strMatchHintMod)) -val inst:Bar = Two(3, One(2)) -println(sj.render(inst)) -// {"_hint":"com.me.Two","b":3,"c":{"_hint":"Yes","a":2}} +given sjFoo: ScalaJack[Foo] = sjCodecOf[Foo]( + SJConfig().withTypeHintLabel("ref").withTypeHintPolicy(TypeHintPolicy.SCRAMBLE_CLASSNAME)) +println(sjFoo.toJson(Blather(2))) +// {"ref":"86999-847-46A","bar":2} //<<-- Type hint is now obfuscated--looks like some kind of id ``` -In this example we mapped the type of the concrete class to some string value (vs modifying the text of the class name itself). Note a key point here--The StringMatchHindModifier is a map of String->*concrete class type*. - -### Pulling it Together -Using these hint modification methods together is where the power becomes apparent. A great case for this is when you're parsing 3rd party JSON you don't control. If there's a "variant" object that could take one of several concrete forms there's bound to be some field that behaves as a discriminator. That field will likely not be labeled "_hint", and the value will not be a class name. +The other use case for modifying type hint values is to support 3rd party JSON that has its own discriminator values that may be different from your class names. To support that, ScalaJack uses annotations on the class. -In that case a StringMatchHintModifier is a great choice, along with a .withHints mapping so you can wrangle what you're given into what you need to read the correct concrete class in your code. +**File1.scala** +```scala +sealed trait Foo{ val bar:Int } +@TypeHint(hintValue = "yammer") +case class Blather(bar:Int) extends Foo +``` +**File2.scala** ```scala -val cardHints = StringMatchHintModifier(Map("ace" -> typeOf[PlayerAce], "king" -> typeOf[PlayerKing])) -val sizeHints = StringMatchHintModifier(Map("small" -> typeOf[Ant], "big" -> typeOf[Elephant])) -val sj = ScalaJack() - .withHintModifiers((typeOf[Card] -> cardHints), (typeOf[Animal] -> sizeHints)) - .withHints((typeOf[Card] -> "card"),(typeOf[Animal] -> "size")) -val inst:Animal = Elephant(3, PlayerAce(2)) -println(sj.render(inst)) -// {"size":"big","b":3,"c":{"card":"ace","a":2}} +given sjFoo: ScalaJack[Foo] = sjCodecOf[Foo](SJConfig().withTypeHintPolicy(TypeHintPolicy.USE_ANNOTATION)) +println(sjFoo.toJson(Blather(2))) +// {"_hint":"yammer","bar":2} //<<-- Type hint for Blather is now "yammer", by annotation ``` -Wow! Now there's nothing left in the rendered (and read) JSON that remotely looks like a normal ScalaJack type hint. Both the labels and the values have been modified, which is entirely suitable for 3rd party JSON. +When using TypeHintPolicy.USE_ANNOTATION, any classes that do not have a @TypeHint annotation will simply revert to the simple class name scheme, so you can mix and match as needed. \ No newline at end of file diff --git a/doc/union.md b/doc/union.md index 57b4a6e9..ea54f519 100644 --- a/doc/union.md +++ b/doc/union.md @@ -1,25 +1,25 @@ -## Union Types -JSON can be messy. Very messy. Take Avro schema file definitions, for example. An Avro "type" field can have 1 of 4 possible values: -1) An ennumeration value of a simple type -2) A symbol (string) of a named complex type -3) An embedded complex type (JSON object) -4) A union of a list of any of the above -Great, eh? Definitely not something easy to deserialize. Ideally, Scala would have built-in support for union types, or a type that can have 1 of several types of values. Several 3rd party libraries, e.g. Cats, glom this feature into Scala with varying degrees of success. The problem is that because these are not language-native ScalaJack's reflection engine doesn't know how to unpack them. +## Union Types -The solution is the ScalaJack Union type, which is a caveman-primitive implementation consisting of several typed fields, all of which are optional, and only one specified. Yuck, right? +JSON can be messy. Very messy. Although it is terrible practice, 3rd party JSON of questionable discipline can specify fields that can hold more than one data type. For example let's assume we get JSON like this from a 3rd party: -Looks like this: +```json +{"id":"12345", "category":"linear", "count": 3} +{"id":"12345", "category":["linear", "concentric"], "count": 3} +``` +In this example, the category field can either be a String or a List[String]. We can use ScalaJack's support of Scala 3's Union type to solve this problem. +**File 1.scala** ```scala -import co.blocke.scalajack.Union3 -case class Multi3(one: Union3[List[String], List[Int], Boolean]) -val m3 = Multi3(Union3(None,Some(List(1,2,3)),None)) +case class VendorRecord(id: String, category: String | List[String], count: Int) ``` -In this case we have a Union3, which can be of type List[String], List[Int], or Boolean. - -ScalaJack defines 3 Union types: Union2, Union3, and Union4. +**File 2.scala** +```scala +given sjVendor: ScalaJack[VendorRecord] = sjCodecOf[VendorRecord] -Not an elegant solution at all, but it does work if you have 3rd party JSON that may be well-formed, but that doesn't follow the strict rules of typed object serialization. +sjVendor.fromJson("""{"id":"12345", "category":"linear", "count": 3}""") +// materializes VendorRecord("12345","linear",3) -> Call For Proposal: If anyone can suggest or contribute a more elegant way of accomplishing this goal, please submit a PR! +sjVendor.fromJson("""{"id":"12345", "category":["linear", "concentric"], "count": 3}""" ) +// materializes VendorRecord("12345",List("linear","concentric"),3) +``` \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala b/src/main/scala/co.blocke.scalajack/SJConfig.scala similarity index 76% rename from src/main/scala/co.blocke.scalajack/json/JsonConfig.scala rename to src/main/scala/co.blocke.scalajack/SJConfig.scala index 6757be12..aa0ddb01 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonConfig.scala +++ b/src/main/scala/co.blocke.scalajack/SJConfig.scala @@ -1,12 +1,14 @@ package co.blocke.scalajack -package json import co.blocke.scala_reflection.TypedName import co.blocke.scala_reflection.reflect.* import co.blocke.scala_reflection.reflect.rtypeRefs.* import scala.quoted.* +import scala.util.control.NoStackTrace -class JsonConfig private[scalajack] ( +class ConfigError(msg: String) extends Throwable(msg) with NoStackTrace + +class SJConfig private[scalajack] ( val noneAsNull: Boolean, val tryFailureHandling: TryPolicy, val eitherLeftHandling: EitherLeftPolicy, @@ -19,15 +21,15 @@ class JsonConfig private[scalajack] ( val _suppressEscapedStrings: Boolean, val _suppressTypeHints: Boolean ): - def withNoneAsNull(): JsonConfig = copy(noneAsNull = true) - def withTryFailureHandling(tryPolicy: TryPolicy): JsonConfig = copy(tryFailureHandling = tryPolicy) - def withEitherLeftHandling(eitherPolicy: EitherLeftPolicy): JsonConfig = copy(eitherLeftHandling = eitherPolicy) - def withTypeHintLabel(label: String): JsonConfig = copy(typeHintLabel = label) - def withTypeHintPolicy(hintPolicy: TypeHintPolicy): JsonConfig = copy(typeHintPolicy = hintPolicy) - def withEnumsAsIds(asIds: List[String]): JsonConfig = copy(enumsAsIds = asIds) - def writeNonConstructorFields(): JsonConfig = copy(_writeNonConstructorFields = true) - def suppressEscapedStrings(): JsonConfig = copy(_suppressEscapedStrings = true) - def suppressTypeHints(): JsonConfig = copy(_suppressTypeHints = true) + def withNoneAsNull(): SJConfig = copy(noneAsNull = true) + def withTryFailureHandling(tryPolicy: TryPolicy): SJConfig = copy(tryFailureHandling = tryPolicy) + def withEitherLeftHandling(eitherPolicy: EitherLeftPolicy): SJConfig = copy(eitherLeftHandling = eitherPolicy) + def withTypeHintLabel(label: String): SJConfig = copy(typeHintLabel = label) + def withTypeHintPolicy(hintPolicy: TypeHintPolicy): SJConfig = copy(typeHintPolicy = hintPolicy) + def withEnumsAsIds(asIds: List[String]): SJConfig = copy(enumsAsIds = asIds) + def writeNonConstructorFields(): SJConfig = copy(_writeNonConstructorFields = true) + def suppressEscapedStrings(): SJConfig = copy(_suppressEscapedStrings = true) + def suppressTypeHints(): SJConfig = copy(_suppressTypeHints = true) private def copy( noneAsNull: Boolean = noneAsNull, @@ -39,7 +41,7 @@ class JsonConfig private[scalajack] ( _writeNonConstructorFields: Boolean = _writeNonConstructorFields, _suppressEscapedStrings: Boolean = _suppressEscapedStrings, _suppressTypeHints: Boolean = _suppressTypeHints - ): JsonConfig = new JsonConfig( + ): SJConfig = new SJConfig( noneAsNull, tryFailureHandling, eitherLeftHandling, @@ -60,8 +62,8 @@ enum EitherLeftPolicy: enum TypeHintPolicy: case SIMPLE_CLASSNAME, SCRAMBLE_CLASSNAME, USE_ANNOTATION -object JsonConfig - extends JsonConfig( +object SJConfig + extends SJConfig( noneAsNull = false, tryFailureHandling = TryPolicy.AS_NULL, eitherLeftHandling = EitherLeftPolicy.AS_VALUE, @@ -74,10 +76,10 @@ object JsonConfig ): import scala.quoted.FromExpr.* - private[scalajack] given ToExpr[JsonConfig] with { - def apply(x: JsonConfig)(using Quotes): Expr[JsonConfig] = + private[scalajack] given ToExpr[SJConfig] with { + def apply(x: SJConfig)(using Quotes): Expr[SJConfig] = '{ - val jc = JsonConfig + val jc = SJConfig .withTryFailureHandling(${ Expr(x.tryFailureHandling) }) .withEitherLeftHandling(${ Expr(x.eitherLeftHandling) }) .withTypeHintLabel(${ Expr(x.typeHintLabel) }) @@ -103,18 +105,18 @@ object JsonConfig } } - private[scalajack] given FromExpr[JsonConfig] with { + private[scalajack] given FromExpr[SJConfig] with { def extract[X: FromExpr](name: String, x: Expr[X])(using Quotes): X = import quotes.reflect.* - summon[FromExpr[X]].unapply(x).getOrElse(throw JsonConfigError(s"Can't parse $name: ${x.show}, tree: ${x.asTerm}")) + summon[FromExpr[X]].unapply(x).getOrElse(throw ConfigError(s"Can't parse $name: ${x.show}, tree: ${x.asTerm}")) - def unapply(x: Expr[JsonConfig])(using Quotes): Option[JsonConfig] = + def unapply(x: Expr[SJConfig])(using Quotes): Option[SJConfig] = import quotes.reflect.* x match case '{ - JsonConfig( + SJConfig( $noneAsNullE, $tryFailureHandlerE, $eitherLeftHandlerE, @@ -129,7 +131,7 @@ object JsonConfig } => try Some( - JsonConfig( + SJConfig( extract("noneAsNull", noneAsNullE), extract("tryFailureHandler", tryFailureHandlerE), extract("eitherLeftHandler", eitherLeftHandlerE), @@ -146,16 +148,16 @@ object JsonConfig println("ERROR: " + x.getMessage) None } - case '{ JsonConfig } => Some(JsonConfig) - case '{ ($x: JsonConfig).withNoneAsNull() } => Some(x.valueOrAbort.withNoneAsNull()) - case '{ ($x: JsonConfig).withTryFailureHandling($v) } => Some(x.valueOrAbort.withTryFailureHandling(v.valueOrAbort)) - case '{ ($x: JsonConfig).withEitherLeftHandling($v) } => Some(x.valueOrAbort.withEitherLeftHandling(v.valueOrAbort)) - case '{ ($x: JsonConfig).withTypeHintLabel($v) } => Some(x.valueOrAbort.withTypeHintLabel(v.valueOrAbort)) - case '{ ($x: JsonConfig).withTypeHintPolicy($v) } => Some(x.valueOrAbort.withTypeHintPolicy(v.valueOrAbort)) - case '{ ($x: JsonConfig).withEnumsAsIds($v) } => Some(x.valueOrAbort.withEnumsAsIds(v.valueOrAbort)) - case '{ ($x: JsonConfig).writeNonConstructorFields() } => Some(x.valueOrAbort.writeNonConstructorFields()) - case '{ ($x: JsonConfig).suppressEscapedStrings() } => Some(x.valueOrAbort.suppressEscapedStrings()) - case '{ ($x: JsonConfig).suppressTypeHints() } => Some(x.valueOrAbort.suppressTypeHints()) + case '{ SJConfig } => Some(SJConfig) + case '{ ($x: SJConfig).withNoneAsNull() } => Some(x.valueOrAbort.withNoneAsNull()) + case '{ ($x: SJConfig).withTryFailureHandling($v) } => Some(x.valueOrAbort.withTryFailureHandling(v.valueOrAbort)) + case '{ ($x: SJConfig).withEitherLeftHandling($v) } => Some(x.valueOrAbort.withEitherLeftHandling(v.valueOrAbort)) + case '{ ($x: SJConfig).withTypeHintLabel($v) } => Some(x.valueOrAbort.withTypeHintLabel(v.valueOrAbort)) + case '{ ($x: SJConfig).withTypeHintPolicy($v) } => Some(x.valueOrAbort.withTypeHintPolicy(v.valueOrAbort)) + case '{ ($x: SJConfig).withEnumsAsIds($v) } => Some(x.valueOrAbort.withEnumsAsIds(v.valueOrAbort)) + case '{ ($x: SJConfig).writeNonConstructorFields() } => Some(x.valueOrAbort.writeNonConstructorFields()) + case '{ ($x: SJConfig).suppressEscapedStrings() } => Some(x.valueOrAbort.suppressEscapedStrings()) + case '{ ($x: SJConfig).suppressTypeHints() } => Some(x.valueOrAbort.suppressTypeHints()) } private[scalajack] given ToExpr[TryPolicy] with { diff --git a/src/main/scala/co.blocke.scalajack/ScalaJack.scala b/src/main/scala/co.blocke.scalajack/ScalaJack.scala index 5b070a6d..84abc10a 100644 --- a/src/main/scala/co.blocke.scalajack/ScalaJack.scala +++ b/src/main/scala/co.blocke.scalajack/ScalaJack.scala @@ -27,15 +27,15 @@ object ScalaJack: def codecOfImpl[T: Type](using Quotes): Expr[ScalaJack[T]] = import quotes.reflect.* val classRef = ReflectOnType[T](quotes)(TypeRepr.of[T], true)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) - val jsonCodec = JsonCodecMaker.generateCodecFor(classRef, JsonConfig) + val jsonCodec = JsonCodecMaker.generateCodecFor(classRef, SJConfig) '{ ScalaJack($jsonCodec) } // ----- Use given JsonConfig - inline def sjCodecOf[T](inline cfg: JsonConfig): ScalaJack[T] = ${ codecOfImplWithConfig[T]('cfg) } - def codecOfImplWithConfig[T: Type](cfgE: Expr[JsonConfig])(using Quotes): Expr[ScalaJack[T]] = + inline def sjCodecOf[T](inline cfg: SJConfig): ScalaJack[T] = ${ codecOfImplWithConfig[T]('cfg) } + def codecOfImplWithConfig[T: Type](cfgE: Expr[SJConfig])(using Quotes): Expr[ScalaJack[T]] = import quotes.reflect.* - val cfg = summon[FromExpr[JsonConfig]].unapply(cfgE) + val cfg = summon[FromExpr[SJConfig]].unapply(cfgE) val classRef = ReflectOnType[T](quotes)(TypeRepr.of[T], true)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) - val jsonCodec = JsonCodecMaker.generateCodecFor(classRef, cfg.getOrElse(JsonConfig)) + val jsonCodec = JsonCodecMaker.generateCodecFor(classRef, cfg.getOrElse(SJConfig)) '{ ScalaJack($jsonCodec) } diff --git a/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala b/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala index 54379541..8864bc4b 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala @@ -21,7 +21,7 @@ import scala.jdk.CollectionConverters.* object JsonCodecMaker: - def generateCodecFor[T](ref: RTypeRef[T], cfg: JsonConfig)(using q: Quotes)(using tt: Type[T]) = + def generateCodecFor[T](ref: RTypeRef[T], cfg: SJConfig)(using q: Quotes)(using tt: Type[T]) = import q.reflect.* // Cache generated method Symbols + an array of the generated functions (DefDef) @@ -120,7 +120,7 @@ object JsonCodecMaker: if !isValid then throw new JsonTypeError(s"For JSON serialization, map keys must be a simple type. ${testRef.name} is too complex.") isValid - def maybeWrite[T](label: String, aE: Expr[T], ref: RTypeRef[T], out: Expr[JsonOutput], cfg: JsonConfig): Expr[Unit] = + def maybeWrite[T](label: String, aE: Expr[T], ref: RTypeRef[T], out: Expr[JsonOutput], cfg: SJConfig): Expr[Unit] = val labelE = Expr(label) _maybeWrite[T]( '{ $out.label($labelE) }, @@ -130,7 +130,7 @@ object JsonCodecMaker: cfg ) - def maybeWriteMap[K, V](keyE: Expr[K], valueE: Expr[V], keyRef: RTypeRef[K], valueRef: RTypeRef[V], out: Expr[JsonOutput], cfg: JsonConfig): Expr[Unit] = + def maybeWriteMap[K, V](keyE: Expr[K], valueE: Expr[V], keyRef: RTypeRef[K], valueRef: RTypeRef[V], out: Expr[JsonOutput], cfg: SJConfig): Expr[Unit] = keyRef.refType match case '[k] => _maybeWrite[V]( @@ -150,7 +150,7 @@ object JsonCodecMaker: // Returns Expr[Unit] containing either the original phrase (if ok to write) or the phrase // prepended with the type-appropriate runtime check. This may seem like drama, but the idea // is to avoid slowing runtime down with extra "if" checks unless they're absolutely needed. - def _maybeWrite[T](prefix: Expr[Unit], aE: Expr[T], ref: RTypeRef[T], out: Expr[JsonOutput], cfg: JsonConfig): Expr[Unit] = + def _maybeWrite[T](prefix: Expr[Unit], aE: Expr[T], ref: RTypeRef[T], out: Expr[JsonOutput], cfg: SJConfig): Expr[Unit] = ref match case t: ScalaOptionRef[?] if !cfg.noneAsNull => t.optionParamType.refType match @@ -2169,7 +2169,9 @@ object JsonCodecMaker: '{ $res match case Right(r) => r - case Left(m) => throw JsonParseError("NeoType validation for " + $tnameE + " failed", $in) + case Left(m) => + $in.backspace() + throw JsonParseError("NeoType validation for " + $tnameE + " failed", $in) } case _ => diff --git a/src/main/scala/co.blocke.scalajack/json/JsonError.scala b/src/main/scala/co.blocke.scalajack/json/JsonError.scala index 5875e93f..6e3f8832 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonError.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonError.scala @@ -6,7 +6,6 @@ import scala.util.control.NoStackTrace class JsonIllegalKeyType(msg: String) extends Throwable(msg) with NoStackTrace class JsonNullKeyValue(msg: String) extends Throwable(msg) with NoStackTrace class JsonUnsupportedType(msg: String) extends Throwable(msg) with NoStackTrace -class JsonConfigError(msg: String) extends Throwable(msg) with NoStackTrace class JsonEitherLeftError(msg: String) extends Throwable(msg) with NoStackTrace class JsonIllegalCharacterError(msg: String) extends Throwable(msg) with NoStackTrace diff --git a/src/main/scala/co.blocke.scalajack/json/schema/JsonSchema.scala b/src/main/scala/co.blocke.scalajack/json/schema/JsonSchema.scala index 6eb8aaa0..85864547 100644 --- a/src/main/scala/co.blocke.scalajack/json/schema/JsonSchema.scala +++ b/src/main/scala/co.blocke.scalajack/json/schema/JsonSchema.scala @@ -65,7 +65,7 @@ object JsonSchema: ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, ${ if defaultValue.isDefined then - val codec = JsonCodecMaker.generateCodecFor[u](t.refType.asInstanceOf[RTypeRef[u]], json.JsonConfig) + val codec = JsonCodecMaker.generateCodecFor[u](t.refType.asInstanceOf[RTypeRef[u]], SJConfig) '{ val out = new writing.JsonOutput() $codec.encodeValue(${ defaultValue.get.asExprOf[u] }, out) @@ -111,7 +111,7 @@ object JsonSchema: ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, ${ if defaultValue.isDefined then - val codec = JsonCodecMaker.generateCodecFor[u](t.asInstanceOf[RTypeRef[u]], json.JsonConfig) + val codec = JsonCodecMaker.generateCodecFor[u](t.asInstanceOf[RTypeRef[u]], SJConfig) '{ val out = new writing.JsonOutput() $codec.encodeValue(${ defaultValue.get.asExprOf[u] }, out) @@ -135,7 +135,7 @@ object JsonSchema: ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, ${ if defaultValue.isDefined then - val codec = JsonCodecMaker.generateCodecFor[u](t.asInstanceOf[RTypeRef[u]], json.JsonConfig) + val codec = JsonCodecMaker.generateCodecFor[u](t.asInstanceOf[RTypeRef[u]], SJConfig) '{ val out = new writing.JsonOutput() $codec.encodeValue(${ defaultValue.get.asExprOf[u] }, out) @@ -164,7 +164,7 @@ object JsonSchema: ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, ${ if defaultValue.isDefined then - val codec = JsonCodecMaker.generateCodecFor[u](t.asInstanceOf[RTypeRef[u]], json.JsonConfig) + val codec = JsonCodecMaker.generateCodecFor[u](t.asInstanceOf[RTypeRef[u]], SJConfig) '{ val out = new writing.JsonOutput() $codec.encodeValue(${ defaultValue.get.asExprOf[u] }, out) @@ -303,7 +303,7 @@ object JsonSchema: ${ Expr(t.annotations.get("co.blocke.scalajack.schema.description").flatMap(_.get("value"))) }, ${ if defaultValue.isDefined then - val codec = JsonCodecMaker.generateCodecFor[u](t.asInstanceOf[RTypeRef[u]], json.JsonConfig.suppressTypeHints()) + val codec = JsonCodecMaker.generateCodecFor[u](t.asInstanceOf[RTypeRef[u]], SJConfig.suppressTypeHints()) '{ val out = new writing.JsonOutput() $codec.encodeValue(${ defaultValue.get.asExprOf[u] }, out) diff --git a/src/main/scala/co.blocke.scalajack/json/writing/AnyWriter.scala b/src/main/scala/co.blocke.scalajack/json/writing/AnyWriter.scala index 66b8f35a..95a478e6 100644 --- a/src/main/scala/co.blocke.scalajack/json/writing/AnyWriter.scala +++ b/src/main/scala/co.blocke.scalajack/json/writing/AnyWriter.scala @@ -18,7 +18,7 @@ import scala.quoted.* object AnyWriter: - def writeAny(target: Any, out: JsonOutput, cfg: JsonConfig, inTuple: Boolean = false): Unit = + def writeAny(target: Any, out: JsonOutput, cfg: SJConfig, inTuple: Boolean = false): Unit = // val rt = RType.of(target.getClass) target match case null => out.burpNull() @@ -122,7 +122,7 @@ object AnyWriter: case _ => throw new JsonUnsupportedType("Class " + v.getClass.getName + " not supported for Any type") // Called by non-Any classes (in JsonCodecMaker) that have Any-typed fields - def isOkToWrite(prefix: Expr[Unit], value: Expr[Any], out: Expr[JsonOutput], cfg: JsonConfig)(using Quotes): Expr[Unit] = + def isOkToWrite(prefix: Expr[Unit], value: Expr[Any], out: Expr[JsonOutput], cfg: SJConfig)(using Quotes): Expr[Unit] = import quotes.reflect.* '{ _okToWrite($value, ${ Expr(cfg) }).map { v => @@ -132,13 +132,13 @@ object AnyWriter: } // Called for Any-typed classes - private def _okToWrite(label: String, value: Any, out: JsonOutput, cfg: JsonConfig): Unit = + private def _okToWrite(label: String, value: Any, out: JsonOutput, cfg: SJConfig): Unit = _okToWrite(value, cfg).map { v => out.label(label) writeAny(v, out, cfg) } - private def _okToWrite(value: Any, cfg: JsonConfig): Option[Any] = + private def _okToWrite(value: Any, cfg: SJConfig): Option[Any] = value match case None => if cfg.noneAsNull then Some(null) else None case Failure(e) => diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scalax b/src/main/scala/co.blocke.scalajack/run/Play.scalax index b313ca6f..71f2c8d0 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scalax +++ b/src/main/scala/co.blocke.scalajack/run/Play.scalax @@ -2,6 +2,8 @@ package co.blocke.scalajack package json package run +import ScalaJack.* + case class Well(repo: scala.collection.mutable.Map[String, Int] = scala.collection.mutable.Map.empty[String, Int]) object RunMe extends App: @@ -10,23 +12,17 @@ object RunMe extends App: import co.blocke.scalajack.json.run.Record import co.blocke.scala_reflection.* - given sjPerson: ScalaJack[Person] = sjCodecOf[Person] - given sjAm: ScalaJack[Amorphous] = sjCodecOf[Amorphous] - - println(sjPerson.toJson(Person())) - - println(sjAm.toJson(Amorphous(Person()))) + given sjValidated: ScalaJack[Validated] = sjCodecOf[Validated] - import VehicleClass.* - // type ComplexPerson = PersonX[Artist[Int, Hobby[Double, Char]], Vehicle[? <: VehicleClass]] - // given sjPersonX: ScalaJack[ComplexPerson] = sjCodecOf[ComplexPerson] - // val inst: ComplexPerson = Employee(Painter(5, Sports(1.2, 'Z')), Car(2)) - // val js = sjPersonX.toJson(inst) - // println(js) + val sj_ok = """{"name":"Mike","xspot":["x","other"],"nada":""}""" + val sj_brokenName = """{"name":"","xspot":["x","other"],"nada":""}""" + val sj_brokenNada = """{"name":"Mike","xspot":["x","other"],"nada":"boom"}""" + val sj_xspot = """{"name":"Mike","xspot":["nintendo","other"],"nada":""}""" - println(RType.of[ComplexPerson].pretty) - given sjPersonX: ScalaJack[ComplexPerson] = sjCodecOf[ComplexPerson] - val inst: ComplexPerson = Employee(Painter(5, Sports(1.2, 'Z')), Car(4)) - val js = sjPersonX.toJson(inst) - println(js) - println(sjPersonX.fromJson(js)) + val inst = Validated(NonEmptyString("Mike"), XList(List("x","y")), EmptyString("")) + val ok = try( sjValidated.fromJson(sj_xspot) ) + catch { + case jpe: JsonParseError => println(jpe.show) + } + println(ok) + \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/run/Record.scalax b/src/main/scala/co.blocke.scalajack/run/Record.scalax index 8980d5f5..63eefcc7 100644 --- a/src/main/scala/co.blocke.scalajack/run/Record.scalax +++ b/src/main/scala/co.blocke.scalajack/run/Record.scalax @@ -87,7 +87,7 @@ given XList: Newtype[List[String]] with override inline def validate(input: List[String]): Boolean = input(0) == "x" -case class Validated(num: NonZeroInt, name: NonEmptyString, xspot: XList, nada: List[EmptyString]) +case class Validated(name: NonEmptyString, xspot: XList, nada: EmptyString) // case class Tag[X](a: X) // given [A, B](using newType: Newtype.WithType[A, B], tag: Tag[A]): Tag[B] = @@ -184,23 +184,3 @@ val record = Record( List(Pet("Fido", "Dog", 5), Pet("Whiskers", "Cat", 3)) ) -//----- - -object VehicleClass extends Enumeration { - type VehicleClass = Value - val Land, Air, Sea = Value -} -import VehicleClass.* - -sealed trait Vehicle { val kind: VehicleClass } -case class Car(passengers: Int) extends Vehicle { val kind: Land.type = Land } - -sealed trait Hobby[X, Y] { val thing1: X; val thing2: Y } -sealed trait Artist[W, Z] { val instrument: W; val effort: Z } -sealed trait PersonX[X, Y] { val who: X; val org: Y } - -case class Sports[A, B](thing1: A, thing2: B) extends Hobby[A, B] -case class Painter[A, B](instrument: A, effort: B) extends Artist[A, B] -case class Employee[A, B, C, D](who: Artist[C, Hobby[D, A]], org: B) extends PersonX[Artist[C, Hobby[D, A]], B] -type ComplexPerson = PersonX[Artist[Int, Hobby[Double, Char]], Vehicle] -// println(RType.of[ComplexPerson].pretty) diff --git a/src/test/scala/co.blocke.scalajack/json/classes/ClassSpec.scala b/src/test/scala/co.blocke.scalajack/json/classes/ClassSpec.scala index 0f7c68d6..fdf1811d 100644 --- a/src/test/scala/co.blocke.scalajack/json/classes/ClassSpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/classes/ClassSpec.scala @@ -35,7 +35,7 @@ class ClassSpec() extends AnyFunSpec with JsonMatchers: inst.hidden_=(true) inst.nope_=(false) inst.foo = "we'll see" - val sj = sjCodecOf[Parent](JsonConfig.writeNonConstructorFields()) + val sj = sjCodecOf[Parent](SJConfig.writeNonConstructorFields()) val js = sj.toJson(inst) js should matchJson("""{"phase":99,"stuff":["x","y"],"foo":"we'll see","hidden":true}""") val re = sj.fromJson(js) @@ -77,7 +77,7 @@ class ClassSpec() extends AnyFunSpec with JsonMatchers: } it("Sealed abstract class with modified type hint label must work") { val inst = AbstractClassHolder(Start2, Fish2("Beta", false), Miami2(101.1)) - val sj = sjCodecOf[AbstractClassHolder](JsonConfig.withTypeHintLabel("ref")) + val sj = sjCodecOf[AbstractClassHolder](SJConfig.withTypeHintLabel("ref")) val js = sj.toJson(inst) js should matchJson("""{"a":"Start2","b":{"ref":"Fish2","species":"Beta","freshwater":false},"c":{"ref":"Miami2","temp":101.1}}""") val re = sj.fromJson(js) @@ -87,7 +87,7 @@ class ClassSpec() extends AnyFunSpec with JsonMatchers: } it("Sealed abstract class with type hint policy SCRAMBLE_CLASSNAME label must work") { val inst = AbstractClassHolder(Start2, Fish2("Beta", false), Miami2(101.1)) - val sj = sjCodecOf[AbstractClassHolder](JsonConfig.withTypeHintPolicy(TypeHintPolicy.SCRAMBLE_CLASSNAME)) + val sj = sjCodecOf[AbstractClassHolder](SJConfig.withTypeHintPolicy(TypeHintPolicy.SCRAMBLE_CLASSNAME)) val js = sj.toJson(inst) val diff = parseJValue(js).diff(parseJValue("""{"a":"Start2","b":{"_hint":"82949-049-49A","species":"Beta","freshwater":false},"c":{"_hint":"53150-867-73B","temp":101.1}}""")) val diffMap = diff.changed.values.asInstanceOf[Map[String, Map[String, ?]]] @@ -99,7 +99,7 @@ class ClassSpec() extends AnyFunSpec with JsonMatchers: } it("Sealed abstract class with type hint policy USE_ANNOTATION label must work") { val inst = AbstractClassHolder(Start2, Fish2("Beta", false), Miami2(101.1)) - val sj = sjCodecOf[AbstractClassHolder](JsonConfig.withTypeHintPolicy(TypeHintPolicy.USE_ANNOTATION)) + val sj = sjCodecOf[AbstractClassHolder](SJConfig.withTypeHintPolicy(TypeHintPolicy.USE_ANNOTATION)) val js = sj.toJson(inst) js should matchJson("""{"a":"Start2","b":{"_hint":"flipper","species":"Beta","freshwater":false},"c":{"_hint":"vice","temp":101.1}}""") val re = sj.fromJson(js) diff --git a/src/test/scala/co.blocke.scalajack/json/classes/TraitSpec.scala b/src/test/scala/co.blocke.scalajack/json/classes/TraitSpec.scala index 27bc6506..3b4fd5f3 100644 --- a/src/test/scala/co.blocke.scalajack/json/classes/TraitSpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/classes/TraitSpec.scala @@ -29,7 +29,7 @@ class TraitSpec() extends AnyFunSpec with JsonMatchers: } it("Sealed trait with modified type hint label must work") { val inst = TraitHolder(Start, Fish("Beta", false), Miami(101.1), CityRouteImpl(25)) - val sj = sjCodecOf[TraitHolder](JsonConfig.withTypeHintLabel("ref")) + val sj = sjCodecOf[TraitHolder](SJConfig.withTypeHintLabel("ref")) val js = sj.toJson(inst) js should matchJson("""{"a":"Start","b":{"ref":"Fish","species":"Beta","freshwater":false},"c":{"ref":"Miami","temp":101.1},"d":{"ref":"CityRoute","numStreets":25}}""") val re = sj.fromJson(js) @@ -40,7 +40,7 @@ class TraitSpec() extends AnyFunSpec with JsonMatchers: } it("Sealed trait with type hint policy SCRAMBLE_CLASSNAME label must work") { val inst = TraitHolder(Start, Fish("Beta", false), Miami(101.1), CityRouteImpl(25)) - val sj = sjCodecOf[TraitHolder](JsonConfig.withTypeHintPolicy(TypeHintPolicy.SCRAMBLE_CLASSNAME)) + val sj = sjCodecOf[TraitHolder](SJConfig.withTypeHintPolicy(TypeHintPolicy.SCRAMBLE_CLASSNAME)) val js = sj.toJson(inst) val diff = parseJValue(js).diff(parseJValue("""{"a":"Start","b":{"_hint":"86999-847-46A","species":"Beta","freshwater":false},"c":{"_hint":"13652-857-33B","temp":101.1},"d":{"_hint":"51470-503-54B","numStreets":25}}""")) val diffMap = diff.changed.values.asInstanceOf[Map[String, Map[String, ?]]] @@ -53,7 +53,7 @@ class TraitSpec() extends AnyFunSpec with JsonMatchers: } it("Sealed trait with type hint policy USE_ANNOTATION label must work") { val inst = TraitHolder(Start, Fish("Beta", false), Miami(101.1), CityRouteImpl(25)) - val sj = sjCodecOf[TraitHolder](JsonConfig.withTypeHintPolicy(TypeHintPolicy.USE_ANNOTATION)) + val sj = sjCodecOf[TraitHolder](SJConfig.withTypeHintPolicy(TypeHintPolicy.USE_ANNOTATION)) val js = sj.toJson(inst) js should matchJson("""{"a":"Start","b":{"_hint":"flipper","species":"Beta","freshwater":false},"c":{"_hint":"vice","temp":101.1},"d":{"_hint":"CityRoute","numStreets":25}}""") val re = sj.fromJson(js) diff --git a/src/test/scala/co.blocke.scalajack/json/collections/JavaCollSpec.scala b/src/test/scala/co.blocke.scalajack/json/collections/JavaCollSpec.scala index 363b1e8b..96a75593 100644 --- a/src/test/scala/co.blocke.scalajack/json/collections/JavaCollSpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/collections/JavaCollSpec.scala @@ -65,7 +65,7 @@ class JavaCollSpec() extends AnyFunSpec with JsonMatchers: } it("Set of either must work") { val inst = JSetHolder[Either[Int, Boolean]](HashSet(Arrays.asList(Right(true), Left(15), Right(false)))) - val sj = sjCodecOf[JSetHolder[Either[Int, Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)) + val sj = sjCodecOf[JSetHolder[Either[Int, Boolean]]](SJConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)) val js = sj.toJson(inst) js should matchJson("""{"a":[true,15,false]}""") sj.fromJson(js) shouldEqual (inst) @@ -138,7 +138,7 @@ class JavaCollSpec() extends AnyFunSpec with JsonMatchers: } it("ArrayList of either must work") { val inst = ArrayListHolder[Either[Int, Boolean]](ArrayList[Either[Int, Boolean]](Arrays.asList(Right(true), Left(15), Right(false)))) - val sj = sjCodecOf[ArrayListHolder[Either[Int, Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)) + val sj = sjCodecOf[ArrayListHolder[Either[Int, Boolean]]](SJConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)) val js = sj.toJson(inst) js should matchJson("""{"a":[true,15,false]}""") sj.fromJson(js) shouldEqual (inst) diff --git a/src/test/scala/co.blocke.scalajack/json/collections/SeqSetArraySpec.scala b/src/test/scala/co.blocke.scalajack/json/collections/SeqSetArraySpec.scala index fddc90eb..0313b347 100644 --- a/src/test/scala/co.blocke.scalajack/json/collections/SeqSetArraySpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/collections/SeqSetArraySpec.scala @@ -52,7 +52,7 @@ class SeqSetArraySpec() extends AnyFunSpec with JsonMatchers: } it("Seq of either must work") { val inst = SeqHolder[Either[Int, Boolean]](List(Right(true), Left(15), Right(false))) - val sj = sjCodecOf[SeqHolder[Either[Int, Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)) + val sj = sjCodecOf[SeqHolder[Either[Int, Boolean]]](SJConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)) val js = sj.toJson(inst) js should matchJson("""{"a":[true,15,false]}""") sj.fromJson(js) shouldEqual (inst) @@ -123,7 +123,7 @@ class SeqSetArraySpec() extends AnyFunSpec with JsonMatchers: } it("Set of either must work") { val inst = SetHolder[Either[Int, Boolean]](HashSet(Right(true), Left(15), Right(false))) - val sj = sjCodecOf[SetHolder[Either[Int, Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)) + val sj = sjCodecOf[SetHolder[Either[Int, Boolean]]](SJConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)) val js = sj.toJson(inst) js should matchJson("""{"a":[15,true,false]}""") sj.fromJson(js) shouldEqual (inst) @@ -194,7 +194,7 @@ class SeqSetArraySpec() extends AnyFunSpec with JsonMatchers: } it("Array of either must work") { val inst = ArrayHolder[Either[Int, Boolean]](Array(Right(true), Left(15), Right(false))) - val sj = sjCodecOf[ArrayHolder[Either[Int, Boolean]]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)) + val sj = sjCodecOf[ArrayHolder[Either[Int, Boolean]]](SJConfig.withEitherLeftHandling(EitherLeftPolicy.AS_VALUE)) val js = sj.toJson(inst) js should matchJson("""{"a":[true,15,false]}""") sj.fromJson(js).a.toList shouldEqual (inst.a.toList) diff --git a/src/test/scala/co.blocke.scalajack/json/misc/AliasSpec.scala b/src/test/scala/co.blocke.scalajack/json/misc/AliasSpec.scala index e3db4cb1..58a2b469 100644 --- a/src/test/scala/co.blocke.scalajack/json/misc/AliasSpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/misc/AliasSpec.scala @@ -36,7 +36,7 @@ class AliasSpec() extends AnyFunSpec with JsonMatchers: } it("Type aliases (opaque types) must be dereferenced (with Option, noneAsNull)") { val inst = AliasHolder2[CountX](Some(5), List(Some(1), None, Some(3)), Map("wow" -> None)) - val sj = sjCodecOf[AliasHolder2[CountX]](JsonConfig.withNoneAsNull()) + val sj = sjCodecOf[AliasHolder2[CountX]](SJConfig.withNoneAsNull()) val js = sj.toJson(inst) js should matchJson("""{"a":5,"b":[1,null,3],"c":{"wow":null}}""") sj.fromJson(js) shouldEqual (inst) diff --git a/src/test/scala/co.blocke.scalajack/json/misc/EnumSpec.scala b/src/test/scala/co.blocke.scalajack/json/misc/EnumSpec.scala index 6cb843a8..4d2207f3 100644 --- a/src/test/scala/co.blocke.scalajack/json/misc/EnumSpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/misc/EnumSpec.scala @@ -24,7 +24,7 @@ class EnumSpec() extends AnyFunSpec with JsonMatchers: } it("Enum as Map key and value must work (using id)") { val inst = MapHolder[Color, Color](Map(Color.Red -> Color.Blue, Color.Green -> Color.Red)) - val sj = sjCodecOf[MapHolder[Color, Color]](JsonConfig.withEnumsAsIds(Nil)) + val sj = sjCodecOf[MapHolder[Color, Color]](SJConfig.withEnumsAsIds(Nil)) val js = sj.toJson(inst) js should matchJson("""{"a":{"0":1,"2":0}}""") sj.fromJson(js) shouldEqual (inst) @@ -40,7 +40,7 @@ class EnumSpec() extends AnyFunSpec with JsonMatchers: it("Enumeration (Scale 2) as Map key and value must work (using id)") { import Permissions.* val inst = MapHolder[Permissions, Permissions](Map(Permissions.READ -> Permissions.WRITE, Permissions.EXEC -> Permissions.NONE)) - val sj = sjCodecOf[MapHolder[Permissions, Permissions]](JsonConfig.withEnumsAsIds(Nil)) + val sj = sjCodecOf[MapHolder[Permissions, Permissions]](SJConfig.withEnumsAsIds(Nil)) val js = sj.toJson(inst) js should matchJson("""{"a":{"0":1,"2":3}}""") sj.fromJson(js) shouldEqual (inst) @@ -64,7 +64,7 @@ class EnumSpec() extends AnyFunSpec with JsonMatchers: } it("Java Enumeration as Map key and value must work (using id)") { val inst = MapHolder[CarEnum, CarEnum](Map(CarEnum.VW -> CarEnum.PORSCHE, CarEnum.PORSCHE -> CarEnum.TOYOTA)) - val sj = sjCodecOf[MapHolder[CarEnum, CarEnum]](JsonConfig.withEnumsAsIds(Nil)) + val sj = sjCodecOf[MapHolder[CarEnum, CarEnum]](SJConfig.withEnumsAsIds(Nil)) val js = sj.toJson(inst) val targetJs = RType.of[CarEnum] match case t: co.blocke.scala_reflection.rtypes.JavaEnumRType[?] => @@ -76,7 +76,7 @@ class EnumSpec() extends AnyFunSpec with JsonMatchers: it("Enum/Enumeration mix of enum as value must work") { import Permissions.* val inst = MapHolder[Color, Permissions](Map(Color.Red -> Permissions.WRITE, Color.Blue -> Permissions.NONE)) - val sj = sjCodecOf[MapHolder[Color, Permissions]](JsonConfig.withEnumsAsIds(List("co.blocke.scalajack.json.misc.Color"))) + val sj = sjCodecOf[MapHolder[Color, Permissions]](SJConfig.withEnumsAsIds(List("co.blocke.scalajack.json.misc.Color"))) val js = sj.toJson(inst) js should matchJson("""{"a":{"0":"WRITE","1":"NONE"}}""") sj.fromJson(js) shouldEqual (inst) @@ -104,7 +104,7 @@ class EnumSpec() extends AnyFunSpec with JsonMatchers: ex.getMessage() shouldEqual ("enum co.blocke.scalajack.json.misc.Color has no case with name: Bogus") } it("Enum must break(using id) - bad value") { - val sj = sjCodecOf[MapHolder[Color, Color]](JsonConfig.withEnumsAsIds(Nil)) + val sj = sjCodecOf[MapHolder[Color, Color]](SJConfig.withEnumsAsIds(Nil)) val js = """{"a":{"0":1,"9":0}}""" val ex = intercept[java.util.NoSuchElementException](sj.fromJson(js)) ex.getMessage() shouldEqual ("enum co.blocke.scalajack.json.misc.Color has no case with ordinal: 9") diff --git a/src/test/scala/co.blocke.scalajack/json/misc/LRSpec.scala b/src/test/scala/co.blocke.scalajack/json/misc/LRSpec.scala index f70a9c78..7be3f3dd 100644 --- a/src/test/scala/co.blocke.scalajack/json/misc/LRSpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/misc/LRSpec.scala @@ -32,7 +32,7 @@ class LRSpec() extends AnyFunSpec with JsonMatchers: } it("Complex Either/Option must work (NoneAsNull)") { val inst = ComplexEither[Int](Some(Right(None))) - val sj = sjCodecOf[ComplexEither[Int]](JsonConfig.withNoneAsNull()) + val sj = sjCodecOf[ComplexEither[Int]](SJConfig.withNoneAsNull()) val js = sj.toJson(inst) js should matchJson("""{"a":null}""") // same output result regardless of noneAsNull setting sj.fromJson(js) shouldEqual (ComplexEither(None)) // None here because value existed, but was null with NoneAsNull @@ -48,14 +48,14 @@ class LRSpec() extends AnyFunSpec with JsonMatchers: } it("Complex Either/Option must work (Left-AS_NULL)") { val inst = ComplexEither[Int](Some(Left("err"))) - val sj = sjCodecOf[ComplexEither[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_NULL)) + val sj = sjCodecOf[ComplexEither[Int]](SJConfig.withEitherLeftHandling(EitherLeftPolicy.AS_NULL)) val js = sj.toJson(inst) js should matchJson("""{"a":null}""") sj.fromJson(js) shouldEqual (ComplexEither(null)) } it("Complex Either/Option must work (Left-AS_NULL, Option nullAsNull)") { val inst = ComplexEither[Int](Some(Left("err"))) - val sj = sjCodecOf[ComplexEither[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_NULL).withNoneAsNull()) + val sj = sjCodecOf[ComplexEither[Int]](SJConfig.withEitherLeftHandling(EitherLeftPolicy.AS_NULL).withNoneAsNull()) val js = sj.toJson(inst) js should matchJson("""{"a":null}""") sj.fromJson(js) shouldEqual (ComplexEither(None)) @@ -69,14 +69,14 @@ class LRSpec() extends AnyFunSpec with JsonMatchers: } it("Either with AS_NULL left policy must work") { val inst = EitherHolder[Int](Left(5), Right(3)) - val sj = sjCodecOf[EitherHolder[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.AS_NULL)) + val sj = sjCodecOf[EitherHolder[Int]](SJConfig.withEitherLeftHandling(EitherLeftPolicy.AS_NULL)) val js = sj.toJson(inst) js should matchJson("""{"a":null,"b":3}""") sj.fromJson(js) shouldEqual (EitherHolder(null, Right(3))) } it("Either with ERR_MSG_STRING left policy must work") { val inst = EitherHolder[Int](Left(5), Right(3)) - val sj = sjCodecOf[EitherHolder[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.ERR_MSG_STRING)) + val sj = sjCodecOf[EitherHolder[Int]](SJConfig.withEitherLeftHandling(EitherLeftPolicy.ERR_MSG_STRING)) val js = sj.toJson(inst) js should matchJson("""{"a":"Left Error: 5","b":3}""") sj.fromJson(js) shouldEqual (EitherHolder(Right("Left Error: 5"), Right(3))) @@ -88,7 +88,7 @@ class LRSpec() extends AnyFunSpec with JsonMatchers: val inst = EitherHolder[Int](Left(5), Right(3)) val caught = intercept[JsonEitherLeftError] { - sjCodecOf[EitherHolder[Int]](JsonConfig.withEitherLeftHandling(EitherLeftPolicy.THROW_EXCEPTION)).toJson(inst) + sjCodecOf[EitherHolder[Int]](SJConfig.withEitherLeftHandling(EitherLeftPolicy.THROW_EXCEPTION)).toJson(inst) } assert(caught.getMessage == "Left Error: 5") } diff --git a/src/test/scala/co.blocke.scalajack/json/misc/MiscSpec.scala b/src/test/scala/co.blocke.scalajack/json/misc/MiscSpec.scala index 12d57f09..e7bda4a2 100644 --- a/src/test/scala/co.blocke.scalajack/json/misc/MiscSpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/misc/MiscSpec.scala @@ -20,7 +20,7 @@ class MiscSpec() extends AnyFunSpec with JsonMatchers: on another level.""") val sj1 = sjCodecOf[StringHolder] val js1 = sj1.toJson(inst) - val sj2 = sjCodecOf[StringHolder](JsonConfig.suppressEscapedStrings()) + val sj2 = sjCodecOf[StringHolder](SJConfig.suppressEscapedStrings()) val js2 = sj2.toJson(inst) js1 should equal("""{"a":"This is a \"strange\" test\non another level."}""") js2 should equal("""{"a":"This is a "strange" test @@ -80,7 +80,7 @@ on another level."}""") (Some('a'), None, Some('b')) ) val sj = sjCodecOf[AnyHolder]( - JsonConfig + SJConfig .withNoneAsNull() .withEitherLeftHandling(EitherLeftPolicy.ERR_MSG_STRING) .withTryFailureHandling(TryPolicy.ERR_MSG_STRING) diff --git a/src/test/scala/co.blocke.scalajack/json/misc/OptionSpec.scala b/src/test/scala/co.blocke.scalajack/json/misc/OptionSpec.scala index 03375979..3fa16ac6 100644 --- a/src/test/scala/co.blocke.scalajack/json/misc/OptionSpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/misc/OptionSpec.scala @@ -68,7 +68,7 @@ class OptionSpec() extends AnyFunSpec with JsonMatchers: Left(None) // Either of Option (L) ) val sj = sjCodecOf[OptionHolder[Int]]( - JsonConfig.withNoneAsNull() + SJConfig.withNoneAsNull() ) val js = sj.toJson(inst) js should matchJson("""{"a":null,"b":[null,"ok"],"c":[null,null,null],"d":{"1":null,"3":null},"e":null,"f":null,"g":null,"h":null,"i":null,"j":null}""") @@ -90,7 +90,7 @@ class OptionSpec() extends AnyFunSpec with JsonMatchers: } it("Either recipe should work (None as null)") { val inst = EitherRecipe[Int](Right(Left(None))) - val sj = sjCodecOf[EitherRecipe[Int]](JsonConfig.withNoneAsNull()) + val sj = sjCodecOf[EitherRecipe[Int]](SJConfig.withNoneAsNull()) val js = sj.toJson(inst) js should matchJson("""{"a":null}""") sj.fromJson(js) shouldEqual (EitherRecipe[Int](null)) @@ -149,7 +149,7 @@ class OptionSpec() extends AnyFunSpec with JsonMatchers: Left(Optional.empty) // Either of Option (L) ) val sj = sjCodecOf[OptionalHolder[Int]]( - JsonConfig.withNoneAsNull() + SJConfig.withNoneAsNull() ) val js = sj.toJson(inst) js should matchJson("""{"a":null,"b":[null,"ok"],"c":[null,null,null],"d":{"1":null,"3":null},"e":null,"f":null,"g":null,"h":null,"i":null,"j":null}""") @@ -171,7 +171,7 @@ class OptionSpec() extends AnyFunSpec with JsonMatchers: } it("Either recipe should work (None as null)") { val inst = EitherRecipeJ[Int](Right(Left(Optional.empty))) - val sj = sjCodecOf[EitherRecipeJ[Int]](JsonConfig.withNoneAsNull()) + val sj = sjCodecOf[EitherRecipeJ[Int]](SJConfig.withNoneAsNull()) val js = sj.toJson(inst) js should matchJson("""{"a":null}""") sj.fromJson(js) shouldEqual (EitherRecipeJ[Int](null)) diff --git a/src/test/scala/co.blocke.scalajack/json/misc/TrySpec.scala b/src/test/scala/co.blocke.scalajack/json/misc/TrySpec.scala index baf08677..df1c1fcf 100644 --- a/src/test/scala/co.blocke.scalajack/json/misc/TrySpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/misc/TrySpec.scala @@ -54,14 +54,14 @@ class TrySpec() extends AnyFunSpec with JsonMatchers: } it("Try w/policy AS_NULL must work (Failure)") { val inst = TryHolder[Int](Failure(new Exception("boom"))) - val sj = sjCodecOf[TryHolder[Int]](JsonConfig.withTryFailureHandling(TryPolicy.AS_NULL)) + val sj = sjCodecOf[TryHolder[Int]](SJConfig.withTryFailureHandling(TryPolicy.AS_NULL)) val js = sj.toJson(inst) js should matchJson("""{"a":null}""") sj.fromJson(js) shouldEqual (TryHolder[Int](null)) } it("Try w/policy ERR_MSG_STRING must work (Failure)") { val inst = TryHolder[Int](Failure(new Exception("boom"))) - val sj = sjCodecOf[TryHolder[Int]](JsonConfig.withTryFailureHandling(TryPolicy.ERR_MSG_STRING)) + val sj = sjCodecOf[TryHolder[Int]](SJConfig.withTryFailureHandling(TryPolicy.ERR_MSG_STRING)) val js = sj.toJson(inst) js should matchJson("""{"a":"Try Failure with msg: boom"}""") val msg = """Unsuccessful attempt to read Try type with failure: Non-numeric character found when integer value expected at position 5 at position [5] @@ -74,13 +74,13 @@ class TrySpec() extends AnyFunSpec with JsonMatchers: val inst = TryHolder[Int](Failure(new Exception("boom"))) val caught = intercept[java.lang.Exception] { - sjCodecOf[TryHolder[Int]](JsonConfig.withTryFailureHandling(TryPolicy.THROW_EXCEPTION)).toJson(inst) + sjCodecOf[TryHolder[Int]](SJConfig.withTryFailureHandling(TryPolicy.THROW_EXCEPTION)).toJson(inst) } assert(caught.getMessage == "boom") } it("Seq and Tuple of Try must work for AS_NULL (Failure)") { val inst = TryHolder2[Int](List(Success(1), Failure(new Exception("boom")), Success(3)), (Failure(new Exception("boom")), Success(0))) - val sj = sjCodecOf[TryHolder2[Int]](JsonConfig.withTryFailureHandling(TryPolicy.AS_NULL)) + val sj = sjCodecOf[TryHolder2[Int]](SJConfig.withTryFailureHandling(TryPolicy.AS_NULL)) val js = sj.toJson(inst) js should matchJson("""{"a":[1,null,3],"b":[null,0]}""") sj.fromJson(js) shouldEqual (TryHolder2[Int](List(Success(1), null, Success(3)), (null, Success(0)))) From efad12a6c0804d078146e7b36cfde1a1bbdb6eab Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Sat, 4 May 2024 01:01:43 -0500 Subject: [PATCH 60/65] doc tweak --- README.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d6e17850..0eb73703 100644 --- a/README.md +++ b/README.md @@ -27,10 +27,15 @@ Include the following in your build.sbt: libraryDependencies ++= Seq("co.blocke" %% "scalajack" % SJ_VERSION) ``` Now you're good to go! Let's use ScalaJack in your project to serialize/de-serialize a case class object into JSON: +**File1.scala** ```scala -import co.blocke.scalajack.* - +// Classes must be defined in a different file from where ScalaJack is called. +// This is a Scala macro requirement, not a ScalaJack "ism" case class Person(name: String, age: Int) +``` +**File2.scala** +```scala +import co.blocke.scalajack.* given sjPerson: ScalaJack[Person] = sjCodecOf[Person] // create a re-usable Person codec ... @@ -86,7 +91,7 @@ This means you will be doing more re-compiling with macro-based code than you wo * [Null and None treatment](doc/nullAndNone.md) * [NeoType Support](doc/neotype.md) * [Union type](doc/union.md) -* [Gimme Speed!](doc/speed.md) +* [Gimme Speed!](benchmark/README.md) ### Notes: From 3d41c1a87125ccdffbf7101a922673d3fd6d2499 Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Fri, 17 May 2024 00:24:49 -0500 Subject: [PATCH 61/65] initial msgpack support --- README.md | 25 +- benchmark/README.md | 33 +- benchmark/build.sbt | 2 +- .../src/main/scala/co.blocke/Benchmark.scala | 2 + .../src/main/scala/co.blocke/MsgPack.scalax | 19 + .../src/main/scala/co.blocke/ScalaJack.scala | 18 +- build.sbt | 1 + .../scala/co.blocke.scalajack/ScalaJack.scala | 21 +- .../json/JsonCodecMaker.scala | 3 +- .../co.blocke.scalajack/json/package.scala | 24 - .../msgpack/MsgPackCodec.scala | 9 + .../msgpack/MsgPackCodecMaker.scala | 722 ++++++++++++++++++ .../msgpack/MsgPackError.scala | 32 + .../msgpack/package.scalax | 50 ++ .../scala/co.blocke.scalajack/package.scala | 25 + .../scala/co.blocke.scalajack/run/Play.scala | 21 + .../scala/co.blocke.scalajack/run/Play.scalax | 28 - .../run/{Record.scalax => Record.scala} | 0 18 files changed, 943 insertions(+), 92 deletions(-) create mode 100644 benchmark/src/main/scala/co.blocke/MsgPack.scalax create mode 100644 src/main/scala/co.blocke.scalajack/msgpack/MsgPackCodec.scala create mode 100644 src/main/scala/co.blocke.scalajack/msgpack/MsgPackCodecMaker.scala create mode 100644 src/main/scala/co.blocke.scalajack/msgpack/MsgPackError.scala create mode 100644 src/main/scala/co.blocke.scalajack/msgpack/package.scalax create mode 100644 src/main/scala/co.blocke.scalajack/package.scala create mode 100644 src/main/scala/co.blocke.scalajack/run/Play.scala delete mode 100644 src/main/scala/co.blocke.scalajack/run/Play.scalax rename src/main/scala/co.blocke.scalajack/run/{Record.scalax => Record.scala} (100%) diff --git a/README.md b/README.md index 0eb73703..bc8d89c9 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,8 @@ ScalaJack 8 is an all-new ScalaJack serializer implemenation built on Scala 3. For Scala 2.13 ScalaJack, please use the frozen version 6.2.0. ScalaJack 8 is built using Scala 3.4.1 on JDK 21 LTS version. -ScalaJack is a very fast, seamless serialization engine for unstructured data types (JSON, HOCON, Yaml, and MsgPack) designed to require a bare minimum of extra code -to serialize a class. +ScalaJack is a very fast, seamless serialization engine for unstructured data types designed to require a bare minimum of extra code +to serialize a class. ScalaJack supports JSON and MsgPack as part of its focus on over-the-wire data and message/event transport. Advanced Features: @@ -27,14 +27,11 @@ Include the following in your build.sbt: libraryDependencies ++= Seq("co.blocke" %% "scalajack" % SJ_VERSION) ``` Now you're good to go! Let's use ScalaJack in your project to serialize/de-serialize a case class object into JSON: -**File1.scala** ```scala -// Classes must be defined in a different file from where ScalaJack is called. -// This is a Scala macro requirement, not a ScalaJack "ism" +// File1.scala case class Person(name: String, age: Int) -``` -**File2.scala** -```scala + +// File2.scala import co.blocke.scalajack.* given sjPerson: ScalaJack[Person] = sjCodecOf[Person] // create a re-usable Person codec @@ -45,12 +42,16 @@ sjPerson.fromJson(js) // re-constitutes original Person ``` Couldn't be simpler! +| **NOTE:** Classes must be defined in a different file from where ScalaJack is called. +| This is a Scala macro requirement, not a ScalaJack "ism" + ### A word about performance... Compared to pre-8.0 ScalaJack, which used Scala 2.x runtime reflection, ScalaJack is dramatically faster in almost every case. How's this work? ScalaJack 8 uses macros, that at compile-time generate all the serialization code for you (the codecs). It's very much like writing hand-tooled, field-by-field serialization code yourself, except ScalaJack does it at compile-time. Wherever you see ```sjCodecOf``` is where the compiler will generate all the serialization code. **(That also means try not to use sjCodecOf more than once for any given class or you'll generate a lot of redundant code!)** +### Easy codecs You only need to worry about generating codecs for your top-most level classes. Some serialization libraries require all nested classes in an object hierarchy to be -specifically called out for code generation. ScalaJack doesn't require this. For example: +specifically called out for code generation, which can get pretty burdensome. ScalaJack doesn't require this. For example: ```scala case class Dog(name: String, numLegs: Int) @@ -59,7 +60,7 @@ case class Person(name: String, age: Int, dog: Dog) // create a re-usable Person codec (includes Dog for free!) given sjPerson: ScalaJack[Person] = sjCodecOf[Person] ``` -In this example, the contained Dog class is automatically detected and genrated by ScalaJack, so if all you care about is Person, that's the only codec you need. +In this example, the contained Dog class is automatically detected and genrated by ScalaJack, so if all you care about is Person, and would never serialize a Dog as a top-level value, then Persion is the only codec you need. ### A word about macros... @@ -74,9 +75,9 @@ given sjFoo: ScalaJack[Foo] = sjCodecOf[Foo] val js = sjFoo.fromJson(someJson) ``` -In a non-macro program (e.g. something using Scala 2 runtime reflection) if you update Foo in File1.scala you naturally expect sbt to re-compile this file, and anything that depends on Foo, and the changes will be picked up in your program, and all will be well. +In a non-macro program (e.g. something using Scala 2 runtime reflection) let's say you add a new field to class Foo in File1.scala. You naturally expect sbt to re-compile this file, and anything that depends on Foo, and the changes will be picked up in your program, and all will be well. -That's **not** necessarily what happens with macros! Remember, the macro code is run at compile-time. File2.scala needs to be re-compiled because the macro needs to be re-run to pick up your changes to Foo class in File1.scala. **Unfortunately sbt doesn't pick up this dependency!** If you don't know any better you'll just re-run your program after a change to File1.scala, like normal, and get a **spectacular exception with exotic errors** that won't mean much to you. The simple, but non-intuitive, solution is you need to also recompile File2.scala. +That's **not** necessarily what happens with macros! Remember, the macro code is run/expnded at compile-time. File2.scala needs to be re-compiled because the macro that gets expanded at sjCodecOf[Foo] needs to be re-generated to pick up your changes to Foo class in File1.scala. **Unfortunately sbt cna't detect up this dependency!** If you don't know any better you'll just re-run your program after a change to File1.scala, like normal, and you'll get a spectacular exception with exotic errors that won't mean much to you. The simple, but non-intuitive, solution is you need to also recompile File2.scala. This means you will be doing more re-compiling with macro-based code than you would without the macros. It's an unfortunate cost of inconvenience, but the payoff is a *dramatic* gain in speed at runtime, and in the case of reflection in Scala 3, using macros is the only way to accomplish reflection, so there really isn't an alternative. diff --git a/benchmark/README.md b/benchmark/README.md index 0961ff03..49115186 100644 --- a/benchmark/README.md +++ b/benchmark/README.md @@ -1,8 +1,6 @@ # Performance -JSON serialization benchmarks I found in various project repos often measured (IMO) silly things like how fast -a parser could handle a small list of Int. For this benchmark I used a small, but slightly more representative model. -It has some nested objects and collections that make it a more interesting test. +JSON serialization benchmarks I found in various project repos often measured (IMO) silly things like how fast a parser could handle a small list of Int. For this benchmark I used a small, but slightly more representative model. It has some nested objects and collections that make it a more interesting test. The test is run via jmh. The JVM is **stock**--not tuned to within an inch of its life, to be a more realistic use case. @@ -50,13 +48,12 @@ processing using sj[Foo](JsonConfig.withSuppressEscapedStrings()) ``` Its for when you're 100% sure there's 0 chance of any double-quotes, newlines, tabs, or -any other non-character/digit unicode special characters in your String values, and you need the -maximum possible performance. +any other non-character/digit unicode special characters in your String values that require escaping. When you need the maximum possible performance. ### Interpretation -Performance for ScalaJack has been a journey. ScalaJack is a mature product--over 10 yrs old. -Long ago it was quite fast vs its competition, but its performance lagged as its peers improved, +Performance for ScalaJack has been a journey. ScalaJack is a mature product--over 10 yrs old! +Long ago it was quite fast vs its competition, but its performance lagged badly as its peers improved, to the point that it became one of the slower serialization libraries. ScalaJack 8 changes that! I was sampling and testing against a collection of popular serializers for Scala util @@ -71,23 +68,22 @@ is heavily informed from Jsoniter, so I'll post their licence here: There are a number of optimizations and design choices I elected not to bring over from Jsoniter, in many cases because ScalaJack doesn't need them for its intended feature set. -Of course ScalaJack utilizes our own macro-driven scala-reflection library to great effect, -which Jsoniter does not. +Of course ScalaJack utilizes our own scala-reflection library to great effect, which Jsoniter does not. -Jsoniter achieves its neck-breaking speed by going deep--very deep into macro code +Jsoniter achieves its neck-breaking speed by going deep--very deep--into macro code generation. They also use a lot of low-level byte arrays and bitwise operators to improve on the standard library functions everyone else uses. It works. ### Technical Notes -Achieving extreme speed for ScalaJack 8 was weeks of learning, trial, error, +Achieving extreme speed for ScalaJack 8 was months of learning, trial, error, and re-writes. I studied Jsoniter, Circe, ZIO Json, and others to learn optimizations. The tough news for anyone wanting to duplicate this kind of performance in your own code is that there isn't one magic trick to achieve maximum performance. It's a basket of techniques, each achieving small marginal gains that add up, and you must decide when enough is enough for you. Here's a partial list of learnings incorporated into ScalaJack 8: -* Being careful when using .asInstanceOf[]... in fact try to avoid it wherever possible +* Being careful when using .asInstanceOf[]. In fact, try to avoid it wherever possible as it messes up CPU cache, harming performance. This means a lot of very careful type management, and its why you see the RTypeRefs from scala-reflection are now all typed in the latest version @@ -106,16 +102,15 @@ enough is enough for you. Here's a partial list of learnings incorporated into * Be mindful of what code your macros generate! You can write a macro straight from a blog example and get something working, but you may be disappointed if maximizing runtime - speed is your goal. The generated code might look kludgy. Rework your macros carefully - until the generated code is as smooth as you might write by hand. + speed is your goal. The generated code may look kludgy and have extra cruft in it. Rework your + macros carefully until the generated code is as smooth as you might write by hand. After all the performance tunings and learnings, I was able to meet or beat Jsoniter for writing speed. For reading I made a number of very substantial improvements, but there is still a substantial performance gap between Jsoniter's reads and ScalaJack's, and for the life of me I can't -figure out what's driving those gains! The generated code is very similar. Json parsing should +figure out what's driving that difference. The generated code is very similar. Json parsing should be similar--in fact in some ways ScalaJack's should be faster. Although micro-benchmarks indicate match/case is significantly slower in generated code than if/than/else, but in practice replacing -these with if/else didn't gain ScalaJack a thing--which I find vexing. Jsoniter has some tricky -ByteArrayAccess code that looks very low-level and clever, but when I benchmarked it, the gains -seemed nominal to none in my use case. I dunno--If anyone has any ideas, please drop a comment -in the Issues in repo! \ No newline at end of file +these with if/else didn't gain ScalaJack a thing. Jsoniter has some tricky ByteArrayAccess code +that looks very low-level and clever, but when I benchmarked it, the gains seemed nominal to none +in my use case. I dunno--If anyone has any ideas, please drop a comment in the Issues in repo! \ No newline at end of file diff --git a/benchmark/build.sbt b/benchmark/build.sbt index 88646a67..d775fdfc 100644 --- a/benchmark/build.sbt +++ b/benchmark/build.sbt @@ -37,7 +37,7 @@ lazy val benchmark = project libraryDependencies ++= Seq( "org.playframework" %% "play-json" % "3.0.1", "io.argonaut" %% "argonaut" % "6.3.9", - "co.blocke" %% "scalajack" % "ce27f8_unknown", + "co.blocke" %% "scalajack" % "efad12_unknown", "dev.zio" %% "zio-json" % "0.6.1", "org.typelevel" %% "fabric-core" % "1.12.6", "org.typelevel" %% "fabric-io" % "1.12.6", diff --git a/benchmark/src/main/scala/co.blocke/Benchmark.scala b/benchmark/src/main/scala/co.blocke/Benchmark.scala index cfb05f5a..3a5e3833 100644 --- a/benchmark/src/main/scala/co.blocke/Benchmark.scala +++ b/benchmark/src/main/scala/co.blocke/Benchmark.scala @@ -43,6 +43,7 @@ trait HandTooledWritingBenchmark { @BenchmarkMode(Array(Mode.Throughput)) @OutputTimeUnit(TimeUnit.SECONDS) class ReadingBenchmark + // extends MsgPack.MsgPackReadingBenchmark extends ScalaJackZ.ScalaJackReadingBenchmark // with CirceZ.CirceReadingBenchmark // extends JsoniterZ.JsoniterReadingBenchmark @@ -54,6 +55,7 @@ class ReadingBenchmark @BenchmarkMode(Array(Mode.Throughput)) @OutputTimeUnit(TimeUnit.SECONDS) class WritingBenchmark + // extends MsgPack.MsgPackWritingBenchmark // extends HandTooledWritingBenchmark // extends CirceZ.CirceWritingBenchmark extends ScalaJackZ.ScalaJackWritingBenchmark diff --git a/benchmark/src/main/scala/co.blocke/MsgPack.scalax b/benchmark/src/main/scala/co.blocke/MsgPack.scalax new file mode 100644 index 00000000..32802441 --- /dev/null +++ b/benchmark/src/main/scala/co.blocke/MsgPack.scalax @@ -0,0 +1,19 @@ +package co.blocke + +import org.openjdk.jmh.annotations._ + +object MsgPack: + import co.blocke.scalajack.ScalaJack.* + import co.blocke.scalajack.* + + given sj: ScalaJack[co.blocke.Record2] = sjCodecOf[co.blocke.Record2] + + trait MsgPackReadingBenchmark{ + @Benchmark + def readRecordScalaJack = sj.fromMsgPack(jsData2) + } + + trait MsgPackWritingBenchmark { + @Benchmark + def writeRecordScalaJack = sj.toMsgPack(record) + } diff --git a/benchmark/src/main/scala/co.blocke/ScalaJack.scala b/benchmark/src/main/scala/co.blocke/ScalaJack.scala index cddf4536..1e9c6e28 100644 --- a/benchmark/src/main/scala/co.blocke/ScalaJack.scala +++ b/benchmark/src/main/scala/co.blocke/ScalaJack.scala @@ -6,16 +6,28 @@ object ScalaJackZ: import co.blocke.scalajack.ScalaJack.* import co.blocke.scalajack.* - implicit val blah: ScalaJack[co.blocke.Record2] = sjCodecOf[co.blocke.Record2] + given sj: ScalaJack[co.blocke.Record2] = sjCodecOf[co.blocke.Record2] + + // implicit val blah: ScalaJack[co.blocke.Record2] = sjCodecOf[co.blocke.Record2] trait ScalaJackReadingBenchmark{ @Benchmark - def readRecordScalaJack = ScalaJack[co.blocke.Record2].fromJson(jsData2) + def readRecordScalaJack = sj.fromJson(jsData2) } trait ScalaJackWritingBenchmark { @Benchmark - def writeRecordScalaJack = ScalaJack[co.blocke.Record2].toJson(record) + def writeRecordScalaJack = sj.toJson(record) + } + + trait MsgPackReadingBenchmark{ + @Benchmark + def readRecordScalaJack = sj.fromMsgPack(jsData2) + } + + trait MsgPackWritingBenchmark { + @Benchmark + def writeRecordScalaJack = sj.toMsgPack(record) } /* diff --git a/build.sbt b/build.sbt index 8a564953..c251b232 100644 --- a/build.sbt +++ b/build.sbt @@ -38,6 +38,7 @@ lazy val root = project "co.blocke" %% "scala-reflection" % "2.0.6", "org.apache.commons" % "commons-text" % "1.11.0", "io.github.kitlangton" %% "neotype" % "0.0.9", + "org.msgpack" % "msgpack-core" % "0.9.8", "org.scalatest" %% "scalatest" % "3.2.17" % Test, "org.json4s" %% "json4s-core" % "4.0.6" % Test, "org.json4s" %% "json4s-native" % "4.0.6" % Test diff --git a/src/main/scala/co.blocke.scalajack/ScalaJack.scala b/src/main/scala/co.blocke.scalajack/ScalaJack.scala index 84abc10a..496d5974 100644 --- a/src/main/scala/co.blocke.scalajack/ScalaJack.scala +++ b/src/main/scala/co.blocke.scalajack/ScalaJack.scala @@ -6,8 +6,9 @@ import scala.quoted.* import quoted.Quotes import json.* +import msgpack.* -case class ScalaJack[T](jsonCodec: JsonCodec[T]): // extends JsonCodec[T] //with YamlCodec with MsgPackCodec +case class ScalaJack[T](jsonCodec: JsonCodec[T], msgPackCodec: MsgPackCodec[T]): // extends JsonCodec[T] //with YamlCodec with MsgPackCodec def fromJson(js: String): T = jsonCodec.decodeValue(reading.JsonSource(js)) @@ -16,6 +17,18 @@ case class ScalaJack[T](jsonCodec: JsonCodec[T]): // extends JsonCodec[T] //with jsonCodec.encodeValue(a, out.clear()) out.result + val outArray = new org.msgpack.core.buffer.ArrayBufferOutput() + def toMsgPack(a: T): Array[Byte] = + outArray.clear() + val outMP = org.msgpack.core.MessagePack.newDefaultPacker(outArray) + msgPackCodec.encodeValue(a, outMP) + outMP.close() + outArray.toByteArray + + def fromMsgPack(a: Array[Byte]): T = + val inMP = org.msgpack.core.MessagePack.newDefaultUnpacker(a) + msgPackCodec.decodeValue(inMP) + // --------------------------------------- object ScalaJack: @@ -28,8 +41,9 @@ object ScalaJack: import quotes.reflect.* val classRef = ReflectOnType[T](quotes)(TypeRepr.of[T], true)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) val jsonCodec = JsonCodecMaker.generateCodecFor(classRef, SJConfig) + val mpCodec = MsgPackCodecMaker.generateCodecFor(classRef, SJConfig) - '{ ScalaJack($jsonCodec) } + '{ ScalaJack($jsonCodec, $mpCodec) } // ----- Use given JsonConfig inline def sjCodecOf[T](inline cfg: SJConfig): ScalaJack[T] = ${ codecOfImplWithConfig[T]('cfg) } @@ -38,4 +52,5 @@ object ScalaJack: val cfg = summon[FromExpr[SJConfig]].unapply(cfgE) val classRef = ReflectOnType[T](quotes)(TypeRepr.of[T], true)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) val jsonCodec = JsonCodecMaker.generateCodecFor(classRef, cfg.getOrElse(SJConfig)) - '{ ScalaJack($jsonCodec) } + val mpCodec = MsgPackCodecMaker.generateCodecFor(classRef, SJConfig) + '{ ScalaJack($jsonCodec, $mpCodec) } diff --git a/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala b/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala index 8864bc4b..efb55add 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala @@ -1211,10 +1211,9 @@ object JsonCodecMaker: // --------------------------------------------------------------------------------------------- def genReadVal[T: Type]( - // default: Expr[T], // needed? This should already be in ref... ref: RTypeRef[T], in: Expr[JsonSource], - inTuple: Boolean = false, // not sure if needed... + inTuple: Boolean = false, isMapKey: Boolean = false )(using Quotes): Expr[T] = val methodKey = MethodKey(ref, false) diff --git a/src/main/scala/co.blocke.scalajack/json/package.scala b/src/main/scala/co.blocke.scalajack/json/package.scala index f68c898c..af721926 100644 --- a/src/main/scala/co.blocke.scalajack/json/package.scala +++ b/src/main/scala/co.blocke.scalajack/json/package.scala @@ -10,26 +10,6 @@ opaque type RawJson = String val BUFFER_EXCEEDED: Char = 7 // Old "BELL" ASCII value, used as a marker when we've run off the end of the known world val END_OF_STRING: Char = 3 -inline def lastPart(n: String) = n.split('.').last.stripSuffix("$") -inline def allButLastPart(n: String) = - val l = n.lastIndexOf('.') - if l >= 0 then n.substring(0, l) - else n - -val random = new scala.util.Random() -def scramble(hash: Int): String = - val last5 = f"$hash%05d".takeRight(5) - val digits = (1 to 5).map(_ => random.nextInt(10)) - if digits(0) % 2 == 0 then s"${last5(0)}${digits(0)}${last5(1)}${digits(1)}${last5(2)}-${digits(2)}${last5(3)}${digits(3)}-${last5(4)}${digits(4)}A" - else s"${digits(0)}${last5(0)}${digits(1)}${last5(1)}${digits(2)}-${last5(2)}${digits(3)}${last5(3)}-${digits(4)}${last5(4)}B" - -def descramble(in: String, hash: Int): Boolean = - val last5 = f"$hash%05d".takeRight(5) - in.last match - case 'A' if in.length == 13 => "" + in(0) + in(2) + in(4) + in(7) + in(10) == last5 - case 'B' if in.length == 13 => "" + in(1) + in(3) + in(6) + in(8) + in(11) == last5 - case _ => false - def ofOption[T](xs: Option[Expr[T]])(using Type[T])(using q: Quotes): Expr[Option[T]] = import q.reflect.* if xs.isEmpty then '{ None } @@ -40,7 +20,3 @@ def ofOptional[T](xs: Optional[Expr[T]])(using Type[T])(using q: Quotes): Expr[O import q.reflect.* if xs.isEmpty then '{ Optional.empty } else '{ Optional.of(${ xs.get }) } - -enum Language { - case Scala, Java -} diff --git a/src/main/scala/co.blocke.scalajack/msgpack/MsgPackCodec.scala b/src/main/scala/co.blocke.scalajack/msgpack/MsgPackCodec.scala new file mode 100644 index 00000000..77341fc2 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/msgpack/MsgPackCodec.scala @@ -0,0 +1,9 @@ +package co.blocke.scalajack +package msgpack + +import org.msgpack.core.{MessagePacker, MessageUnpacker} + +trait MsgPackCodec[A] { + def encodeValue(in: A, out: MessagePacker): Unit + def decodeValue(in: MessageUnpacker): A +} diff --git a/src/main/scala/co.blocke.scalajack/msgpack/MsgPackCodecMaker.scala b/src/main/scala/co.blocke.scalajack/msgpack/MsgPackCodecMaker.scala new file mode 100644 index 00000000..e14f3164 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/msgpack/MsgPackCodecMaker.scala @@ -0,0 +1,722 @@ +package co.blocke.scalajack +package msgpack + +import scala.collection.mutable.ArrayBuffer +import org.msgpack.core.{MessagePacker, MessageUnpacker, MessageFormat} +import org.msgpack.value.ValueType +import org.msgpack.core.MessagePack.PackerConfig +import org.msgpack.core.buffer.ArrayBufferOutput +import co.blocke.scala_reflection.RTypeRef +import co.blocke.scala_reflection.reflect.rtypeRefs.* +import scala.reflect.ClassTag +import scala.util.Success +import scala.quoted.* + +object MsgPackCodecMaker: + + def generateCodecFor[T](ref: RTypeRef[T], cfg: SJConfig)(using q: Quotes)(using tt: Type[T]) = + import q.reflect.* + + // Cache generated method Symbols + an array of the generated functions (DefDef) + case class MethodKey(ref: RTypeRef[?], isNonConstructorFields: Boolean) + val writeMethodSyms = new scala.collection.mutable.HashMap[MethodKey, Symbol] + val writeMethodDefs = new scala.collection.mutable.ArrayBuffer[DefDef] + val readMethodSyms = new scala.collection.mutable.HashMap[MethodKey, Symbol] + val readMethodDefs = new scala.collection.mutable.ArrayBuffer[DefDef] + + inline def changeFieldName(fr: FieldInfoRef): String = fr.annotations.get("co.blocke.scalajack.Change").flatMap(_.get("name")).getOrElse(fr.name) + + def makeWriteFn[U: Type](methodKey: MethodKey, arg: Expr[U], out: Expr[MessagePacker])(f: (Expr[U], Expr[MessagePacker]) => Expr[Unit]): Expr[Unit] = + // Get a symbol, if one already created for this key... else make one. + Apply( + Ref( + writeMethodSyms.getOrElse( + methodKey, { + val sym = Symbol.newMethod( + Symbol.spliceOwner, + "w" + writeMethodSyms.size, // 'w' is for Writer! + MethodType(List("in", "out"))(_ => List(TypeRepr.of[U], TypeRepr.of[MessagePacker]), _ => TypeRepr.of[Unit]) + ) + writeMethodSyms.update(methodKey, sym) + writeMethodDefs += DefDef( + sym, + params => { + val List(List(in, out)) = params + Some(f(in.asExprOf[U], out.asExprOf[MessagePacker]).asTerm.changeOwner(sym)) + } + ) + sym + } + ) + ), + List(arg.asTerm, out.asTerm) + ).asExprOf[Unit] + + def makeReadFn[U: Type](methodKey: MethodKey, in: Expr[MessageUnpacker])(f: Expr[MessageUnpacker] => Expr[U])(using Quotes)(using Type[MessageUnpacker]): Expr[Unit] = + readMethodSyms.getOrElse( + methodKey, { + val sym = Symbol.newMethod( + Symbol.spliceOwner, + "r" + readMethodSyms.size, + MethodType(List("in"))(_ => List(TypeRepr.of[MessageUnpacker]), _ => TypeRepr.of[U]) + // (_ => List(input_params,...), _ => resultType) + ) + readMethodSyms.update(methodKey, sym) + readMethodDefs += DefDef( + sym, + params => { + val List(List(in)) = params + Some(f(in.asExprOf[MessageUnpacker]).asTerm.changeOwner(sym)) + } + ) + } + ) + '{} + + val classFieldMatrixSyms = new scala.collection.mutable.HashMap[MethodKey, Symbol] + val classFieldMatrixValDefs = new scala.collection.mutable.ArrayBuffer[ValDef] + + def makeClassFieldMatrixValDef(methodKey: MethodKey, className: String, fieldNames: Array[String])(using Quotes): Expr[Unit] = + classFieldMatrixSyms.getOrElse( + methodKey, { + val sym = Symbol.newVal( + Symbol.spliceOwner, + s"__$className" + "_fields", + TypeRepr.of[Map[String,Int]], + Flags.EmptyFlags, + Symbol.noSymbol + ) + classFieldMatrixSyms.update(methodKey, sym) + val names = Expr(fieldNames) + classFieldMatrixValDefs += ValDef(sym, Some('{ $names.zipWithIndex.toMap }.asTerm)) + } + ) + '{} + + def maybeWrite[T](label: String, aE: Expr[T], ref: RTypeRef[T], out: Expr[MessagePacker], cfg: SJConfig): Expr[Unit] = + val labelE = Expr(label) + _maybeWrite[T]( + '{ $out.packString($labelE) }, + aE, + ref, + out, + cfg + ) + + def _maybeWrite[T](prefix: Expr[Unit], aE: Expr[T], ref: RTypeRef[T], out: Expr[MessagePacker], cfg: SJConfig): Expr[Unit] = + ref match + case _ => + ref.refType match + case '[u] => + '{ + $prefix + ${ genWriteVal[u](aE.asExprOf[u], ref.asInstanceOf[RTypeRef[u]], out) } + } + + def lrHasOptionChild(lr: LeftRightRef[?]): (String, Language) = + lr.rightRef match + case t: ScalaOptionRef[?] => ("r", Language.Scala) + case t: JavaOptionalRef[?] => ("r", Language.Java) + case t: LeftRightRef[?] => + val (recipe, lang) = lrHasOptionChild(t) + ("r" + recipe, lang) + case _ => + lr.leftRef match + case t: ScalaOptionRef[?] => ("l", Language.Scala) + case t: JavaOptionalRef[?] => ("l", Language.Java) + case t: LeftRightRef[?] => + val (recipe, lang) = lrHasOptionChild(t) + ("l" + recipe, lang) + case _ => ("", Language.Scala) + + def tryHasOptionChild(t: TryRef[?]): (Boolean, Language) = + t.tryRef match + case o: ScalaOptionRef[?] => (true, Language.Scala) + case o: JavaOptionalRef[?] => (true, Language.Java) + case lr: LeftRightRef[?] => + val (recipe, lang) = lrHasOptionChild(lr) + (recipe.length > 0, lang) + case _ => (false, Language.Scala) + + def genEncFnBody[T](r: RTypeRef[?], aE: Expr[T], out: Expr[MessagePacker], emitDiscriminator: Boolean = false, inTuple: Boolean = false)(using Quotes): Expr[Unit] = + r.refType match + case '[b] => + r match + case t: ArrayRef[?] => + makeWriteFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => + t.elementRef.refType match + case '[e] => + val tin = in.asInstanceOf[Expr[Array[e]]] + '{ + if $tin == null then $out.packNil() + else + $out.packArrayHeader($tin.length) + $tin.foreach { i => + ${ genWriteVal('{ i }, t.elementRef.asInstanceOf[RTypeRef[e]], out) } + } + } + } + case t: SeqRef[?] => + makeWriteFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => + t.elementRef.refType match + case '[e] => + val tin = if t.isMutable then in.asExprOf[scala.collection.mutable.Seq[e]] else in.asExprOf[Seq[e]] + '{ + if $tin == null then $out.packNil() + else + $out.packArrayHeader($tin.length) + $tin.foreach { i => + ${ genWriteVal('{ i }, t.elementRef.asInstanceOf[RTypeRef[e]], out) } + } + } + } + case t: ScalaClassRef[?] => + makeWriteFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => + val body = { + val eachField = t.fields.map { f => + f.fieldRef.refType match + case '[z] => + val fieldValue = Select.unique(in.asTerm, f.name).asExprOf[z] + val fieldName = changeFieldName(f) + maybeWrite[z](fieldName, fieldValue, f.fieldRef.asInstanceOf[RTypeRef[z]], out, cfg) + } + if emitDiscriminator then + val cname = cfg.typeHintPolicy match + case TypeHintPolicy.SIMPLE_CLASSNAME => Expr(lastPart(t.name)) + case TypeHintPolicy.SCRAMBLE_CLASSNAME => '{ scramble(${ Expr(lastPart(t.name).hashCode) }) } + case TypeHintPolicy.USE_ANNOTATION => + Expr(t.annotations.get("co.blocke.scalajack.TypeHint").flatMap(_.get("hintValue")).getOrElse(lastPart(t.name))) + val withDisc = '{ + $out.packString(${ Expr(cfg.typeHintLabel) }) + $out.packString($cname) + } +: eachField + Expr.block(withDisc.init, withDisc.last) + else if eachField.length == 1 then eachField.head + else Expr.block(eachField.init, eachField.last) + } + + val fieldCount = Expr(if emitDiscriminator then t.fields.size + 1 else t.fields.size) + if !t.isCaseClass && cfg._writeNonConstructorFields then + val eachField = t.nonConstructorFields.map { f => + f.fieldRef.refType match + case '[e] => + val fieldValue = Select.unique(in.asTerm, f.getterLabel).asExprOf[e] + val fieldName = changeFieldName(f) + maybeWrite[e](fieldName, fieldValue, f.fieldRef.asInstanceOf[RTypeRef[e]], out, cfg) + } + val subBody = eachField.length match + case 0 => '{} + case 1 => eachField.head + case _ => Expr.block(eachField.init, eachField.last) + '{ + if $in == null then $out.packNil() + else + $out.packMapHeader( $fieldCount ) + $body + $subBody + } + else + '{ + if $in == null then $out.packNil() + else + $out.packMapHeader( $fieldCount ) + $body + } + } + + def genDecFnBody[T: Type](r: RTypeRef[?], in: Expr[MessageUnpacker])(using Quotes): Expr[Unit] = + + def typeArgs(tpe: TypeRepr): List[TypeRepr] = tpe match + case AppliedType(_, typeArgs) => typeArgs.map(_.dealias) + case _ => Nil + + r.refType match // refType is Type[r.R] + case '[b] => + r match + case t: ScalaClassRef[?] => + makeReadFn[T](MethodKey(t, false), in)(in => + // Generate vars for each contractor argument, populated with either a "unit" value (eg 0, "") or given default value + val tpe = TypeRepr.of[b] + val classCompanion = tpe.typeSymbol.companionClass + val companionModule = tpe.typeSymbol.companionModule + val totalRequired = math.pow(2, t.fields.length).toInt - 1 + var required = 0 + val reqSym = Symbol.newVal(Symbol.spliceOwner, "required", TypeRepr.of[Int], Flags.Mutable, Symbol.noSymbol) + val allFieldNames = Expr(t.fields.map(f => changeFieldName(f)).toArray) // Used for missing required field error + + val together = t.fields.map { oneField => + oneField.fieldRef.refType match { + case '[f] => + val dvMembers = classCompanion.methodMember("$lessinit$greater$default$" + (oneField.index + 1)) + val sym = Symbol.newVal(Symbol.spliceOwner, "_" + oneField.name, TypeRepr.of[f], Flags.Mutable, Symbol.noSymbol) + val fieldSymRef = Ident(sym.termRef) + val reqBit = Expr(math.pow(2, oneField.index).toInt) + val fieldName = Expr(oneField.name) + + val caseDef = CaseDef( + Literal(IntConstant(oneField.index)), + None, + '{ + if (${ Ref(reqSym).asExprOf[Int] } & $reqBit) != 0 then + ${ Assign(Ref(reqSym), '{ ${ Ref(reqSym).asExprOf[Int] } ^ $reqBit }.asTerm).asExprOf[Unit] } + ${ Assign(fieldSymRef, genReadVal[f](oneField.fieldRef.asInstanceOf[RTypeRef[f]], in).asTerm).asExprOf[Unit] } + else throw new MsgPackParseError("Duplicate field " + $fieldName) + }.asTerm + ) + if dvMembers.isEmpty then + // no default... required? Not if Option/Optional, or a collection + val unitVal = oneField.fieldRef match { + case _: OptionRef[?] => + oneField.fieldRef.unitVal.asTerm // not required + case _: AnyRef => + oneField.fieldRef.unitVal.asTerm // not required + case r: LeftRightRef[?] if r.lrkind == LRKind.EITHER => // maybe required + val (optionRecipe, lang) = lrHasOptionChild(r) + if optionRecipe.length == 0 then + required = required | math.pow(2, oneField.index).toInt // required + oneField.fieldRef.unitVal.asTerm + else + val recipeE = Expr(optionRecipe) + if lang == Language.Scala then + '{ + $recipeE.foldRight(None: Any)((c, acc) => if c == 'r' then Right(acc) else Left(acc)).asInstanceOf[f] + }.asTerm + else + '{ + $recipeE.foldRight(java.util.Optional.empty: Any)((c, acc) => if c == 'r' then Right(acc) else Left(acc)).asInstanceOf[f] + }.asTerm + case r: LeftRightRef[?] => // maybe required + val (optionRecipe, lang) = lrHasOptionChild(r) + if optionRecipe.length == 0 then // no Option children -> required + required = required | math.pow(2, oneField.index).toInt // required + oneField.fieldRef.unitVal.asTerm + else // at least one Option child -> optional + if lang == Language.Scala then '{ None }.asTerm + else '{ java.util.Optional.empty.asInstanceOf[f] }.asTerm + case y: TryRef[?] => + tryHasOptionChild(y) match + case (true, Language.Scala) => '{ Success(None) }.asTerm + case (true, Language.Java) => '{ Success(java.util.Optional.empty).asInstanceOf[f] }.asTerm + case _ => + required = required | math.pow(2, oneField.index).toInt // required + oneField.fieldRef.unitVal.asTerm + case _ => + required = required | math.pow(2, oneField.index).toInt // required + oneField.fieldRef.unitVal.asTerm + } + (ValDef(sym, Some(unitVal)), caseDef, fieldSymRef) + else + val methodSymbol = dvMembers.head + val dvSelectNoTArgs = Ref(companionModule).select(methodSymbol) + val dvSelect = methodSymbol.paramSymss match + case Nil => dvSelectNoTArgs + case List(params) if (params.exists(_.isTypeParam)) => + typeArgs(tpe) match + case Nil => ??? // throw JsonParseError("Expected an applied type", ???) + case typeArgs => TypeApply(dvSelectNoTArgs, typeArgs.map(Inferred(_))) + case _ => ??? // fail(s"Default method for ${symbol.name} of class ${tpe.show} have a complex " + + (ValDef(sym, Some(dvSelect)), caseDef, fieldSymRef) + } + } + val reqVarDef = ValDef(reqSym, Some(Literal(IntConstant(totalRequired)))) + val (varDefs, caseDefs, idents) = together.unzip3 + val caseDefsWithFinal = caseDefs :+ CaseDef(Wildcard(), None, '{ $in.skipValue() }.asTerm) // skip values of unrecognized fields + + val argss = List(idents) + val primaryConstructor = tpe.classSymbol.get.primaryConstructor + val constructorNoTypes = Select(New(Inferred(tpe)), primaryConstructor) + val constructor = typeArgs(tpe) match + case Nil => constructorNoTypes + case typeArgs => TypeApply(constructorNoTypes, typeArgs.map(Inferred(_))) + val instantiateClass = argss.tail.foldLeft(Apply(constructor, argss.head))((acc, args) => Apply(acc, args)) + + val exprRequired = Expr(required) + + makeClassFieldMatrixValDef(MethodKey(t, false), t.name.replaceAll("\\.", "_"), t.fields.map(f => changeFieldName(f)).toArray) + val fieldMatrixSym = classFieldMatrixSyms(MethodKey(t, false)).asInstanceOf[Symbol] + + var finalVarDefs = varDefs + val parseLoop = + if !cfg._writeNonConstructorFields || t.nonConstructorFields.isEmpty then + // When we don't care about non-constructor fields + '{ + $in.getNextFormat().getValueType() match + case ValueType.NIL => null + case ValueType.MAP => + (0 to $in.unpackMapHeader()-1).foreach{ _ => + ${ Ref(classFieldMatrixSyms(MethodKey(t, false))).asExprOf[Map[String,Int]] } + .get($in.unpackString()) + .map{ fieldNum => + ${ Match('{ fieldNum }.asTerm, caseDefsWithFinal).asExprOf[Any] } + } + () + } + if (${ Ref(reqSym).asExprOf[Int] } & ${ exprRequired }) == 0 then ${ instantiateClass.asExprOf[T] } + else throw new MsgPackParseError("Missing required field(s) " + ${ allFieldNames }(Integer.numberOfTrailingZeros(${ Ref(reqSym).asExprOf[Int] } & ${ exprRequired }))) + case fmt => throw new MsgPackUnexpectededType("Expected Integer value but got "+fmt) + }.asTerm + else + /* + val instanceSym = Symbol.newVal(Symbol.spliceOwner, "_instance", TypeRepr.of[T], Flags.Mutable, Symbol.noSymbol) + finalVarDefs = finalVarDefs :+ ValDef(instanceSym, Some('{ null }.asTerm)) // add var _instance=null to gen'ed code + val instanceSymRef = Ident(instanceSym.termRef) + // When we do care about non-constructor fields + makeClassFieldMatrixValDef(MethodKey(t, true), t.name.replaceAll("\\.", "_"), t.nonConstructorFields.sortBy(_.index).map(f => changeFieldName(f)).toArray) + val fieldMatrixSymNCF = classFieldMatrixSyms(MethodKey(t, true)).asInstanceOf[Symbol] + // New Case/Match for non-constructor fields + val caseDefsWithFinalNC = t.nonConstructorFields.map(ncf => + ncf.fieldRef.refType match + case '[u] => + CaseDef( + Literal(IntConstant(ncf.index)), + None, + // Call the setter for this field here... + Apply(Select.unique(Ref(instanceSym), ncf.setterLabel), List(genReadVal[u](ncf.fieldRef.asInstanceOf[RTypeRef[u]], in).asTerm)).asExpr.asTerm + ) + ) :+ CaseDef(Wildcard(), None, '{ $in.skipValue() }.asTerm) // skip values of unrecognized fields + '{ + val mark = $in.pos + var maybeFieldNum = $in.expectFirstObjectField(${ Ref(fieldMatrixSym).asExprOf[StringMatrix] }) + if maybeFieldNum == null then null.asInstanceOf[T] + else + while maybeFieldNum.isDefined do + ${ Match('{ maybeFieldNum.get }.asTerm, caseDefsWithFinal).asExprOf[Any] } + maybeFieldNum = $in.expectObjectField(${ Ref(fieldMatrixSym).asExprOf[StringMatrix] }) + + if (${ Ref(reqSym).asExprOf[Int] } & ${ exprRequired }) == 0 then + ${ Assign(instanceSymRef, instantiateClass.asExpr.asTerm).asExprOf[Unit] } // _instance = (new instance) + $in.revertToPos(mark) // go back to re-parse object json, this time for non-constructor fields + maybeFieldNum = $in.expectFirstObjectField(${ Ref(fieldMatrixSymNCF).asExprOf[StringMatrix] }) + while maybeFieldNum.isDefined do + ${ Match('{ maybeFieldNum.get }.asTerm, caseDefsWithFinalNC).asExprOf[Any] } + maybeFieldNum = $in.expectObjectField(${ Ref(fieldMatrixSymNCF).asExprOf[StringMatrix] }) + ${ Ref(instanceSym).asExprOf[T] } + else throw new JsonParseError("Missing required field(s) " + ${ allFieldNames }(Integer.numberOfTrailingZeros(${ Ref(reqSym).asExprOf[Int] } & ${ exprRequired })), $in) + }.asTerm + */ + '{ + null + }.asTerm + + Block(finalVarDefs :+ reqVarDef, parseLoop).asExprOf[T] + ) + + def genWriteVal[T: Type]( + aE: Expr[T], + ref: RTypeRef[T], + out: Expr[MessagePacker], + inTuple: Boolean = false + )(using Quotes): Expr[Unit] = + val methodKey = MethodKey(ref, false) + writeMethodSyms + .get(methodKey) + .map { sym => // hit cache first... then match on Ref type + Apply(Ref(sym), List(aE.asTerm, out.asTerm)).asExprOf[Unit] + } + .getOrElse( + ref match + // First cover all primitive and simple types... + case t: BigDecimalRef => + throw MsgPackUnsupportedType("BigDecimal not supported for MsgPack format") + case t: BigIntRef => + '{ if $aE == null then $out.packNil() else $out.packBigInteger($aE.bigInteger) } + case t: BooleanRef => + '{ $out.packBoolean($aE) } + case t: ByteRef => + '{ $out.packByte($aE) } + case t: CharRef => + '{ $out.packByte($aE.toByte) } + case t: DoubleRef => + '{ $out.packDouble($aE) } + case t: FloatRef => + '{ $out.packFloat($aE) } + case t: IntRef => + '{ $out.packInt($aE) } + case t: LongRef => + '{ $out.packLong($aE) } + case t: ShortRef => + '{ $out.packShort($aE) } + + case t: StringRef => + '{ if $aE == null then $out.packNil() else $out.packString($aE) } + + case t: JBigDecimalRef => + throw MsgPackUnsupportedType("BigDecimal not supported for MsgPack format") + case t: JBigIntegerRef => + '{ if $aE == null then $out.packNil() else $out.packBigInteger($aE) } + case t: JBooleanRef => + '{ $out.packBoolean(${aE.asExprOf[Boolean]}) } + case t: JByteRef => + '{ $out.packByte(${aE.asExprOf[Byte]}) } + case t: JCharacterRef => + '{ $out.packByte($aE.toByte) } + case t: JDoubleRef => + '{ $out.packDouble(${ aE.asExprOf[Double] }) } + case t: JFloatRef => + '{ $out.packFloat(${ aE.asExprOf[Float] }) } + case t: JIntegerRef => + '{ $out.packInt(${ aE.asExprOf[Int] }) } + case t: JLongRef => + '{ $out.packLong(${ aE.asExprOf[Long] }) } + case t: JShortRef => + '{ $out.packShort(${ aE.asExprOf[Short] }) } + case t: JNumberRef => + throw MsgPackUnsupportedType("Number not supported for MsgPack format") + + /* + case t: DurationRef => '{ $out.value(${ aE.asExprOf[java.time.Duration] }) } + case t: InstantRef => '{ $out.value(${ aE.asExprOf[java.time.Instant] }) } + case t: LocalDateRef => '{ $out.value(${ aE.asExprOf[java.time.LocalDate] }) } + case t: LocalDateTimeRef => '{ $out.value(${ aE.asExprOf[java.time.LocalDateTime] }) } + case t: LocalTimeRef => '{ $out.value(${ aE.asExprOf[java.time.LocalTime] }) } + case t: MonthDayRef => '{ $out.value(${ aE.asExprOf[java.time.MonthDay] }) } + case t: OffsetDateTimeRef => '{ $out.value(${ aE.asExprOf[java.time.OffsetDateTime] }) } + case t: OffsetTimeRef => '{ $out.value(${ aE.asExprOf[java.time.OffsetTime] }) } + case t: PeriodRef => '{ $out.value(${ aE.asExprOf[java.time.Period] }) } + case t: YearRef => '{ $out.value(${ aE.asExprOf[java.time.Year] }) } + case t: YearMonthRef => '{ $out.value(${ aE.asExprOf[java.time.YearMonth] }) } + case t: ZonedDateTimeRef => '{ $out.value(${ aE.asExprOf[java.time.ZonedDateTime] }) } + case t: ZoneIdRef => '{ $out.value(${ aE.asExprOf[java.time.ZoneId] }) } + case t: ZoneOffsetRef => '{ $out.value(${ aE.asExprOf[java.time.ZoneOffset] }) } + + case t: URLRef => '{ $out.value(${ aE.asExprOf[java.net.URL] }) } + case t: URIRef => '{ $out.value(${ aE.asExprOf[java.net.URI] }) } + case t: UUIDRef => '{ $out.value(${ aE.asExprOf[java.util.UUID] }) } + case t: ObjectRef[?] => '{ $out.value(${ Expr(t.name) }) } + */ + + /* + case t: AliasRef[?] => + // Special check for RawJson pseudo-type + if lastPart(t.definedType) == "RawJson" then '{ $out.valueRaw(${ aE.asExprOf[RawJson] }) } + else + t.unwrappedType.refType match + case '[e] => + genWriteVal[e](aE.asInstanceOf[Expr[e]], t.unwrappedType.asInstanceOf[RTypeRef[e]], out, inTuple = inTuple) + */ + + // These are here becaue Enums and their various flavors can be Map keys + // (EnumRef handles: Scala 3 enum, Scala 2 Enumeration, Java Enumeration) + /* + case t: EnumRef[?] => + val enumAsId = cfg.enumsAsIds match + case List("-") => false + case Nil => true + case list if list.contains(t.name) => true + case _ => false + val rtype = t.expr + if enumAsId then + if isMapKey then '{ $out.value($rtype.asInstanceOf[EnumRType[?]].ordinal($aE.toString).get.toString) } // stringified id + else '{ $out.value($rtype.asInstanceOf[EnumRType[?]].ordinal($aE.toString).get) } // int value of id + else '{ if $aE == null then $out.burpNull() else $out.value($aE.toString) } + */ + + // NeoType is a bit of a puzzle-box. To get the correct underlying base type, I had to dig into + // the argument of method validate$retainedBody. It happened to have the correctly-typed parameter. + // With the correct type, we can correct write out the value. + /* + case t: NeoTypeRef[?] => // in Quotes context + Symbol.requiredModule(t.typedName.toString).methodMember("validate$retainedBody").head.paramSymss.head.head.tree match + case ValDef(_, tt, _) => + tt.tpe.asType match + case '[u] => + val baseTypeRef = ReflectOnType.apply(q)(tt.tpe)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) + genWriteVal[u]('{ $aE.asInstanceOf[u] }, baseTypeRef.asInstanceOf[RTypeRef[u]], out) + */ + + // case t: AnyRef => '{ AnyWriter.writeAny($aE, $out, ${ Expr(cfg) }) } + + // Everything else... + case _ => genEncFnBody(ref, aE, out, inTuple = inTuple) + ) + + def genReadVal[T: Type]( + ref: RTypeRef[T], + in: Expr[MessageUnpacker], + inTuple: Boolean = false // not sure if needed... + )(using Quotes): Expr[T] = + val methodKey = MethodKey(ref, false) + readMethodSyms + .get(methodKey) + .map { sym => // hit cache first... then match on Ref type + Apply(Ref(sym), List(in.asTerm)).asExprOf[T] + } + .getOrElse( + ref match + // First cover all primitive and simple types... + case t: BigDecimalRef => + throw MsgPackUnsupportedType("BigDecimal not supported for MsgPack format") + case t: BigIntRef => + '{ + $in.getNextFormat().getValueType() match + case ValueType.NIL => null + case ValueType.INTEGER => scala.math.BigInt($in.unpackBigInteger()) + case fmt => throw new MsgPackUnexpectededType("Expected Integer value but got "+fmt) + }.asExprOf[T] + case t: BooleanRef => + '{ + $in.getNextFormat().getValueType() match + case ValueType.BOOLEAN => $in.unpackBoolean() + case fmt => throw new MsgPackUnexpectededType("Expected Boolean value but got "+fmt) + }.asExprOf[T] + case t: ByteRef => + '{ + $in.getNextFormat().getValueType() match + case ValueType.INTEGER => $in.unpackByte() + case fmt => throw new MsgPackUnexpectededType("Expected Integer (byte) value but got "+fmt) + }.asExprOf[T] + case t: CharRef => + '{ + $in.getNextFormat().getValueType() match + case ValueType.INTEGER => $in.unpackByte().toChar + case fmt => throw new MsgPackUnexpectededType("Expected Integer (byte/char) value but got "+fmt) + }.asExprOf[T] + case t: DoubleRef => + '{ + $in.getNextFormat().getValueType() match + case ValueType.FLOAT => $in.unpackDouble() + case fmt => throw new MsgPackUnexpectededType("Expected Float (double) value but got "+fmt) + }.asExprOf[T] + case t: FloatRef => + '{ + $in.getNextFormat().getValueType() match + case ValueType.FLOAT => $in.unpackFloat() + case fmt => throw new MsgPackUnexpectededType("Expected Float value but got "+fmt) + }.asExprOf[T] + case t: IntRef => + '{ + $in.getNextFormat().getValueType() match + case ValueType.INTEGER => $in.unpackInt() + case fmt => throw new MsgPackUnexpectededType("Expected Integer value but got "+fmt) + }.asExprOf[T] + case t: LongRef => + '{ + $in.getNextFormat().getValueType() match + case ValueType.INTEGER => $in.unpackLong() + case fmt => throw new MsgPackUnexpectededType("Expected Integer (long) value but got "+fmt) + }.asExprOf[T] + case t: ShortRef => + '{ + $in.getNextFormat().getValueType() match + case ValueType.INTEGER => $in.unpackShort() + case fmt => throw new MsgPackUnexpectededType("Expected Integer (short) value but got "+fmt) + }.asExprOf[T] + case t: StringRef => + '{ + $in.getNextFormat().getValueType() match + case ValueType.NIL => null + case ValueType.STRING => $in.unpackString() + case fmt => throw new MsgPackUnexpectededType("Expected String value but got "+fmt) + }.asExprOf[T] + + case t: JBigDecimalRef => + throw MsgPackUnsupportedType("BigDecimal not supported for MsgPack format") + case t: JBigIntegerRef => + '{ + $in.getNextFormat().getValueType() match + case ValueType.NIL => null + case ValueType.INTEGER => $in.unpackBigInteger() + case fmt => throw new MsgPackUnexpectededType("Expected Integer value but got "+fmt) + }.asExprOf[T] + case t: JBooleanRef => + '{ + $in.getNextFormat().getValueType() match + case ValueType.BOOLEAN => java.lang.Boolean.valueOf($in.unpackBoolean()) + case fmt => throw new MsgPackUnexpectededType("Expected Boolean value but got "+fmt) + }.asExprOf[T] + case t: JByteRef => + '{ + $in.getNextFormat().getValueType() match + case ValueType.NIL => null + case ValueType.INTEGER => java.lang.Byte.valueOf($in.unpackByte()) + case fmt => throw new MsgPackUnexpectededType("Expected Integer (byte) value but got "+fmt) + }.asExprOf[T] + case t: JCharacterRef => + '{ + $in.getNextFormat().getValueType() match + case ValueType.NIL => null + case ValueType.INTEGER => java.lang.Character.valueOf($in.unpackByte().toChar) + case fmt => throw new MsgPackUnexpectededType("Expected Integer (byte/char) value but got "+fmt) + }.asExprOf[T] + case t: JDoubleRef => + '{ + $in.getNextFormat().getValueType() match + case ValueType.NIL => null + case ValueType.FLOAT => java.lang.Double.valueOf($in.unpackDouble()) + case fmt => throw new MsgPackUnexpectededType("Expected Float (double) value but got "+fmt) + }.asExprOf[T] + case t: JFloatRef => + '{ + $in.getNextFormat().getValueType() match + case ValueType.NIL => null + case ValueType.FLOAT => java.lang.Float.valueOf($in.unpackFloat()) + case fmt => throw new MsgPackUnexpectededType("Expected Float value but got "+fmt) + }.asExprOf[T] + case t: JIntegerRef => + '{ + $in.getNextFormat().getValueType() match + case ValueType.NIL => null + case ValueType.INTEGER =>java.lang.Integer.valueOf( $in.unpackInt()) + case fmt => throw new MsgPackUnexpectededType("Expected Integer value but got "+fmt) + }.asExprOf[T] + case t: JLongRef => + '{ + $in.getNextFormat().getValueType() match + case ValueType.NIL => null + case ValueType.INTEGER => java.lang.Long.valueOf($in.unpackLong()) + case fmt => throw new MsgPackUnexpectededType("Expected Integer (long) value but got "+fmt) + }.asExprOf[T] + case t: JShortRef => + '{ + $in.getNextFormat().getValueType() match + case ValueType.NIL => null + case ValueType.INTEGER => java.lang.Short.valueOf($in.unpackShort()) + case fmt => throw new MsgPackUnexpectededType("Expected Integer (short) value but got "+fmt) + }.asExprOf[T] + case t: JNumberRef => + throw MsgPackUnsupportedType("Number not supported for MsgPack format") + + case t: SeqRef[?] => + ref.refType match + case '[List[?]] => + t.elementRef.refType match + case '[e] => + val rtypeRef = t.elementRef.asInstanceOf[RTypeRef[e]] + '{ + $in.getNextFormat().getValueType() match + case ValueType.NIL => null + case ValueType.ARRAY => + (0 to $in.unpackArrayHeader()-1).map(_ => ${ genReadVal[e](rtypeRef, in, inTuple) }).toList + case fmt => throw new MsgPackUnexpectededType("Expected Array value but got "+fmt) + }.asExprOf[T] + + case t: ArrayRef[?] => + t.elementRef.refType match + case '[e] => + val rtypeRef = t.elementRef.asInstanceOf[RTypeRef[e]] + val ct = Expr.summon[ClassTag[e]].get + '{ + $in.getNextFormat().getValueType() match + case ValueType.NIL => null + case ValueType.ARRAY => + val parsedArray = (0 to $in.unpackArrayHeader()-1).map(_ => ${ genReadVal[e](rtypeRef, in, inTuple) }) + implicit val ctt = $ct + parsedArray.toArray[e] + case fmt => throw new MsgPackUnexpectededType("Expected Array value but got "+fmt) + }.asExprOf[T] + + case _ => + // Classes, traits, etc. + genDecFnBody[T](ref, in) // Create a new decoder function (presumably for class, trait, etc) + genReadVal(ref, in, inTuple) + ) + + val codecDef = '{ // FIXME: generate a type class instance using `ClassDef.apply` and `Symbol.newClass` calls after graduating from experimental API: https://www.scala-lang.org/blog/2022/06/21/scala-3.1.3-released.html + new MsgPackCodec[T] { + def encodeValue(in: T, out: MessagePacker): Unit = ${ genWriteVal('in, ref, 'out) } + def decodeValue(in: MessageUnpacker): T = ${ genReadVal(ref, 'in) } + } + }.asTerm + val codec = Block((classFieldMatrixValDefs ++ writeMethodDefs ++ readMethodDefs).toList, codecDef).asExprOf[MsgPackCodec[T]] + // println(s"Codec: ${codec.show}") + codec diff --git a/src/main/scala/co.blocke.scalajack/msgpack/MsgPackError.scala b/src/main/scala/co.blocke.scalajack/msgpack/MsgPackError.scala new file mode 100644 index 00000000..deb0ad39 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/msgpack/MsgPackError.scala @@ -0,0 +1,32 @@ +package co.blocke.scalajack +package msgpack + +import scala.util.control.NoStackTrace + +// class JsonIllegalKeyType(msg: String) extends Throwable(msg) with NoStackTrace +// class JsonNullKeyValue(msg: String) extends Throwable(msg) with NoStackTrace +class MsgPackUnsupportedType(msg: String) extends Throwable(msg) with NoStackTrace +class MsgPackUnexpectededType(msg: String) extends Throwable(msg) with NoStackTrace +class MsgPackParseError(msg: String) extends Throwable(msg) with NoStackTrace +// class JsonEitherLeftError(msg: String) extends Throwable(msg) with NoStackTrace +// class JsonIllegalCharacterError(msg: String) extends Throwable(msg) with NoStackTrace + +// class ParseError(val msg: String) extends Throwable(msg) with NoStackTrace: +// val show: String = "" + +// Thrown at compile-time only! +// case class MsgPackTypeError(override val msg: String) extends ParseError(msg) with NoStackTrace: +// override val show: String = "" + +// Thrown at runtime only! +// case class JsonParseError(override val msg: String, context: reading.JsonSource) extends ParseError(msg + " at position " + context.pos) with NoStackTrace: +// override val show: String = +// val js = context.js.toString +// val (clip, dashes) = context.pos match { +// case ep if ep <= 50 && context.max < 80 => (js, ep) +// case ep if ep <= 50 => (js.substring(0, 77) + "...", ep) +// case ep if ep > 50 && ep + 30 >= context.max => +// ("..." + js.substring(context.pos - 49), 52) +// case ep => ("..." + js.substring(ep - 49, ep + 27) + "...", 52) +// } +// msg + s" at position [${context.pos}]" + "\n" + clip.replaceAll("[\n\t]", "~") + "\n" + ("-" * dashes) + "^" diff --git a/src/main/scala/co.blocke.scalajack/msgpack/package.scalax b/src/main/scala/co.blocke.scalajack/msgpack/package.scalax new file mode 100644 index 00000000..f0bfebba --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/msgpack/package.scalax @@ -0,0 +1,50 @@ +package co.blocke.scalajack +package msgpack + +val MP_NIL: Byte = 0xc0 +val MP_FALSE: Byte = 0xc2 +val MP_TRUE: Byte = 0xc3 +val MP_BIN8: Byte = 0xc4 +val MP_BIN16: Byte = 0xc5 +val MP_BIN32: Byte = 0xc6 +val MP_EXT8: Byte = 0xc7 +val MP_EXT16: Byte = 0xc8 +val MP_EXT32: Byte = 0xc9 +val MP_FLOAT32: Byte = 0xca +val MP_FLOAT64: Byte = 0xcb +val MP_UINT8: Byte = 0xcc +val MP_UINT16: Byte = 0xcd +val MP_UINT32: Byte = 0xce +val MP_UINT64: Byte = 0xcf +val MP_INT8: Byte = 0xd0 +val MP_INT16: Byte = 0xd1 +val MP_INT32: Byte = 0xd2 +val MP_INT64: Byte = 0xd3 +val MP_FIXEXT1: Byte = 0xd4 +val MP_FIXEXT2: Byte = 0xd5 +val MP_FIXEXT4: Byte = 0xd6 +val MP_FIXEXT8: Byte = 0xd7 +val MP_FIXEXT16: Byte = 0xd8 +val MP_STR8: Byte = 0xd9 +val MP_STR16: Byte = 0xda +val MP_STR32: Byte = 0xdb +val MP_ARR16: Byte = 0xdc +val MP_ARR32: Byte = 0xdd +val MP_MAP16: Byte = 0xde +val MP_MAP32: Byte = 0xdf + +// Masks +val MP_POS_FIXINT_MASK: Byte = b"10000000" +val MP_FIXMAP_MASK: Byte = b"11110000" +val MP_FIXMAP: Byte = b"10000000" +val MP_FIXMAP_VALUE: Byte = b"00001111" +val MP_FIXARR_MASK: Byte = b"11110000" +val MP_FIXARR: Byte = b"10010000" +val MP_FIXARR_VALUE: Byte = b"00001111" +val MP_FIXSTR_MASK: Byte = b"11100000" +val MP_FIXSTR: Byte = b"10100000" +val MP_FIXSTR_VALUE: Byte = b"00011111" +val MP_NEGFIXINT_MASK: Byte = b"11100000" +val MP_NEGFIXINT: Byte = b"11100000" +val MP_NEGFIXINT_VALUE: Byte = b"00011111" + diff --git a/src/main/scala/co.blocke.scalajack/package.scala b/src/main/scala/co.blocke.scalajack/package.scala new file mode 100644 index 00000000..6b0e9423 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/package.scala @@ -0,0 +1,25 @@ +package co.blocke.scalajack + +inline def lastPart(n: String) = n.split('.').last.stripSuffix("$") +inline def allButLastPart(n: String) = + val l = n.lastIndexOf('.') + if l >= 0 then n.substring(0, l) + else n + +val random = new scala.util.Random() +def scramble(hash: Int): String = + val last5 = f"$hash%05d".takeRight(5) + val digits = (1 to 5).map(_ => random.nextInt(10)) + if digits(0) % 2 == 0 then s"${last5(0)}${digits(0)}${last5(1)}${digits(1)}${last5(2)}-${digits(2)}${last5(3)}${digits(3)}-${last5(4)}${digits(4)}A" + else s"${digits(0)}${last5(0)}${digits(1)}${last5(1)}${digits(2)}-${last5(2)}${digits(3)}${last5(3)}-${digits(4)}${last5(4)}B" + +def descramble(in: String, hash: Int): Boolean = + val last5 = f"$hash%05d".takeRight(5) + in.last match + case 'A' if in.length == 13 => "" + in(0) + in(2) + in(4) + in(7) + in(10) == last5 + case 'B' if in.length == 13 => "" + in(1) + in(3) + in(6) + in(8) + in(11) == last5 + case _ => false + +enum Language { + case Scala, Java +} diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scala new file mode 100644 index 00000000..ab2db9a2 --- /dev/null +++ b/src/main/scala/co.blocke.scalajack/run/Play.scala @@ -0,0 +1,21 @@ +package co.blocke.scalajack +package json +package run + +import ScalaJack.* + +case class Well(repo: scala.collection.mutable.Map[String, Int] = scala.collection.mutable.Map.empty[String, Int]) + +object RunMe extends App: + + import ScalaJack.* + + given sj: ScalaJack[Pet] = sjCodecOf[Pet] + + val a = sj.toMsgPack(Pet("Mindy","Frenchie",3)) + println("LEN: "+a.length) + println(a.toList.map(i => f"$i%02X").mkString(" ")) + + println("BACK: "+ sj.fromMsgPack(a)) + println("ok") + \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scalax b/src/main/scala/co.blocke.scalajack/run/Play.scalax deleted file mode 100644 index 71f2c8d0..00000000 --- a/src/main/scala/co.blocke.scalajack/run/Play.scalax +++ /dev/null @@ -1,28 +0,0 @@ -package co.blocke.scalajack -package json -package run - -import ScalaJack.* - -case class Well(repo: scala.collection.mutable.Map[String, Int] = scala.collection.mutable.Map.empty[String, Int]) - -object RunMe extends App: - - import ScalaJack.* - import co.blocke.scalajack.json.run.Record - import co.blocke.scala_reflection.* - - given sjValidated: ScalaJack[Validated] = sjCodecOf[Validated] - - val sj_ok = """{"name":"Mike","xspot":["x","other"],"nada":""}""" - val sj_brokenName = """{"name":"","xspot":["x","other"],"nada":""}""" - val sj_brokenNada = """{"name":"Mike","xspot":["x","other"],"nada":"boom"}""" - val sj_xspot = """{"name":"Mike","xspot":["nintendo","other"],"nada":""}""" - - val inst = Validated(NonEmptyString("Mike"), XList(List("x","y")), EmptyString("")) - val ok = try( sjValidated.fromJson(sj_xspot) ) - catch { - case jpe: JsonParseError => println(jpe.show) - } - println(ok) - \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/run/Record.scalax b/src/main/scala/co.blocke.scalajack/run/Record.scala similarity index 100% rename from src/main/scala/co.blocke.scalajack/run/Record.scalax rename to src/main/scala/co.blocke.scalajack/run/Record.scala From 0823f7d7bcf6b4503b47919a2a895ba7aa36c97e Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Mon, 20 May 2024 23:25:41 -0500 Subject: [PATCH 62/65] remove msgpack --- README.md | 3 +- benchmark/build.sbt | 8 +- .../src/main/scala/co.blocke/Benchmark.scala | 17 +- .../src/main/scala/co.blocke/MsgPack.scalax | 19 - .../src/main/scala/co.blocke/ScalaJack.scala | 12 - build.sbt | 1 - .../scala/co.blocke.scalajack/ScalaJack.scala | 21 +- .../msgpack/MsgPackCodec.scala | 9 - .../msgpack/MsgPackCodecMaker.scala | 722 ------------------ .../msgpack/MsgPackError.scala | 32 - .../msgpack/package.scalax | 50 -- .../run/{Play.scala => Play.scalax} | 6 - .../run/{Record.scala => Record.scalax} | 1 - .../json/misc/MiscSpec.scala | 4 +- 14 files changed, 12 insertions(+), 893 deletions(-) delete mode 100644 benchmark/src/main/scala/co.blocke/MsgPack.scalax delete mode 100644 src/main/scala/co.blocke.scalajack/msgpack/MsgPackCodec.scala delete mode 100644 src/main/scala/co.blocke.scalajack/msgpack/MsgPackCodecMaker.scala delete mode 100644 src/main/scala/co.blocke.scalajack/msgpack/MsgPackError.scala delete mode 100644 src/main/scala/co.blocke.scalajack/msgpack/package.scalax rename src/main/scala/co.blocke.scalajack/run/{Play.scala => Play.scalax} (63%) rename src/main/scala/co.blocke.scalajack/run/{Record.scala => Record.scalax} (99%) diff --git a/README.md b/README.md index bc8d89c9..5d53c2ea 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,8 @@ ScalaJack 8 is an all-new ScalaJack serializer implemenation built on Scala 3. F using Scala 3.4.1 on JDK 21 LTS version. ScalaJack is a very fast, seamless serialization engine for unstructured data types designed to require a bare minimum of extra code -to serialize a class. ScalaJack supports JSON and MsgPack as part of its focus on over-the-wire data and message/event transport. +to serialize a class. ScalaJack supports JSON in its focus on over-the-wire data and message/event transport. (We looked at offering MsgPack support, but to our surprise benchmarks +showed that MsgPack serialization had about 25% slower write performance and 45% slower read performance than JSON, so for now we're sticking with just JSON support.) Advanced Features: diff --git a/benchmark/build.sbt b/benchmark/build.sbt index d775fdfc..42abc17a 100644 --- a/benchmark/build.sbt +++ b/benchmark/build.sbt @@ -13,7 +13,7 @@ val compilerOptions = Seq( val circeVersion = "0.15.0-M1" val scalaTestVersion = "3.2.11" -ThisBuild / scalaVersion := "3.3.0" +ThisBuild / scalaVersion := "3.4.1" def priorTo2_13(scalaVersion: String): Boolean = CrossVersion.partialVersion(scalaVersion) match { @@ -37,14 +37,14 @@ lazy val benchmark = project libraryDependencies ++= Seq( "org.playframework" %% "play-json" % "3.0.1", "io.argonaut" %% "argonaut" % "6.3.9", - "co.blocke" %% "scalajack" % "efad12_unknown", + "co.blocke" %% "scalajack" % "8.0.0", "dev.zio" %% "zio-json" % "0.6.1", "org.typelevel" %% "fabric-core" % "1.12.6", "org.typelevel" %% "fabric-io" % "1.12.6", "org.typelevel" %% "jawn-parser" % "1.3.2", "org.typelevel" %% "jawn-ast" % "1.3.2", - "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core" % "2.24.5-SNAPSHOT", - "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-macros" % "2.24.5-SNAPSHOT" % "compile-internal", + "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core" % "2.28.5", + "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-macros" % "2.28.5" % "compile-internal", // "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core" % "2.24.4", // "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-macros" % "2.24.4" % "compile-internal", // "io.circe" %% "circe-derivation" % "0.15.0-M1", diff --git a/benchmark/src/main/scala/co.blocke/Benchmark.scala b/benchmark/src/main/scala/co.blocke/Benchmark.scala index 3a5e3833..793e3914 100644 --- a/benchmark/src/main/scala/co.blocke/Benchmark.scala +++ b/benchmark/src/main/scala/co.blocke/Benchmark.scala @@ -43,7 +43,6 @@ trait HandTooledWritingBenchmark { @BenchmarkMode(Array(Mode.Throughput)) @OutputTimeUnit(TimeUnit.SECONDS) class ReadingBenchmark - // extends MsgPack.MsgPackReadingBenchmark extends ScalaJackZ.ScalaJackReadingBenchmark // with CirceZ.CirceReadingBenchmark // extends JsoniterZ.JsoniterReadingBenchmark @@ -55,25 +54,11 @@ class ReadingBenchmark @BenchmarkMode(Array(Mode.Throughput)) @OutputTimeUnit(TimeUnit.SECONDS) class WritingBenchmark - // extends MsgPack.MsgPackWritingBenchmark // extends HandTooledWritingBenchmark // extends CirceZ.CirceWritingBenchmark + // extends ScalaJackZ.MsgPackWritingBenchmark extends ScalaJackZ.ScalaJackWritingBenchmark // extends JsoniterZ.JsoniterWritingBenchmark // with ZIOZ.ZIOJsonWritingBenchmark // with PlayZ.PlayWritingBenchmark // with ArgonautZ.ArgonautWritingBenchmark - - - -/* LATEST RUN: - -[info] Benchmark Mode Cnt Score Error Units -[info] ReadingBenchmark.readRecordJsoniter thrpt 20 1346388.345 ± 17028.863 ops/s -[info] ReadingBenchmark.readRecordScalaJack thrpt 20 941679.824 ± 58208.523 ops/s -[info] ReadingBenchmark.readRecordZIOJson thrpt 20 590995.917 ± 817.817 ops/s -[info] ReadingBenchmark.readRecordCirce thrpt 20 210805.946 ± 32488.564 ops/s -[info] ReadingBenchmark.readRecordPlay thrpt 20 198747.067 ± 7253.896 ops/s -[info] ReadingBenchmark.readRecordArgonaut thrpt 20 183670.032 ± 8981.485 ops/s - - */ diff --git a/benchmark/src/main/scala/co.blocke/MsgPack.scalax b/benchmark/src/main/scala/co.blocke/MsgPack.scalax deleted file mode 100644 index 32802441..00000000 --- a/benchmark/src/main/scala/co.blocke/MsgPack.scalax +++ /dev/null @@ -1,19 +0,0 @@ -package co.blocke - -import org.openjdk.jmh.annotations._ - -object MsgPack: - import co.blocke.scalajack.ScalaJack.* - import co.blocke.scalajack.* - - given sj: ScalaJack[co.blocke.Record2] = sjCodecOf[co.blocke.Record2] - - trait MsgPackReadingBenchmark{ - @Benchmark - def readRecordScalaJack = sj.fromMsgPack(jsData2) - } - - trait MsgPackWritingBenchmark { - @Benchmark - def writeRecordScalaJack = sj.toMsgPack(record) - } diff --git a/benchmark/src/main/scala/co.blocke/ScalaJack.scala b/benchmark/src/main/scala/co.blocke/ScalaJack.scala index 1e9c6e28..2c448de5 100644 --- a/benchmark/src/main/scala/co.blocke/ScalaJack.scala +++ b/benchmark/src/main/scala/co.blocke/ScalaJack.scala @@ -8,8 +8,6 @@ object ScalaJackZ: given sj: ScalaJack[co.blocke.Record2] = sjCodecOf[co.blocke.Record2] - // implicit val blah: ScalaJack[co.blocke.Record2] = sjCodecOf[co.blocke.Record2] - trait ScalaJackReadingBenchmark{ @Benchmark def readRecordScalaJack = sj.fromJson(jsData2) @@ -20,16 +18,6 @@ object ScalaJackZ: def writeRecordScalaJack = sj.toJson(record) } - trait MsgPackReadingBenchmark{ - @Benchmark - def readRecordScalaJack = sj.fromMsgPack(jsData2) - } - - trait MsgPackWritingBenchmark { - @Benchmark - def writeRecordScalaJack = sj.toMsgPack(record) - } - /* This is the way: diff --git a/build.sbt b/build.sbt index c251b232..8a564953 100644 --- a/build.sbt +++ b/build.sbt @@ -38,7 +38,6 @@ lazy val root = project "co.blocke" %% "scala-reflection" % "2.0.6", "org.apache.commons" % "commons-text" % "1.11.0", "io.github.kitlangton" %% "neotype" % "0.0.9", - "org.msgpack" % "msgpack-core" % "0.9.8", "org.scalatest" %% "scalatest" % "3.2.17" % Test, "org.json4s" %% "json4s-core" % "4.0.6" % Test, "org.json4s" %% "json4s-native" % "4.0.6" % Test diff --git a/src/main/scala/co.blocke.scalajack/ScalaJack.scala b/src/main/scala/co.blocke.scalajack/ScalaJack.scala index 496d5974..432fe60e 100644 --- a/src/main/scala/co.blocke.scalajack/ScalaJack.scala +++ b/src/main/scala/co.blocke.scalajack/ScalaJack.scala @@ -6,9 +6,8 @@ import scala.quoted.* import quoted.Quotes import json.* -import msgpack.* -case class ScalaJack[T](jsonCodec: JsonCodec[T], msgPackCodec: MsgPackCodec[T]): // extends JsonCodec[T] //with YamlCodec with MsgPackCodec +case class ScalaJack[T](jsonCodec: JsonCodec[T]): def fromJson(js: String): T = jsonCodec.decodeValue(reading.JsonSource(js)) @@ -17,18 +16,6 @@ case class ScalaJack[T](jsonCodec: JsonCodec[T], msgPackCodec: MsgPackCodec[T]): jsonCodec.encodeValue(a, out.clear()) out.result - val outArray = new org.msgpack.core.buffer.ArrayBufferOutput() - def toMsgPack(a: T): Array[Byte] = - outArray.clear() - val outMP = org.msgpack.core.MessagePack.newDefaultPacker(outArray) - msgPackCodec.encodeValue(a, outMP) - outMP.close() - outArray.toByteArray - - def fromMsgPack(a: Array[Byte]): T = - val inMP = org.msgpack.core.MessagePack.newDefaultUnpacker(a) - msgPackCodec.decodeValue(inMP) - // --------------------------------------- object ScalaJack: @@ -41,9 +28,8 @@ object ScalaJack: import quotes.reflect.* val classRef = ReflectOnType[T](quotes)(TypeRepr.of[T], true)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) val jsonCodec = JsonCodecMaker.generateCodecFor(classRef, SJConfig) - val mpCodec = MsgPackCodecMaker.generateCodecFor(classRef, SJConfig) - '{ ScalaJack($jsonCodec, $mpCodec) } + '{ ScalaJack($jsonCodec) } // ----- Use given JsonConfig inline def sjCodecOf[T](inline cfg: SJConfig): ScalaJack[T] = ${ codecOfImplWithConfig[T]('cfg) } @@ -52,5 +38,4 @@ object ScalaJack: val cfg = summon[FromExpr[SJConfig]].unapply(cfgE) val classRef = ReflectOnType[T](quotes)(TypeRepr.of[T], true)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) val jsonCodec = JsonCodecMaker.generateCodecFor(classRef, cfg.getOrElse(SJConfig)) - val mpCodec = MsgPackCodecMaker.generateCodecFor(classRef, SJConfig) - '{ ScalaJack($jsonCodec, $mpCodec) } + '{ ScalaJack($jsonCodec) } diff --git a/src/main/scala/co.blocke.scalajack/msgpack/MsgPackCodec.scala b/src/main/scala/co.blocke.scalajack/msgpack/MsgPackCodec.scala deleted file mode 100644 index 77341fc2..00000000 --- a/src/main/scala/co.blocke.scalajack/msgpack/MsgPackCodec.scala +++ /dev/null @@ -1,9 +0,0 @@ -package co.blocke.scalajack -package msgpack - -import org.msgpack.core.{MessagePacker, MessageUnpacker} - -trait MsgPackCodec[A] { - def encodeValue(in: A, out: MessagePacker): Unit - def decodeValue(in: MessageUnpacker): A -} diff --git a/src/main/scala/co.blocke.scalajack/msgpack/MsgPackCodecMaker.scala b/src/main/scala/co.blocke.scalajack/msgpack/MsgPackCodecMaker.scala deleted file mode 100644 index e14f3164..00000000 --- a/src/main/scala/co.blocke.scalajack/msgpack/MsgPackCodecMaker.scala +++ /dev/null @@ -1,722 +0,0 @@ -package co.blocke.scalajack -package msgpack - -import scala.collection.mutable.ArrayBuffer -import org.msgpack.core.{MessagePacker, MessageUnpacker, MessageFormat} -import org.msgpack.value.ValueType -import org.msgpack.core.MessagePack.PackerConfig -import org.msgpack.core.buffer.ArrayBufferOutput -import co.blocke.scala_reflection.RTypeRef -import co.blocke.scala_reflection.reflect.rtypeRefs.* -import scala.reflect.ClassTag -import scala.util.Success -import scala.quoted.* - -object MsgPackCodecMaker: - - def generateCodecFor[T](ref: RTypeRef[T], cfg: SJConfig)(using q: Quotes)(using tt: Type[T]) = - import q.reflect.* - - // Cache generated method Symbols + an array of the generated functions (DefDef) - case class MethodKey(ref: RTypeRef[?], isNonConstructorFields: Boolean) - val writeMethodSyms = new scala.collection.mutable.HashMap[MethodKey, Symbol] - val writeMethodDefs = new scala.collection.mutable.ArrayBuffer[DefDef] - val readMethodSyms = new scala.collection.mutable.HashMap[MethodKey, Symbol] - val readMethodDefs = new scala.collection.mutable.ArrayBuffer[DefDef] - - inline def changeFieldName(fr: FieldInfoRef): String = fr.annotations.get("co.blocke.scalajack.Change").flatMap(_.get("name")).getOrElse(fr.name) - - def makeWriteFn[U: Type](methodKey: MethodKey, arg: Expr[U], out: Expr[MessagePacker])(f: (Expr[U], Expr[MessagePacker]) => Expr[Unit]): Expr[Unit] = - // Get a symbol, if one already created for this key... else make one. - Apply( - Ref( - writeMethodSyms.getOrElse( - methodKey, { - val sym = Symbol.newMethod( - Symbol.spliceOwner, - "w" + writeMethodSyms.size, // 'w' is for Writer! - MethodType(List("in", "out"))(_ => List(TypeRepr.of[U], TypeRepr.of[MessagePacker]), _ => TypeRepr.of[Unit]) - ) - writeMethodSyms.update(methodKey, sym) - writeMethodDefs += DefDef( - sym, - params => { - val List(List(in, out)) = params - Some(f(in.asExprOf[U], out.asExprOf[MessagePacker]).asTerm.changeOwner(sym)) - } - ) - sym - } - ) - ), - List(arg.asTerm, out.asTerm) - ).asExprOf[Unit] - - def makeReadFn[U: Type](methodKey: MethodKey, in: Expr[MessageUnpacker])(f: Expr[MessageUnpacker] => Expr[U])(using Quotes)(using Type[MessageUnpacker]): Expr[Unit] = - readMethodSyms.getOrElse( - methodKey, { - val sym = Symbol.newMethod( - Symbol.spliceOwner, - "r" + readMethodSyms.size, - MethodType(List("in"))(_ => List(TypeRepr.of[MessageUnpacker]), _ => TypeRepr.of[U]) - // (_ => List(input_params,...), _ => resultType) - ) - readMethodSyms.update(methodKey, sym) - readMethodDefs += DefDef( - sym, - params => { - val List(List(in)) = params - Some(f(in.asExprOf[MessageUnpacker]).asTerm.changeOwner(sym)) - } - ) - } - ) - '{} - - val classFieldMatrixSyms = new scala.collection.mutable.HashMap[MethodKey, Symbol] - val classFieldMatrixValDefs = new scala.collection.mutable.ArrayBuffer[ValDef] - - def makeClassFieldMatrixValDef(methodKey: MethodKey, className: String, fieldNames: Array[String])(using Quotes): Expr[Unit] = - classFieldMatrixSyms.getOrElse( - methodKey, { - val sym = Symbol.newVal( - Symbol.spliceOwner, - s"__$className" + "_fields", - TypeRepr.of[Map[String,Int]], - Flags.EmptyFlags, - Symbol.noSymbol - ) - classFieldMatrixSyms.update(methodKey, sym) - val names = Expr(fieldNames) - classFieldMatrixValDefs += ValDef(sym, Some('{ $names.zipWithIndex.toMap }.asTerm)) - } - ) - '{} - - def maybeWrite[T](label: String, aE: Expr[T], ref: RTypeRef[T], out: Expr[MessagePacker], cfg: SJConfig): Expr[Unit] = - val labelE = Expr(label) - _maybeWrite[T]( - '{ $out.packString($labelE) }, - aE, - ref, - out, - cfg - ) - - def _maybeWrite[T](prefix: Expr[Unit], aE: Expr[T], ref: RTypeRef[T], out: Expr[MessagePacker], cfg: SJConfig): Expr[Unit] = - ref match - case _ => - ref.refType match - case '[u] => - '{ - $prefix - ${ genWriteVal[u](aE.asExprOf[u], ref.asInstanceOf[RTypeRef[u]], out) } - } - - def lrHasOptionChild(lr: LeftRightRef[?]): (String, Language) = - lr.rightRef match - case t: ScalaOptionRef[?] => ("r", Language.Scala) - case t: JavaOptionalRef[?] => ("r", Language.Java) - case t: LeftRightRef[?] => - val (recipe, lang) = lrHasOptionChild(t) - ("r" + recipe, lang) - case _ => - lr.leftRef match - case t: ScalaOptionRef[?] => ("l", Language.Scala) - case t: JavaOptionalRef[?] => ("l", Language.Java) - case t: LeftRightRef[?] => - val (recipe, lang) = lrHasOptionChild(t) - ("l" + recipe, lang) - case _ => ("", Language.Scala) - - def tryHasOptionChild(t: TryRef[?]): (Boolean, Language) = - t.tryRef match - case o: ScalaOptionRef[?] => (true, Language.Scala) - case o: JavaOptionalRef[?] => (true, Language.Java) - case lr: LeftRightRef[?] => - val (recipe, lang) = lrHasOptionChild(lr) - (recipe.length > 0, lang) - case _ => (false, Language.Scala) - - def genEncFnBody[T](r: RTypeRef[?], aE: Expr[T], out: Expr[MessagePacker], emitDiscriminator: Boolean = false, inTuple: Boolean = false)(using Quotes): Expr[Unit] = - r.refType match - case '[b] => - r match - case t: ArrayRef[?] => - makeWriteFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => - t.elementRef.refType match - case '[e] => - val tin = in.asInstanceOf[Expr[Array[e]]] - '{ - if $tin == null then $out.packNil() - else - $out.packArrayHeader($tin.length) - $tin.foreach { i => - ${ genWriteVal('{ i }, t.elementRef.asInstanceOf[RTypeRef[e]], out) } - } - } - } - case t: SeqRef[?] => - makeWriteFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => - t.elementRef.refType match - case '[e] => - val tin = if t.isMutable then in.asExprOf[scala.collection.mutable.Seq[e]] else in.asExprOf[Seq[e]] - '{ - if $tin == null then $out.packNil() - else - $out.packArrayHeader($tin.length) - $tin.foreach { i => - ${ genWriteVal('{ i }, t.elementRef.asInstanceOf[RTypeRef[e]], out) } - } - } - } - case t: ScalaClassRef[?] => - makeWriteFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => - val body = { - val eachField = t.fields.map { f => - f.fieldRef.refType match - case '[z] => - val fieldValue = Select.unique(in.asTerm, f.name).asExprOf[z] - val fieldName = changeFieldName(f) - maybeWrite[z](fieldName, fieldValue, f.fieldRef.asInstanceOf[RTypeRef[z]], out, cfg) - } - if emitDiscriminator then - val cname = cfg.typeHintPolicy match - case TypeHintPolicy.SIMPLE_CLASSNAME => Expr(lastPart(t.name)) - case TypeHintPolicy.SCRAMBLE_CLASSNAME => '{ scramble(${ Expr(lastPart(t.name).hashCode) }) } - case TypeHintPolicy.USE_ANNOTATION => - Expr(t.annotations.get("co.blocke.scalajack.TypeHint").flatMap(_.get("hintValue")).getOrElse(lastPart(t.name))) - val withDisc = '{ - $out.packString(${ Expr(cfg.typeHintLabel) }) - $out.packString($cname) - } +: eachField - Expr.block(withDisc.init, withDisc.last) - else if eachField.length == 1 then eachField.head - else Expr.block(eachField.init, eachField.last) - } - - val fieldCount = Expr(if emitDiscriminator then t.fields.size + 1 else t.fields.size) - if !t.isCaseClass && cfg._writeNonConstructorFields then - val eachField = t.nonConstructorFields.map { f => - f.fieldRef.refType match - case '[e] => - val fieldValue = Select.unique(in.asTerm, f.getterLabel).asExprOf[e] - val fieldName = changeFieldName(f) - maybeWrite[e](fieldName, fieldValue, f.fieldRef.asInstanceOf[RTypeRef[e]], out, cfg) - } - val subBody = eachField.length match - case 0 => '{} - case 1 => eachField.head - case _ => Expr.block(eachField.init, eachField.last) - '{ - if $in == null then $out.packNil() - else - $out.packMapHeader( $fieldCount ) - $body - $subBody - } - else - '{ - if $in == null then $out.packNil() - else - $out.packMapHeader( $fieldCount ) - $body - } - } - - def genDecFnBody[T: Type](r: RTypeRef[?], in: Expr[MessageUnpacker])(using Quotes): Expr[Unit] = - - def typeArgs(tpe: TypeRepr): List[TypeRepr] = tpe match - case AppliedType(_, typeArgs) => typeArgs.map(_.dealias) - case _ => Nil - - r.refType match // refType is Type[r.R] - case '[b] => - r match - case t: ScalaClassRef[?] => - makeReadFn[T](MethodKey(t, false), in)(in => - // Generate vars for each contractor argument, populated with either a "unit" value (eg 0, "") or given default value - val tpe = TypeRepr.of[b] - val classCompanion = tpe.typeSymbol.companionClass - val companionModule = tpe.typeSymbol.companionModule - val totalRequired = math.pow(2, t.fields.length).toInt - 1 - var required = 0 - val reqSym = Symbol.newVal(Symbol.spliceOwner, "required", TypeRepr.of[Int], Flags.Mutable, Symbol.noSymbol) - val allFieldNames = Expr(t.fields.map(f => changeFieldName(f)).toArray) // Used for missing required field error - - val together = t.fields.map { oneField => - oneField.fieldRef.refType match { - case '[f] => - val dvMembers = classCompanion.methodMember("$lessinit$greater$default$" + (oneField.index + 1)) - val sym = Symbol.newVal(Symbol.spliceOwner, "_" + oneField.name, TypeRepr.of[f], Flags.Mutable, Symbol.noSymbol) - val fieldSymRef = Ident(sym.termRef) - val reqBit = Expr(math.pow(2, oneField.index).toInt) - val fieldName = Expr(oneField.name) - - val caseDef = CaseDef( - Literal(IntConstant(oneField.index)), - None, - '{ - if (${ Ref(reqSym).asExprOf[Int] } & $reqBit) != 0 then - ${ Assign(Ref(reqSym), '{ ${ Ref(reqSym).asExprOf[Int] } ^ $reqBit }.asTerm).asExprOf[Unit] } - ${ Assign(fieldSymRef, genReadVal[f](oneField.fieldRef.asInstanceOf[RTypeRef[f]], in).asTerm).asExprOf[Unit] } - else throw new MsgPackParseError("Duplicate field " + $fieldName) - }.asTerm - ) - if dvMembers.isEmpty then - // no default... required? Not if Option/Optional, or a collection - val unitVal = oneField.fieldRef match { - case _: OptionRef[?] => - oneField.fieldRef.unitVal.asTerm // not required - case _: AnyRef => - oneField.fieldRef.unitVal.asTerm // not required - case r: LeftRightRef[?] if r.lrkind == LRKind.EITHER => // maybe required - val (optionRecipe, lang) = lrHasOptionChild(r) - if optionRecipe.length == 0 then - required = required | math.pow(2, oneField.index).toInt // required - oneField.fieldRef.unitVal.asTerm - else - val recipeE = Expr(optionRecipe) - if lang == Language.Scala then - '{ - $recipeE.foldRight(None: Any)((c, acc) => if c == 'r' then Right(acc) else Left(acc)).asInstanceOf[f] - }.asTerm - else - '{ - $recipeE.foldRight(java.util.Optional.empty: Any)((c, acc) => if c == 'r' then Right(acc) else Left(acc)).asInstanceOf[f] - }.asTerm - case r: LeftRightRef[?] => // maybe required - val (optionRecipe, lang) = lrHasOptionChild(r) - if optionRecipe.length == 0 then // no Option children -> required - required = required | math.pow(2, oneField.index).toInt // required - oneField.fieldRef.unitVal.asTerm - else // at least one Option child -> optional - if lang == Language.Scala then '{ None }.asTerm - else '{ java.util.Optional.empty.asInstanceOf[f] }.asTerm - case y: TryRef[?] => - tryHasOptionChild(y) match - case (true, Language.Scala) => '{ Success(None) }.asTerm - case (true, Language.Java) => '{ Success(java.util.Optional.empty).asInstanceOf[f] }.asTerm - case _ => - required = required | math.pow(2, oneField.index).toInt // required - oneField.fieldRef.unitVal.asTerm - case _ => - required = required | math.pow(2, oneField.index).toInt // required - oneField.fieldRef.unitVal.asTerm - } - (ValDef(sym, Some(unitVal)), caseDef, fieldSymRef) - else - val methodSymbol = dvMembers.head - val dvSelectNoTArgs = Ref(companionModule).select(methodSymbol) - val dvSelect = methodSymbol.paramSymss match - case Nil => dvSelectNoTArgs - case List(params) if (params.exists(_.isTypeParam)) => - typeArgs(tpe) match - case Nil => ??? // throw JsonParseError("Expected an applied type", ???) - case typeArgs => TypeApply(dvSelectNoTArgs, typeArgs.map(Inferred(_))) - case _ => ??? // fail(s"Default method for ${symbol.name} of class ${tpe.show} have a complex " + - (ValDef(sym, Some(dvSelect)), caseDef, fieldSymRef) - } - } - val reqVarDef = ValDef(reqSym, Some(Literal(IntConstant(totalRequired)))) - val (varDefs, caseDefs, idents) = together.unzip3 - val caseDefsWithFinal = caseDefs :+ CaseDef(Wildcard(), None, '{ $in.skipValue() }.asTerm) // skip values of unrecognized fields - - val argss = List(idents) - val primaryConstructor = tpe.classSymbol.get.primaryConstructor - val constructorNoTypes = Select(New(Inferred(tpe)), primaryConstructor) - val constructor = typeArgs(tpe) match - case Nil => constructorNoTypes - case typeArgs => TypeApply(constructorNoTypes, typeArgs.map(Inferred(_))) - val instantiateClass = argss.tail.foldLeft(Apply(constructor, argss.head))((acc, args) => Apply(acc, args)) - - val exprRequired = Expr(required) - - makeClassFieldMatrixValDef(MethodKey(t, false), t.name.replaceAll("\\.", "_"), t.fields.map(f => changeFieldName(f)).toArray) - val fieldMatrixSym = classFieldMatrixSyms(MethodKey(t, false)).asInstanceOf[Symbol] - - var finalVarDefs = varDefs - val parseLoop = - if !cfg._writeNonConstructorFields || t.nonConstructorFields.isEmpty then - // When we don't care about non-constructor fields - '{ - $in.getNextFormat().getValueType() match - case ValueType.NIL => null - case ValueType.MAP => - (0 to $in.unpackMapHeader()-1).foreach{ _ => - ${ Ref(classFieldMatrixSyms(MethodKey(t, false))).asExprOf[Map[String,Int]] } - .get($in.unpackString()) - .map{ fieldNum => - ${ Match('{ fieldNum }.asTerm, caseDefsWithFinal).asExprOf[Any] } - } - () - } - if (${ Ref(reqSym).asExprOf[Int] } & ${ exprRequired }) == 0 then ${ instantiateClass.asExprOf[T] } - else throw new MsgPackParseError("Missing required field(s) " + ${ allFieldNames }(Integer.numberOfTrailingZeros(${ Ref(reqSym).asExprOf[Int] } & ${ exprRequired }))) - case fmt => throw new MsgPackUnexpectededType("Expected Integer value but got "+fmt) - }.asTerm - else - /* - val instanceSym = Symbol.newVal(Symbol.spliceOwner, "_instance", TypeRepr.of[T], Flags.Mutable, Symbol.noSymbol) - finalVarDefs = finalVarDefs :+ ValDef(instanceSym, Some('{ null }.asTerm)) // add var _instance=null to gen'ed code - val instanceSymRef = Ident(instanceSym.termRef) - // When we do care about non-constructor fields - makeClassFieldMatrixValDef(MethodKey(t, true), t.name.replaceAll("\\.", "_"), t.nonConstructorFields.sortBy(_.index).map(f => changeFieldName(f)).toArray) - val fieldMatrixSymNCF = classFieldMatrixSyms(MethodKey(t, true)).asInstanceOf[Symbol] - // New Case/Match for non-constructor fields - val caseDefsWithFinalNC = t.nonConstructorFields.map(ncf => - ncf.fieldRef.refType match - case '[u] => - CaseDef( - Literal(IntConstant(ncf.index)), - None, - // Call the setter for this field here... - Apply(Select.unique(Ref(instanceSym), ncf.setterLabel), List(genReadVal[u](ncf.fieldRef.asInstanceOf[RTypeRef[u]], in).asTerm)).asExpr.asTerm - ) - ) :+ CaseDef(Wildcard(), None, '{ $in.skipValue() }.asTerm) // skip values of unrecognized fields - '{ - val mark = $in.pos - var maybeFieldNum = $in.expectFirstObjectField(${ Ref(fieldMatrixSym).asExprOf[StringMatrix] }) - if maybeFieldNum == null then null.asInstanceOf[T] - else - while maybeFieldNum.isDefined do - ${ Match('{ maybeFieldNum.get }.asTerm, caseDefsWithFinal).asExprOf[Any] } - maybeFieldNum = $in.expectObjectField(${ Ref(fieldMatrixSym).asExprOf[StringMatrix] }) - - if (${ Ref(reqSym).asExprOf[Int] } & ${ exprRequired }) == 0 then - ${ Assign(instanceSymRef, instantiateClass.asExpr.asTerm).asExprOf[Unit] } // _instance = (new instance) - $in.revertToPos(mark) // go back to re-parse object json, this time for non-constructor fields - maybeFieldNum = $in.expectFirstObjectField(${ Ref(fieldMatrixSymNCF).asExprOf[StringMatrix] }) - while maybeFieldNum.isDefined do - ${ Match('{ maybeFieldNum.get }.asTerm, caseDefsWithFinalNC).asExprOf[Any] } - maybeFieldNum = $in.expectObjectField(${ Ref(fieldMatrixSymNCF).asExprOf[StringMatrix] }) - ${ Ref(instanceSym).asExprOf[T] } - else throw new JsonParseError("Missing required field(s) " + ${ allFieldNames }(Integer.numberOfTrailingZeros(${ Ref(reqSym).asExprOf[Int] } & ${ exprRequired })), $in) - }.asTerm - */ - '{ - null - }.asTerm - - Block(finalVarDefs :+ reqVarDef, parseLoop).asExprOf[T] - ) - - def genWriteVal[T: Type]( - aE: Expr[T], - ref: RTypeRef[T], - out: Expr[MessagePacker], - inTuple: Boolean = false - )(using Quotes): Expr[Unit] = - val methodKey = MethodKey(ref, false) - writeMethodSyms - .get(methodKey) - .map { sym => // hit cache first... then match on Ref type - Apply(Ref(sym), List(aE.asTerm, out.asTerm)).asExprOf[Unit] - } - .getOrElse( - ref match - // First cover all primitive and simple types... - case t: BigDecimalRef => - throw MsgPackUnsupportedType("BigDecimal not supported for MsgPack format") - case t: BigIntRef => - '{ if $aE == null then $out.packNil() else $out.packBigInteger($aE.bigInteger) } - case t: BooleanRef => - '{ $out.packBoolean($aE) } - case t: ByteRef => - '{ $out.packByte($aE) } - case t: CharRef => - '{ $out.packByte($aE.toByte) } - case t: DoubleRef => - '{ $out.packDouble($aE) } - case t: FloatRef => - '{ $out.packFloat($aE) } - case t: IntRef => - '{ $out.packInt($aE) } - case t: LongRef => - '{ $out.packLong($aE) } - case t: ShortRef => - '{ $out.packShort($aE) } - - case t: StringRef => - '{ if $aE == null then $out.packNil() else $out.packString($aE) } - - case t: JBigDecimalRef => - throw MsgPackUnsupportedType("BigDecimal not supported for MsgPack format") - case t: JBigIntegerRef => - '{ if $aE == null then $out.packNil() else $out.packBigInteger($aE) } - case t: JBooleanRef => - '{ $out.packBoolean(${aE.asExprOf[Boolean]}) } - case t: JByteRef => - '{ $out.packByte(${aE.asExprOf[Byte]}) } - case t: JCharacterRef => - '{ $out.packByte($aE.toByte) } - case t: JDoubleRef => - '{ $out.packDouble(${ aE.asExprOf[Double] }) } - case t: JFloatRef => - '{ $out.packFloat(${ aE.asExprOf[Float] }) } - case t: JIntegerRef => - '{ $out.packInt(${ aE.asExprOf[Int] }) } - case t: JLongRef => - '{ $out.packLong(${ aE.asExprOf[Long] }) } - case t: JShortRef => - '{ $out.packShort(${ aE.asExprOf[Short] }) } - case t: JNumberRef => - throw MsgPackUnsupportedType("Number not supported for MsgPack format") - - /* - case t: DurationRef => '{ $out.value(${ aE.asExprOf[java.time.Duration] }) } - case t: InstantRef => '{ $out.value(${ aE.asExprOf[java.time.Instant] }) } - case t: LocalDateRef => '{ $out.value(${ aE.asExprOf[java.time.LocalDate] }) } - case t: LocalDateTimeRef => '{ $out.value(${ aE.asExprOf[java.time.LocalDateTime] }) } - case t: LocalTimeRef => '{ $out.value(${ aE.asExprOf[java.time.LocalTime] }) } - case t: MonthDayRef => '{ $out.value(${ aE.asExprOf[java.time.MonthDay] }) } - case t: OffsetDateTimeRef => '{ $out.value(${ aE.asExprOf[java.time.OffsetDateTime] }) } - case t: OffsetTimeRef => '{ $out.value(${ aE.asExprOf[java.time.OffsetTime] }) } - case t: PeriodRef => '{ $out.value(${ aE.asExprOf[java.time.Period] }) } - case t: YearRef => '{ $out.value(${ aE.asExprOf[java.time.Year] }) } - case t: YearMonthRef => '{ $out.value(${ aE.asExprOf[java.time.YearMonth] }) } - case t: ZonedDateTimeRef => '{ $out.value(${ aE.asExprOf[java.time.ZonedDateTime] }) } - case t: ZoneIdRef => '{ $out.value(${ aE.asExprOf[java.time.ZoneId] }) } - case t: ZoneOffsetRef => '{ $out.value(${ aE.asExprOf[java.time.ZoneOffset] }) } - - case t: URLRef => '{ $out.value(${ aE.asExprOf[java.net.URL] }) } - case t: URIRef => '{ $out.value(${ aE.asExprOf[java.net.URI] }) } - case t: UUIDRef => '{ $out.value(${ aE.asExprOf[java.util.UUID] }) } - case t: ObjectRef[?] => '{ $out.value(${ Expr(t.name) }) } - */ - - /* - case t: AliasRef[?] => - // Special check for RawJson pseudo-type - if lastPart(t.definedType) == "RawJson" then '{ $out.valueRaw(${ aE.asExprOf[RawJson] }) } - else - t.unwrappedType.refType match - case '[e] => - genWriteVal[e](aE.asInstanceOf[Expr[e]], t.unwrappedType.asInstanceOf[RTypeRef[e]], out, inTuple = inTuple) - */ - - // These are here becaue Enums and their various flavors can be Map keys - // (EnumRef handles: Scala 3 enum, Scala 2 Enumeration, Java Enumeration) - /* - case t: EnumRef[?] => - val enumAsId = cfg.enumsAsIds match - case List("-") => false - case Nil => true - case list if list.contains(t.name) => true - case _ => false - val rtype = t.expr - if enumAsId then - if isMapKey then '{ $out.value($rtype.asInstanceOf[EnumRType[?]].ordinal($aE.toString).get.toString) } // stringified id - else '{ $out.value($rtype.asInstanceOf[EnumRType[?]].ordinal($aE.toString).get) } // int value of id - else '{ if $aE == null then $out.burpNull() else $out.value($aE.toString) } - */ - - // NeoType is a bit of a puzzle-box. To get the correct underlying base type, I had to dig into - // the argument of method validate$retainedBody. It happened to have the correctly-typed parameter. - // With the correct type, we can correct write out the value. - /* - case t: NeoTypeRef[?] => // in Quotes context - Symbol.requiredModule(t.typedName.toString).methodMember("validate$retainedBody").head.paramSymss.head.head.tree match - case ValDef(_, tt, _) => - tt.tpe.asType match - case '[u] => - val baseTypeRef = ReflectOnType.apply(q)(tt.tpe)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) - genWriteVal[u]('{ $aE.asInstanceOf[u] }, baseTypeRef.asInstanceOf[RTypeRef[u]], out) - */ - - // case t: AnyRef => '{ AnyWriter.writeAny($aE, $out, ${ Expr(cfg) }) } - - // Everything else... - case _ => genEncFnBody(ref, aE, out, inTuple = inTuple) - ) - - def genReadVal[T: Type]( - ref: RTypeRef[T], - in: Expr[MessageUnpacker], - inTuple: Boolean = false // not sure if needed... - )(using Quotes): Expr[T] = - val methodKey = MethodKey(ref, false) - readMethodSyms - .get(methodKey) - .map { sym => // hit cache first... then match on Ref type - Apply(Ref(sym), List(in.asTerm)).asExprOf[T] - } - .getOrElse( - ref match - // First cover all primitive and simple types... - case t: BigDecimalRef => - throw MsgPackUnsupportedType("BigDecimal not supported for MsgPack format") - case t: BigIntRef => - '{ - $in.getNextFormat().getValueType() match - case ValueType.NIL => null - case ValueType.INTEGER => scala.math.BigInt($in.unpackBigInteger()) - case fmt => throw new MsgPackUnexpectededType("Expected Integer value but got "+fmt) - }.asExprOf[T] - case t: BooleanRef => - '{ - $in.getNextFormat().getValueType() match - case ValueType.BOOLEAN => $in.unpackBoolean() - case fmt => throw new MsgPackUnexpectededType("Expected Boolean value but got "+fmt) - }.asExprOf[T] - case t: ByteRef => - '{ - $in.getNextFormat().getValueType() match - case ValueType.INTEGER => $in.unpackByte() - case fmt => throw new MsgPackUnexpectededType("Expected Integer (byte) value but got "+fmt) - }.asExprOf[T] - case t: CharRef => - '{ - $in.getNextFormat().getValueType() match - case ValueType.INTEGER => $in.unpackByte().toChar - case fmt => throw new MsgPackUnexpectededType("Expected Integer (byte/char) value but got "+fmt) - }.asExprOf[T] - case t: DoubleRef => - '{ - $in.getNextFormat().getValueType() match - case ValueType.FLOAT => $in.unpackDouble() - case fmt => throw new MsgPackUnexpectededType("Expected Float (double) value but got "+fmt) - }.asExprOf[T] - case t: FloatRef => - '{ - $in.getNextFormat().getValueType() match - case ValueType.FLOAT => $in.unpackFloat() - case fmt => throw new MsgPackUnexpectededType("Expected Float value but got "+fmt) - }.asExprOf[T] - case t: IntRef => - '{ - $in.getNextFormat().getValueType() match - case ValueType.INTEGER => $in.unpackInt() - case fmt => throw new MsgPackUnexpectededType("Expected Integer value but got "+fmt) - }.asExprOf[T] - case t: LongRef => - '{ - $in.getNextFormat().getValueType() match - case ValueType.INTEGER => $in.unpackLong() - case fmt => throw new MsgPackUnexpectededType("Expected Integer (long) value but got "+fmt) - }.asExprOf[T] - case t: ShortRef => - '{ - $in.getNextFormat().getValueType() match - case ValueType.INTEGER => $in.unpackShort() - case fmt => throw new MsgPackUnexpectededType("Expected Integer (short) value but got "+fmt) - }.asExprOf[T] - case t: StringRef => - '{ - $in.getNextFormat().getValueType() match - case ValueType.NIL => null - case ValueType.STRING => $in.unpackString() - case fmt => throw new MsgPackUnexpectededType("Expected String value but got "+fmt) - }.asExprOf[T] - - case t: JBigDecimalRef => - throw MsgPackUnsupportedType("BigDecimal not supported for MsgPack format") - case t: JBigIntegerRef => - '{ - $in.getNextFormat().getValueType() match - case ValueType.NIL => null - case ValueType.INTEGER => $in.unpackBigInteger() - case fmt => throw new MsgPackUnexpectededType("Expected Integer value but got "+fmt) - }.asExprOf[T] - case t: JBooleanRef => - '{ - $in.getNextFormat().getValueType() match - case ValueType.BOOLEAN => java.lang.Boolean.valueOf($in.unpackBoolean()) - case fmt => throw new MsgPackUnexpectededType("Expected Boolean value but got "+fmt) - }.asExprOf[T] - case t: JByteRef => - '{ - $in.getNextFormat().getValueType() match - case ValueType.NIL => null - case ValueType.INTEGER => java.lang.Byte.valueOf($in.unpackByte()) - case fmt => throw new MsgPackUnexpectededType("Expected Integer (byte) value but got "+fmt) - }.asExprOf[T] - case t: JCharacterRef => - '{ - $in.getNextFormat().getValueType() match - case ValueType.NIL => null - case ValueType.INTEGER => java.lang.Character.valueOf($in.unpackByte().toChar) - case fmt => throw new MsgPackUnexpectededType("Expected Integer (byte/char) value but got "+fmt) - }.asExprOf[T] - case t: JDoubleRef => - '{ - $in.getNextFormat().getValueType() match - case ValueType.NIL => null - case ValueType.FLOAT => java.lang.Double.valueOf($in.unpackDouble()) - case fmt => throw new MsgPackUnexpectededType("Expected Float (double) value but got "+fmt) - }.asExprOf[T] - case t: JFloatRef => - '{ - $in.getNextFormat().getValueType() match - case ValueType.NIL => null - case ValueType.FLOAT => java.lang.Float.valueOf($in.unpackFloat()) - case fmt => throw new MsgPackUnexpectededType("Expected Float value but got "+fmt) - }.asExprOf[T] - case t: JIntegerRef => - '{ - $in.getNextFormat().getValueType() match - case ValueType.NIL => null - case ValueType.INTEGER =>java.lang.Integer.valueOf( $in.unpackInt()) - case fmt => throw new MsgPackUnexpectededType("Expected Integer value but got "+fmt) - }.asExprOf[T] - case t: JLongRef => - '{ - $in.getNextFormat().getValueType() match - case ValueType.NIL => null - case ValueType.INTEGER => java.lang.Long.valueOf($in.unpackLong()) - case fmt => throw new MsgPackUnexpectededType("Expected Integer (long) value but got "+fmt) - }.asExprOf[T] - case t: JShortRef => - '{ - $in.getNextFormat().getValueType() match - case ValueType.NIL => null - case ValueType.INTEGER => java.lang.Short.valueOf($in.unpackShort()) - case fmt => throw new MsgPackUnexpectededType("Expected Integer (short) value but got "+fmt) - }.asExprOf[T] - case t: JNumberRef => - throw MsgPackUnsupportedType("Number not supported for MsgPack format") - - case t: SeqRef[?] => - ref.refType match - case '[List[?]] => - t.elementRef.refType match - case '[e] => - val rtypeRef = t.elementRef.asInstanceOf[RTypeRef[e]] - '{ - $in.getNextFormat().getValueType() match - case ValueType.NIL => null - case ValueType.ARRAY => - (0 to $in.unpackArrayHeader()-1).map(_ => ${ genReadVal[e](rtypeRef, in, inTuple) }).toList - case fmt => throw new MsgPackUnexpectededType("Expected Array value but got "+fmt) - }.asExprOf[T] - - case t: ArrayRef[?] => - t.elementRef.refType match - case '[e] => - val rtypeRef = t.elementRef.asInstanceOf[RTypeRef[e]] - val ct = Expr.summon[ClassTag[e]].get - '{ - $in.getNextFormat().getValueType() match - case ValueType.NIL => null - case ValueType.ARRAY => - val parsedArray = (0 to $in.unpackArrayHeader()-1).map(_ => ${ genReadVal[e](rtypeRef, in, inTuple) }) - implicit val ctt = $ct - parsedArray.toArray[e] - case fmt => throw new MsgPackUnexpectededType("Expected Array value but got "+fmt) - }.asExprOf[T] - - case _ => - // Classes, traits, etc. - genDecFnBody[T](ref, in) // Create a new decoder function (presumably for class, trait, etc) - genReadVal(ref, in, inTuple) - ) - - val codecDef = '{ // FIXME: generate a type class instance using `ClassDef.apply` and `Symbol.newClass` calls after graduating from experimental API: https://www.scala-lang.org/blog/2022/06/21/scala-3.1.3-released.html - new MsgPackCodec[T] { - def encodeValue(in: T, out: MessagePacker): Unit = ${ genWriteVal('in, ref, 'out) } - def decodeValue(in: MessageUnpacker): T = ${ genReadVal(ref, 'in) } - } - }.asTerm - val codec = Block((classFieldMatrixValDefs ++ writeMethodDefs ++ readMethodDefs).toList, codecDef).asExprOf[MsgPackCodec[T]] - // println(s"Codec: ${codec.show}") - codec diff --git a/src/main/scala/co.blocke.scalajack/msgpack/MsgPackError.scala b/src/main/scala/co.blocke.scalajack/msgpack/MsgPackError.scala deleted file mode 100644 index deb0ad39..00000000 --- a/src/main/scala/co.blocke.scalajack/msgpack/MsgPackError.scala +++ /dev/null @@ -1,32 +0,0 @@ -package co.blocke.scalajack -package msgpack - -import scala.util.control.NoStackTrace - -// class JsonIllegalKeyType(msg: String) extends Throwable(msg) with NoStackTrace -// class JsonNullKeyValue(msg: String) extends Throwable(msg) with NoStackTrace -class MsgPackUnsupportedType(msg: String) extends Throwable(msg) with NoStackTrace -class MsgPackUnexpectededType(msg: String) extends Throwable(msg) with NoStackTrace -class MsgPackParseError(msg: String) extends Throwable(msg) with NoStackTrace -// class JsonEitherLeftError(msg: String) extends Throwable(msg) with NoStackTrace -// class JsonIllegalCharacterError(msg: String) extends Throwable(msg) with NoStackTrace - -// class ParseError(val msg: String) extends Throwable(msg) with NoStackTrace: -// val show: String = "" - -// Thrown at compile-time only! -// case class MsgPackTypeError(override val msg: String) extends ParseError(msg) with NoStackTrace: -// override val show: String = "" - -// Thrown at runtime only! -// case class JsonParseError(override val msg: String, context: reading.JsonSource) extends ParseError(msg + " at position " + context.pos) with NoStackTrace: -// override val show: String = -// val js = context.js.toString -// val (clip, dashes) = context.pos match { -// case ep if ep <= 50 && context.max < 80 => (js, ep) -// case ep if ep <= 50 => (js.substring(0, 77) + "...", ep) -// case ep if ep > 50 && ep + 30 >= context.max => -// ("..." + js.substring(context.pos - 49), 52) -// case ep => ("..." + js.substring(ep - 49, ep + 27) + "...", 52) -// } -// msg + s" at position [${context.pos}]" + "\n" + clip.replaceAll("[\n\t]", "~") + "\n" + ("-" * dashes) + "^" diff --git a/src/main/scala/co.blocke.scalajack/msgpack/package.scalax b/src/main/scala/co.blocke.scalajack/msgpack/package.scalax deleted file mode 100644 index f0bfebba..00000000 --- a/src/main/scala/co.blocke.scalajack/msgpack/package.scalax +++ /dev/null @@ -1,50 +0,0 @@ -package co.blocke.scalajack -package msgpack - -val MP_NIL: Byte = 0xc0 -val MP_FALSE: Byte = 0xc2 -val MP_TRUE: Byte = 0xc3 -val MP_BIN8: Byte = 0xc4 -val MP_BIN16: Byte = 0xc5 -val MP_BIN32: Byte = 0xc6 -val MP_EXT8: Byte = 0xc7 -val MP_EXT16: Byte = 0xc8 -val MP_EXT32: Byte = 0xc9 -val MP_FLOAT32: Byte = 0xca -val MP_FLOAT64: Byte = 0xcb -val MP_UINT8: Byte = 0xcc -val MP_UINT16: Byte = 0xcd -val MP_UINT32: Byte = 0xce -val MP_UINT64: Byte = 0xcf -val MP_INT8: Byte = 0xd0 -val MP_INT16: Byte = 0xd1 -val MP_INT32: Byte = 0xd2 -val MP_INT64: Byte = 0xd3 -val MP_FIXEXT1: Byte = 0xd4 -val MP_FIXEXT2: Byte = 0xd5 -val MP_FIXEXT4: Byte = 0xd6 -val MP_FIXEXT8: Byte = 0xd7 -val MP_FIXEXT16: Byte = 0xd8 -val MP_STR8: Byte = 0xd9 -val MP_STR16: Byte = 0xda -val MP_STR32: Byte = 0xdb -val MP_ARR16: Byte = 0xdc -val MP_ARR32: Byte = 0xdd -val MP_MAP16: Byte = 0xde -val MP_MAP32: Byte = 0xdf - -// Masks -val MP_POS_FIXINT_MASK: Byte = b"10000000" -val MP_FIXMAP_MASK: Byte = b"11110000" -val MP_FIXMAP: Byte = b"10000000" -val MP_FIXMAP_VALUE: Byte = b"00001111" -val MP_FIXARR_MASK: Byte = b"11110000" -val MP_FIXARR: Byte = b"10010000" -val MP_FIXARR_VALUE: Byte = b"00001111" -val MP_FIXSTR_MASK: Byte = b"11100000" -val MP_FIXSTR: Byte = b"10100000" -val MP_FIXSTR_VALUE: Byte = b"00011111" -val MP_NEGFIXINT_MASK: Byte = b"11100000" -val MP_NEGFIXINT: Byte = b"11100000" -val MP_NEGFIXINT_VALUE: Byte = b"00011111" - diff --git a/src/main/scala/co.blocke.scalajack/run/Play.scala b/src/main/scala/co.blocke.scalajack/run/Play.scalax similarity index 63% rename from src/main/scala/co.blocke.scalajack/run/Play.scala rename to src/main/scala/co.blocke.scalajack/run/Play.scalax index ab2db9a2..6df3623e 100644 --- a/src/main/scala/co.blocke.scalajack/run/Play.scala +++ b/src/main/scala/co.blocke.scalajack/run/Play.scalax @@ -12,10 +12,4 @@ object RunMe extends App: given sj: ScalaJack[Pet] = sjCodecOf[Pet] - val a = sj.toMsgPack(Pet("Mindy","Frenchie",3)) - println("LEN: "+a.length) - println(a.toList.map(i => f"$i%02X").mkString(" ")) - - println("BACK: "+ sj.fromMsgPack(a)) println("ok") - \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/run/Record.scala b/src/main/scala/co.blocke.scalajack/run/Record.scalax similarity index 99% rename from src/main/scala/co.blocke.scalajack/run/Record.scala rename to src/main/scala/co.blocke.scalajack/run/Record.scalax index 63eefcc7..10fe5db8 100644 --- a/src/main/scala/co.blocke.scalajack/run/Record.scala +++ b/src/main/scala/co.blocke.scalajack/run/Record.scalax @@ -183,4 +183,3 @@ val record = Record( List(Friend("Jane Smith", 28, "jane.smith@example.com"), Friend("Bob Johnson", 32, "bob.johnson@example.com")), List(Pet("Fido", "Dog", 5), Pet("Whiskers", "Cat", 3)) ) - diff --git a/src/test/scala/co.blocke.scalajack/json/misc/MiscSpec.scala b/src/test/scala/co.blocke.scalajack/json/misc/MiscSpec.scala index e7bda4a2..31d7380a 100644 --- a/src/test/scala/co.blocke.scalajack/json/misc/MiscSpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/misc/MiscSpec.scala @@ -44,9 +44,9 @@ on another level."}""") val sj = sjCodecOf[Validated] val js = """{"name":"","xspot":["x","y","z"],"nada":["","",""]}""" val msg = - """NeoType validation for NonEmptyString failed at position [10] + """NeoType validation for NonEmptyString failed at position [9] |{"name":"","xspot":["x","y","z"],"nada":["","",""]} - |----------^""".stripMargin + |---------^""".stripMargin val ex = intercept[JsonParseError](sj.fromJson(js)) ex.show shouldEqual msg } From 84900833cbfbdd7dd4dbb77bd45c8bcb972d9aeb Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Wed, 29 May 2024 23:31:31 -0500 Subject: [PATCH 63/65] wrapping up --- .github/workflows/ci.yml | 96 +++--- .github/workflows/coveralls.yml | 59 ++++ README.md | 29 +- benchmark/build.sbt | 2 +- .../src/main/scala/co.blocke/Benchmark.scala | 25 +- build.sbt | 17 +- project/plugins.sbt | 3 +- .../scala/co.blocke.scalajack/ScalaJack.scala | 2 - .../internal/CodePrinter.scala | 2 + .../{json/reading => internal}/Numbers.scala | 8 +- .../json/JsonCodecMaker.scala | 111 +++--- .../co.blocke.scalajack/json/package.scala | 2 - .../json/reading/JsonSource.scala | 3 +- .../json/schema/JsonSchema.scala | 323 ------------------ .../json/schema/Schema.scala | 114 ------- .../json/writing/JsonOutput.scala | 5 - .../json/collections/MapSpec.scala | 124 ++++++- .../json/collections/Model.scala | 9 +- .../json/collections/SeqSetArraySpec.scala | 37 ++ .../json/collections/TupleSpec.scala | 7 + .../co.blocke.scalajack/json/misc/Model.scala | 1 + .../json/misc/OptionSpec.scala | 7 + .../json/misc/TrySpec.scala | 7 + .../json/primitives/ScalaPrimSpec.scala | 50 +++ .../json/primitives/SimpleSpec.scala | 4 +- 25 files changed, 443 insertions(+), 604 deletions(-) create mode 100644 .github/workflows/coveralls.yml rename src/main/scala/co.blocke.scalajack/{json/reading => internal}/Numbers.scala (99%) delete mode 100644 src/main/scala/co.blocke.scalajack/json/schema/JsonSchema.scala delete mode 100644 src/main/scala/co.blocke.scalajack/json/schema/Schema.scala diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f73ca6f3..3ce76a45 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,66 +17,57 @@ on: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + +concurrency: + group: ${{ github.workflow }} @ ${{ github.ref }} + cancel-in-progress: true + jobs: build: name: Build and Test strategy: matrix: - os: [ubuntu-latest, windows-latest] - scala: [3.3.0] - java: [zulu@8] + os: [ubuntu-latest] + scala: [3.4.2] + java: [zulu@21] runs-on: ${{ matrix.os }} + timeout-minutes: 60 steps: - - name: Ignore line ending differences in git - if: contains(runner.os, 'windows') - shell: bash - run: git config --global core.autocrlf false - - name: Checkout current branch (full) - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - - name: Setup Java (zulu@8) - if: matrix.java == 'zulu@8' - uses: actions/setup-java@v3 + - name: Setup Java (zulu@21) + id: setup-java-zulu-21 + if: matrix.java == 'zulu@21' + uses: actions/setup-java@v4 with: distribution: zulu - java-version: 8 + java-version: 21 + cache: sbt - - name: Cache sbt - uses: actions/cache@v3 - with: - path: | - ~/.sbt - ~/.ivy2/cache - ~/.coursier/cache/v1 - ~/.cache/coursier/v1 - ~/AppData/Local/Coursier/Cache/v1 - ~/Library/Caches/Coursier/v1 - key: ${{ runner.os }}-sbt-cache-v2-${{ hashFiles('**/*.sbt') }}-${{ hashFiles('project/build.properties') }} + - name: sbt update + if: matrix.java == 'zulu@21' && steps.setup-java-zulu-21.outputs.cache-hit == 'false' + run: sbt +update - name: Check that workflows are up to date - shell: bash run: sbt githubWorkflowCheck - name: Build project - shell: bash - run: sbt '++${{ matrix.scala }}' test + run: sbt '++ ${{ matrix.scala }}' test - name: Make target directories if: github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) - shell: bash run: mkdir -p target project/target - name: Compress target directories if: github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) - shell: bash run: tar cf targets.tar target project/target - name: Upload target directories if: github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: target-${{ matrix.os }}-${{ matrix.java }}-${{ matrix.scala }} path: targets.tar @@ -88,44 +79,33 @@ jobs: strategy: matrix: os: [ubuntu-latest] - scala: [3.3.0] - java: [zulu@8] + java: [zulu@21] runs-on: ${{ matrix.os }} steps: - - name: Ignore line ending differences in git - if: contains(runner.os, 'windows') - run: git config --global core.autocrlf false - - name: Checkout current branch (full) - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - - name: Setup Java (zulu@8) - if: matrix.java == 'zulu@8' - uses: actions/setup-java@v3 + - name: Setup Java (zulu@21) + id: setup-java-zulu-21 + if: matrix.java == 'zulu@21' + uses: actions/setup-java@v4 with: distribution: zulu - java-version: 8 + java-version: 21 + cache: sbt - - name: Cache sbt - uses: actions/cache@v3 - with: - path: | - ~/.sbt - ~/.ivy2/cache - ~/.coursier/cache/v1 - ~/.cache/coursier/v1 - ~/AppData/Local/Coursier/Cache/v1 - ~/Library/Caches/Coursier/v1 - key: ${{ runner.os }}-sbt-cache-v2-${{ hashFiles('**/*.sbt') }}-${{ hashFiles('project/build.properties') }} - - - name: Download target directories (3.3.0) - uses: actions/download-artifact@v3 + - name: sbt update + if: matrix.java == 'zulu@21' && steps.setup-java-zulu-21.outputs.cache-hit == 'false' + run: sbt +update + + - name: Download target directories (3.4.2) + uses: actions/download-artifact@v4 with: - name: target-${{ matrix.os }}-${{ matrix.java }}-3.3.0 + name: target-${{ matrix.os }}-${{ matrix.java }}-3.4.2 - - name: Inflate target directories (3.3.0) + - name: Inflate target directories (3.4.2) run: | tar xf targets.tar rm targets.tar @@ -136,4 +116,4 @@ jobs: SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} PGP_SECRET: ${{ secrets.PGP_SECRET }} - run: sbt '++${{ matrix.scala }}' ci-release + run: sbt ci-release diff --git a/.github/workflows/coveralls.yml b/.github/workflows/coveralls.yml new file mode 100644 index 00000000..b6d50810 --- /dev/null +++ b/.github/workflows/coveralls.yml @@ -0,0 +1,59 @@ +name: Coveralls Publish + +on: + push: + branches: [main] + tags: ["v*"] + +env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + +concurrency: + group: ${{ github.workflow }} @ ${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + name: Build and Test + strategy: + matrix: + os: [ubuntu-latest, windows-latest] + scala: [3.4.2] + java: [zulu@21] + runs-on: ${{ matrix.os }} + timeout-minutes: 60 + steps: + - name: Ignore line ending differences in git + if: contains(runner.os, 'windows') + shell: bash + run: git config --global core.autocrlf false + + - name: Checkout current branch (full) + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Java (zulu@21) + id: setup-java-zulu-21 + if: matrix.java == 'zulu@21' + uses: actions/setup-java@v4 + with: + distribution: zulu + java-version: 21 + cache: sbt + + - name: sbt update + if: matrix.java == 'zulu@21' && steps.setup-java-zulu-21.outputs.cache-hit == 'false' + shell: bash + run: sbt +update + + - name: Build project + run: sbt '++ ${{ matrix.scala }}' coverage test + + - run: sbt '++ ${{ matrix.scala }}' coverageReport + + - name: Coveralls + uses: coverallsapp/github-action@v2 + with: + git-branch: main \ No newline at end of file diff --git a/README.md b/README.md index 5d53c2ea..808b39da 100644 --- a/README.md +++ b/README.md @@ -5,20 +5,11 @@ [![Maven Central](https://maven-badges.herokuapp.com/maven-central/co.blocke/scalajack_3/badge.svg)](https://search.maven.org/artifact/co.blocke/scalajack_3/8.0.0/jar) ScalaJack 8 is an all-new ScalaJack serializer implemenation built on Scala 3. For Scala 2.13 ScalaJack, please use the frozen version 6.2.0. ScalaJack 8 is built -using Scala 3.4.1 on JDK 21 LTS version. +using Scala 3.4.2 on JDK 21 LTS version. This is done to be as current as possible and also because Scala 3.4.2 provides improvements to code test coverage instrumentation. -ScalaJack is a very fast, seamless serialization engine for unstructured data types designed to require a bare minimum of extra code -to serialize a class. ScalaJack supports JSON in its focus on over-the-wire data and message/event transport. (We looked at offering MsgPack support, but to our surprise benchmarks -showed that MsgPack serialization had about 25% slower write performance and 45% slower read performance than JSON, so for now we're sticking with just JSON support.) - -Advanced Features: - -- Handles tuples -- 'Any' support -- Handles default values for case class fields -- Rich configuration of trait type hint/value -- Supports value classes -- Sealed trait-style enumerations +ScalaJack is a very fast, seamless serialization engine for non-schema data designed to require a bare minimum of extra code +to serialize a class. ScalaJack currently only supports JSON, however when we looked at adding MsgPack support to our great surprise benchmarks +showed that MsgPack serialization had about 25% slower write performance and 45% slower read performance than JSON, so we're sticking with JSON for the time being. ## Use ScalaJack is extremely simple to use. @@ -27,7 +18,7 @@ Include the following in your build.sbt: ``` libraryDependencies ++= Seq("co.blocke" %% "scalajack" % SJ_VERSION) ``` -Now you're good to go! Let's use ScalaJack in your project to serialize/de-serialize a case class object into JSON: +Now you're good to go! Let's use ScalaJack in your project to serialize/deserialize a case class object into JSON: ```scala // File1.scala case class Person(name: String, age: Int) @@ -44,15 +35,15 @@ sjPerson.fromJson(js) // re-constitutes original Person Couldn't be simpler! | **NOTE:** Classes must be defined in a different file from where ScalaJack is called. -| This is a Scala macro requirement, not a ScalaJack "ism" +| This is a Scala macro requirement, not a ScalaJack limitation. ### A word about performance... -Compared to pre-8.0 ScalaJack, which used Scala 2.x runtime reflection, ScalaJack is dramatically faster in almost every case. How's this work? ScalaJack 8 uses macros, that at compile-time generate all the serialization code for you (the codecs). It's very much like writing hand-tooled, field-by-field serialization code yourself, except ScalaJack does it at compile-time. Wherever you see ```sjCodecOf``` is where the compiler will generate all the serialization code. **(That also means try not to use sjCodecOf more than once for any given class or you'll generate a lot of redundant code!)** +Compared to pre-8.0 ScalaJack, which used Scala 2.x runtime reflection, ScalaJack is dramatically faster in almost every case. How does this work? ScalaJack 8 uses compile-time macros to generate all the serialization code for you (the codecs). It's very much like writing hand-tooled, field-by-field serialization code yourself, except ScalaJack does it at compile-time. Wherever you see ```sjCodecOf``` is where the compiler will generate all the serialization code. **(That also means you should try not to use sjCodecOf more than once for any given class or you'll generate a lot of redundant code!)** ### Easy codecs You only need to worry about generating codecs for your top-most level classes. Some serialization libraries require all nested classes in an object hierarchy to be -specifically called out for code generation, which can get pretty burdensome. ScalaJack doesn't require this. For example: +specifically called out for codec generation, which can get pretty burdensome. ScalaJack doesn't require this. For example: ```scala case class Dog(name: String, numLegs: Int) @@ -78,7 +69,7 @@ val js = sjFoo.fromJson(someJson) In a non-macro program (e.g. something using Scala 2 runtime reflection) let's say you add a new field to class Foo in File1.scala. You naturally expect sbt to re-compile this file, and anything that depends on Foo, and the changes will be picked up in your program, and all will be well. -That's **not** necessarily what happens with macros! Remember, the macro code is run/expnded at compile-time. File2.scala needs to be re-compiled because the macro that gets expanded at sjCodecOf[Foo] needs to be re-generated to pick up your changes to Foo class in File1.scala. **Unfortunately sbt cna't detect up this dependency!** If you don't know any better you'll just re-run your program after a change to File1.scala, like normal, and you'll get a spectacular exception with exotic errors that won't mean much to you. The simple, but non-intuitive, solution is you need to also recompile File2.scala. +That's **not** necessarily what happens with macros! Remember, the macro code is run/expnded at compile-time. File2.scala needs to be re-compiled because the macro that gets expanded at sjCodecOf[Foo] needs to be re-generated to pick up your changes to Foo class in File1.scala. **Unfortunately sbt can't detect this dependency!** If you don't know any better you'll just re-run your program after a change to File1.scala, like normal, and you'll get a spectacular exception with exotic errors that won't mean much to you. The simple, but non-intuitive, solution is you need to also recompile File2.scala. This means you will be doing more re-compiling with macro-based code than you would without the macros. It's an unfortunate cost of inconvenience, but the payoff is a *dramatic* gain in speed at runtime, and in the case of reflection in Scala 3, using macros is the only way to accomplish reflection, so there really isn't an alternative. @@ -97,7 +88,7 @@ This means you will be doing more re-compiling with macro-based code than you wo ### Notes: -* 8.0.0 -- Rebuild on Scala 3.4.1 and major refactor of ScalaJack 7.0 +* 8.0.0 -- Rebuild on Scala 3.4.2 and deep refactor of ScalaJack 7.0 * 7.0.3 -- Rebuild on Scala 3.2.1 * 7.0.1 -- GA release of ScalaJack 7 for Scala 3. * 7.0.0-M2 -- Initial release for Scala 3 \ No newline at end of file diff --git a/benchmark/build.sbt b/benchmark/build.sbt index 42abc17a..548c1c1e 100644 --- a/benchmark/build.sbt +++ b/benchmark/build.sbt @@ -13,7 +13,7 @@ val compilerOptions = Seq( val circeVersion = "0.15.0-M1" val scalaTestVersion = "3.2.11" -ThisBuild / scalaVersion := "3.4.1" +ThisBuild / scalaVersion := "3.4.2" def priorTo2_13(scalaVersion: String): Boolean = CrossVersion.partialVersion(scalaVersion) match { diff --git a/benchmark/src/main/scala/co.blocke/Benchmark.scala b/benchmark/src/main/scala/co.blocke/Benchmark.scala index 793e3914..72e57d30 100644 --- a/benchmark/src/main/scala/co.blocke/Benchmark.scala +++ b/benchmark/src/main/scala/co.blocke/Benchmark.scala @@ -44,21 +44,20 @@ trait HandTooledWritingBenchmark { @OutputTimeUnit(TimeUnit.SECONDS) class ReadingBenchmark extends ScalaJackZ.ScalaJackReadingBenchmark - // with CirceZ.CirceReadingBenchmark - // extends JsoniterZ.JsoniterReadingBenchmark - // with ZIOZ.ZIOJsonReadingBenchmark - // with PlayZ.PlayReadingBenchmark - // with ArgonautZ.ArgonautReadingBenchmark + with CirceZ.CirceReadingBenchmark + with JsoniterZ.JsoniterReadingBenchmark + with ZIOZ.ZIOJsonReadingBenchmark + with PlayZ.PlayReadingBenchmark + with ArgonautZ.ArgonautReadingBenchmark @State(Scope.Thread) @BenchmarkMode(Array(Mode.Throughput)) @OutputTimeUnit(TimeUnit.SECONDS) class WritingBenchmark - // extends HandTooledWritingBenchmark - // extends CirceZ.CirceWritingBenchmark - // extends ScalaJackZ.MsgPackWritingBenchmark - extends ScalaJackZ.ScalaJackWritingBenchmark - // extends JsoniterZ.JsoniterWritingBenchmark - // with ZIOZ.ZIOJsonWritingBenchmark - // with PlayZ.PlayWritingBenchmark - // with ArgonautZ.ArgonautWritingBenchmark + extends HandTooledWritingBenchmark + with CirceZ.CirceWritingBenchmark + with ScalaJackZ.ScalaJackWritingBenchmark + with JsoniterZ.JsoniterWritingBenchmark + with ZIOZ.ZIOJsonWritingBenchmark + with PlayZ.PlayWritingBenchmark + with ArgonautZ.ArgonautWritingBenchmark diff --git a/build.sbt b/build.sbt index 8a564953..aa197695 100644 --- a/build.sbt +++ b/build.sbt @@ -1,4 +1,6 @@ import org.typelevel.sbt.gha.JavaSpec.Distribution.Zulu +import scoverage.ScoverageKeys._ + lazy val isCI = sys.env.get("CI").contains("true") inThisBuild(List( @@ -19,7 +21,8 @@ inThisBuild(List( name := "scalajack" ThisBuild / organization := "co.blocke" -ThisBuild / scalaVersion := "3.4.1" +ThisBuild / scalaVersion := "3.4.2" +ThisBuild / githubWorkflowScalaVersions := Seq("3.4.2") lazy val root = project .in(file(".")) @@ -35,7 +38,7 @@ lazy val root = project Test / parallelExecution := false, scalafmtOnCompile := !isCI, libraryDependencies ++= Seq( - "co.blocke" %% "scala-reflection" % "2.0.6", + "co.blocke" %% "scala-reflection" % "2.0.8", "org.apache.commons" % "commons-text" % "1.11.0", "io.github.kitlangton" %% "neotype" % "0.0.9", "org.scalatest" %% "scalatest" % "3.2.17" % Test, @@ -44,7 +47,7 @@ lazy val root = project ) ) -ThisBuild / githubWorkflowJavaVersions := Seq(JavaSpec(Zulu, "8")) +ThisBuild / githubWorkflowJavaVersions := Seq(JavaSpec(Zulu, "21")) ThisBuild / githubWorkflowOSes := Seq("ubuntu-latest") ThisBuild / githubWorkflowPublishTargetBranches := Seq( RefPredicate.Equals(Ref.Branch("main")), @@ -77,7 +80,13 @@ lazy val compilerOptions = Seq( "-feature", "-language:implicitConversions", "-deprecation", - // "-explain", + // "-explain",' + "-coverage-exclude-files", + ".*SJConfig.*", + "-coverage-exclude-classlikes", + ".*internal.*", + "-coverage-exclude-classlikes", + ".*AnyWriter", "-encoding", "utf8" ) diff --git a/project/plugins.sbt b/project/plugins.sbt index 2424290e..0b0faec3 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,5 +1,6 @@ addSbtPlugin("co.blocke" % "gitflow-packager" % "0.1.32") addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.5.11") addSbtPlugin("org.typelevel" % "sbt-typelevel-sonatype-ci-release" % "0.5.0-M6") -addSbtPlugin("org.typelevel" % "sbt-typelevel-github-actions" % "0.4.16") +addSbtPlugin("org.typelevel" % "sbt-typelevel-github-actions" % "0.7.1") addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.2") +addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.0.12") \ No newline at end of file diff --git a/src/main/scala/co.blocke.scalajack/ScalaJack.scala b/src/main/scala/co.blocke.scalajack/ScalaJack.scala index 432fe60e..9b36c0be 100644 --- a/src/main/scala/co.blocke.scalajack/ScalaJack.scala +++ b/src/main/scala/co.blocke.scalajack/ScalaJack.scala @@ -20,8 +20,6 @@ case class ScalaJack[T](jsonCodec: JsonCodec[T]): object ScalaJack: - def apply[A](implicit a: ScalaJack[A]): ScalaJack[A] = a - // ----- Use default JsonConfig inline def sjCodecOf[T]: ScalaJack[T] = ${ codecOfImpl[T] } def codecOfImpl[T: Type](using Quotes): Expr[ScalaJack[T]] = diff --git a/src/main/scala/co.blocke.scalajack/internal/CodePrinter.scala b/src/main/scala/co.blocke.scalajack/internal/CodePrinter.scala index 162bee24..fde8eb7f 100644 --- a/src/main/scala/co.blocke.scalajack/internal/CodePrinter.scala +++ b/src/main/scala/co.blocke.scalajack/internal/CodePrinter.scala @@ -10,6 +10,7 @@ import scala.quoted.* * sj[Record] * } */ +//$COVERAGE-OFF$3rd party library object CodePrinter { inline def structure[A](inline value: A) = ${ structureMacro('value) } @@ -30,3 +31,4 @@ object CodePrinter { value } } +//$COVERAGE-ON$ diff --git a/src/main/scala/co.blocke.scalajack/json/reading/Numbers.scala b/src/main/scala/co.blocke.scalajack/internal/Numbers.scala similarity index 99% rename from src/main/scala/co.blocke.scalajack/json/reading/Numbers.scala rename to src/main/scala/co.blocke.scalajack/internal/Numbers.scala index 23bfe631..fba4ba19 100644 --- a/src/main/scala/co.blocke.scalajack/json/reading/Numbers.scala +++ b/src/main/scala/co.blocke.scalajack/internal/Numbers.scala @@ -14,11 +14,14 @@ * limitations under the License. */ package co.blocke.scalajack -package json -package reading +package internal import java.io.* import scala.util.control.NoStackTrace +import json.reading.JsonSource +import json.* + +// $COVERAGE-OFF$3rd party library /** Total, fast, number parsing. * @@ -834,3 +837,4 @@ object UnsafeNumbers { private val longunderflow: Long = Long.MinValue / 10L private val longoverflow: Long = Long.MaxValue / 10L } +// $COVERAGE-ON$ diff --git a/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala b/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala index efb55add..5e2f0fbb 100644 --- a/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala +++ b/src/main/scala/co.blocke.scalajack/json/JsonCodecMaker.scala @@ -286,6 +286,22 @@ object JsonCodecMaker: } } + case t: IterableRef[?] => + makeWriteFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => + t.elementRef.refType match + case '[e] => + val tin = in.asInstanceOf[Expr[Iterable[e]]] + '{ + if $tin == null then $out.burpNull() + else + $out.startArray() + $tin.foreach { i => + ${ genWriteVal('{ i }, t.elementRef.asInstanceOf[RTypeRef[e]], out) } + } + $out.endArray() + } + } + case t: SeqRef[?] => makeWriteFn[b](MethodKey(t, false), aE.asInstanceOf[Expr[b]], out) { (in, out) => t.elementRef.refType match @@ -791,12 +807,9 @@ object JsonCodecMaker: case t: ObjectRef[?] => '{ $out.value(${ Expr(t.name) }) } case t: AliasRef[?] => - // Special check for RawJson pseudo-type - if lastPart(t.definedType) == "RawJson" then '{ $out.valueRaw(${ aE.asExprOf[RawJson] }) } - else - t.unwrappedType.refType match - case '[e] => - genWriteVal[e](aE.asInstanceOf[Expr[e]], t.unwrappedType.asInstanceOf[RTypeRef[e]], out, inTuple = inTuple) + t.unwrappedType.refType match + case '[e] => + genWriteVal[e](aE.asInstanceOf[Expr[e]], t.unwrappedType.asInstanceOf[RTypeRef[e]], out, inTuple = inTuple) // These are here becaue Enums and their various flavors can be Map keys // (EnumRef handles: Scala 3 enum, Scala 2 Enumeration, Java Enumeration) @@ -1454,31 +1467,23 @@ object JsonCodecMaker: case t: ZoneIdRef => '{ $in.expectString(java.time.ZoneId.of) }.asExprOf[T] case t: ZoneOffsetRef => '{ $in.expectString(java.time.ZoneOffset.of) }.asExprOf[T] - case t: URLRef => '{ $in.expectString((s: String) => new java.net.URL(s)) }.asExprOf[T] + case t: URLRef => '{ $in.expectString((s: String) => new java.net.URI(s).toURL()) }.asExprOf[T] case t: URIRef => '{ $in.expectString((s: String) => new java.net.URI(s)) }.asExprOf[T] case t: UUIDRef => '{ $in.expectString(java.util.UUID.fromString) }.asExprOf[T] case t: AliasRef[?] => - // Special check for RawJson pseudo-type - if lastPart(t.definedType) == "RawJson" then - '{ - val mark = $in.pos - $in.skipValue() - $in.captureMark(mark) - }.asExprOf[T] - else - t.unwrappedType.refType match - case '[e] => - '{ - ${ - genReadVal[e]( - t.unwrappedType.asInstanceOf[RTypeRef[e]], - in, - inTuple, - isMapKey - ) - }.asInstanceOf[T] - } + t.unwrappedType.refType match + case '[e] => + '{ + ${ + genReadVal[e]( + t.unwrappedType.asInstanceOf[RTypeRef[e]], + in, + inTuple, + isMapKey + ) + }.asInstanceOf[T] + } // -------------------- // Options... @@ -1673,15 +1678,6 @@ object JsonCodecMaker: if parsedArray != null then parsedArray.toVector else null }.asExprOf[T] - case '[Seq[?]] => - t.elementRef.refType match - case '[e] => - val rtypeRef = t.elementRef.asInstanceOf[RTypeRef[e]] - '{ - val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in, inTuple) }) - if parsedArray != null then parsedArray.toSeq - else null - }.asExprOf[T] case '[IndexedSeq[?]] => t.elementRef.refType match case '[e] => @@ -1691,13 +1687,13 @@ object JsonCodecMaker: if parsedArray != null then parsedArray.toIndexedSeq else null }.asExprOf[T] - case '[Iterable[?]] => + case '[Seq[?]] => t.elementRef.refType match case '[e] => val rtypeRef = t.elementRef.asInstanceOf[RTypeRef[e]] '{ val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in, inTuple) }) - if parsedArray != null then parsedArray.toIterable + if parsedArray != null then parsedArray.toSeq else null }.asExprOf[T] // Catch all, with (slightly) slower type coersion to proper Seq flavor @@ -1711,6 +1707,19 @@ object JsonCodecMaker: else null }.asExprOf[T] + case t: IterableRef[?] => + t.elementRef.refType match + case '[e] => + val rtypeRef = t.elementRef.asInstanceOf[RTypeRef[e]] + val ct = Expr.summon[ClassTag[e]].get + '{ + val parsedArray = $in.expectArray[e](() => ${ genReadVal[e](rtypeRef, in, inTuple) }) + if parsedArray != null then + implicit val ctt = $ct + parsedArray.asInstanceOf[Iterable[e]] + else null + }.asExprOf[T] + case t: ArrayRef[?] => t.elementRef.refType match case '[e] => @@ -2080,24 +2089,16 @@ object JsonCodecMaker: // make all the tuple terms, accounting for , and ] detection val tupleTerms = - if t.tupleRefs.length == 1 then - t.tupleRefs(0).refType match + t.tupleRefs.zipWithIndex.map { case (tpart, i) => + tpart.refType match case '[e] => - List('{ - ${ genReadVal[e](t.tupleRefs(0).asInstanceOf[RTypeRef[e]], in, true) } - $in.expectToken(']') - }.asTerm) - else - t.tupleRefs.zipWithIndex.map { case (tpart, i) => - tpart.refType match - case '[e] => - if i == 0 then genReadVal[e](tpart.asInstanceOf[RTypeRef[e]], in, true).asTerm - else - '{ - $in.expectToken(',') - ${ genReadVal[e](tpart.asInstanceOf[RTypeRef[e]], in, true) } - }.asTerm - } + if i == 0 then genReadVal[e](tpart.asInstanceOf[RTypeRef[e]], in, true).asTerm + else + '{ + $in.expectToken(',') + ${ genReadVal[e](tpart.asInstanceOf[RTypeRef[e]], in, true) } + }.asTerm + } '{ if $in.expectNull() then null else diff --git a/src/main/scala/co.blocke.scalajack/json/package.scala b/src/main/scala/co.blocke.scalajack/json/package.scala index af721926..5a78546b 100644 --- a/src/main/scala/co.blocke.scalajack/json/package.scala +++ b/src/main/scala/co.blocke.scalajack/json/package.scala @@ -5,8 +5,6 @@ import scala.util.Failure import scala.quoted.{Expr, Quotes, Type} import java.util.Optional -opaque type RawJson = String - val BUFFER_EXCEEDED: Char = 7 // Old "BELL" ASCII value, used as a marker when we've run off the end of the known world val END_OF_STRING: Char = 3 diff --git a/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala b/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala index 2adac083..8c436ac1 100644 --- a/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala +++ b/src/main/scala/co.blocke.scalajack/json/reading/JsonSource.scala @@ -3,7 +3,8 @@ package json package reading import scala.annotation.{switch, tailrec} -import co.blocke.scalajack.json.reading.SafeNumbers.double +import co.blocke.scalajack.internal.SafeNumbers.double +import co.blocke.scalajack.internal.UnsafeNumbers object JsonSource: val ull: Array[Char] = "ull".toCharArray diff --git a/src/main/scala/co.blocke.scalajack/json/schema/JsonSchema.scala b/src/main/scala/co.blocke.scalajack/json/schema/JsonSchema.scala deleted file mode 100644 index 85864547..00000000 --- a/src/main/scala/co.blocke.scalajack/json/schema/JsonSchema.scala +++ /dev/null @@ -1,323 +0,0 @@ -package co.blocke.scalajack -package json -package schema - -import java.net.URL -import co.blocke.scala_reflection.* -import co.blocke.scala_reflection.reflect.ReflectOnType -import co.blocke.scala_reflection.reflect.rtypeRefs.* -import org.apache.commons.text.StringEscapeUtils - -// Macro... -import scala.quoted.* - -object JsonSchema: - - inline def of[T](overrides: Map[String, Schema]): Schema = ${ ofImpl[T]('overrides) } - - inline def of[T]: Schema = ${ ofImpl[T]() } - - def ofImpl[T]()(using t: Type[T])(using quotes: Quotes): Expr[Schema] = - import quotes.reflect.* - val classRef = ReflectOnType[T](quotes)(TypeRepr.of[T], true)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) - genSchema(quotes)(classRef, None, '{ Map.empty[String, Schema] }, None, true) - - def ofImpl[T](overrides: Expr[Map[String, Schema]])(using t: Type[T])(using quotes: Quotes): Expr[Schema] = - import quotes.reflect.* - val classRef = ReflectOnType[T](quotes)(TypeRepr.of[T], true)(using scala.collection.mutable.Map.empty[TypedName, Boolean]) - genSchema(quotes)(classRef, None, overrides, None, true) - - private def genSchema[T](quotes: Quotes)( - rt: RTypeRef[T], - context: Option[ScalaFieldInfoRef] = None, - overrides: Expr[Map[String, Schema]], - defaultValue: Option[quotes.reflect.Term] = None, - isInitialSchema: Boolean = false - ): Expr[Schema] = - import quotes.reflect.* - implicit val q: Quotes = quotes - - def typeArgs(tpe: TypeRepr): List[TypeRepr] = tpe match - case AppliedType(_, typeArgs) => typeArgs.map(_.dealias) - case _ => Nil - - val rtName = Expr(rt.name) - '{ - $overrides - .get($rtName) - .getOrElse(${ - rt match - case t: AliasRef[?] => - t.unwrappedType.refType match - case '[e] => - genSchema[e](quotes)(t.unwrappedType.asInstanceOf[RTypeRef[e]], context, overrides, defaultValue) - case t: ArrayRef[?] => - t.refType match - case '[u] => - t.elementRef.refType match - case '[e] => - '{ - ArraySchema( - ${ genSchema[e](quotes)(t.elementRef.asInstanceOf[RTypeRef[e]], context, overrides, None) }, - ${ Expr(context.flatMap(_.annotations.get("minItems")).flatMap(_.get("value")).map(_.toInt)) }, - ${ Expr(context.flatMap(_.annotations.get("maxItems")).flatMap(_.get("value")).map(_.toInt)) }, - ${ Expr(context.flatMap(_.annotations.get("uniqueItems")).flatMap(_.get("value")).map(_.toBoolean)) }, - ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, - ${ - if defaultValue.isDefined then - val codec = JsonCodecMaker.generateCodecFor[u](t.refType.asInstanceOf[RTypeRef[u]], SJConfig) - '{ - val out = new writing.JsonOutput() - $codec.encodeValue(${ defaultValue.get.asExprOf[u] }, out) - Some(out.toString.asInstanceOf[RawJson]) - } - else Expr(None) - } - ) - } - case t: EnumRef[?] => '{ EnumSchema(${ Expr(t.values) }) } - case t: OptionRef[?] => - // Go ahead and gen schema for body of Option. Higher level (ie class) is resposible for tracking if a - // value is required or not... - t.optionParamType.refType match - case '[e] => - defaultValue - .map { dv => - val dve = dv.asExprOf[Option[e]] - '{ - $dve match - case Some(a) => ${ genSchema[e](quotes)(t.optionParamType.asInstanceOf[RTypeRef[e]], context, overrides, Some('{ a }.asTerm)) } - case None => ${ genSchema[e](quotes)(t.optionParamType.asInstanceOf[RTypeRef[e]], context, overrides, None) } - } - } - .getOrElse(genSchema[e](quotes)(t.optionParamType.asInstanceOf[RTypeRef[e]], context, overrides, None)) - case t: TryRef[?] => - // Go ahead and gen schema for body of Try. Higher level (ie class) is resposible for tracking if a - // value is required or not... - t.tryRef.refType match - case '[e] => - genSchema[e](quotes)(t.tryRef.asInstanceOf[RTypeRef[e]], context, overrides, None) - case t: SeqRef[?] => - t.refType match - case '[u] => - t.elementRef.refType match - case '[e] => - '{ - ArraySchema( - ${ genSchema[e](quotes)(t.elementRef.asInstanceOf[RTypeRef[e]], context, overrides, None) }, - ${ Expr(context.flatMap(_.annotations.get("minItems")).flatMap(_.get("value")).map(_.toInt)) }, - ${ Expr(context.flatMap(_.annotations.get("maxItems")).flatMap(_.get("value")).map(_.toInt)) }, - ${ Expr(context.flatMap(_.annotations.get("uniqueItems")).flatMap(_.get("value")).map(_.toBoolean)) }, - ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, - ${ - if defaultValue.isDefined then - val codec = JsonCodecMaker.generateCodecFor[u](t.asInstanceOf[RTypeRef[u]], SJConfig) - '{ - val out = new writing.JsonOutput() - $codec.encodeValue(${ defaultValue.get.asExprOf[u] }, out) - Some(out.result.asInstanceOf[RawJson]) - } - else Expr(None) - } - ) - } - case t: SetRef[?] => - t.refType match - case '[u] => - t.elementRef.refType match - case '[e] => - '{ - ArraySchema( - ${ genSchema[e](quotes)(t.elementRef.asInstanceOf[RTypeRef[e]], context, overrides, None) }, - ${ Expr(context.flatMap(_.annotations.get("minItems")).flatMap(_.get("value")).map(_.toInt)) }, - ${ Expr(context.flatMap(_.annotations.get("maxItems")).flatMap(_.get("value")).map(_.toInt)) }, - ${ Expr(context.flatMap(_.annotations.get("uniqueItems")).flatMap(_.get("value")).map(_.toBoolean)) }, - ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, - ${ - if defaultValue.isDefined then - val codec = JsonCodecMaker.generateCodecFor[u](t.asInstanceOf[RTypeRef[u]], SJConfig) - '{ - val out = new writing.JsonOutput() - $codec.encodeValue(${ defaultValue.get.asExprOf[u] }, out) - Some(out.result.asInstanceOf[RawJson]) - } - else Expr(None) - } - ) - } - case t: TupleRef[?] => - t.refType match - case '[u] => - '{ - TupleSchema( - ${ - Expr.ofList(t.tupleRefs.map { tr => - tr.refType match - case '[w] => - genSchema[w](quotes)(tr.asInstanceOf[RTypeRef[w]], context, overrides, None) - }) - }, - ${ Expr(context.flatMap(_.annotations.get("items")).flatMap(_.get("value")).map(_.toBoolean)) }, - ${ Expr(context.flatMap(_.annotations.get("minItems")).flatMap(_.get("value")).map(_.toInt)) }, - ${ Expr(context.flatMap(_.annotations.get("maxItems")).flatMap(_.get("value")).map(_.toInt)) }, - ${ Expr(context.flatMap(_.annotations.get("uniqueItems")).flatMap(_.get("value")).map(_.toBoolean)) }, - ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, - ${ - if defaultValue.isDefined then - val codec = JsonCodecMaker.generateCodecFor[u](t.asInstanceOf[RTypeRef[u]], SJConfig) - '{ - val out = new writing.JsonOutput() - $codec.encodeValue(${ defaultValue.get.asExprOf[u] }, out) - Some(out.result.asInstanceOf[RawJson]) - } - else Expr(None) - } - ) - } - case t: BooleanRef => - '{ - BooleanSchema( - ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, - ${ - ofOption(defaultValue.map(v => '{ ${ v.asExprOf[Boolean] }.toString.asInstanceOf[RawJson] })) - } - ) - } - case t: DoubleRef => - '{ - NumberSchema( - ${ Expr(context.flatMap(_.annotations.get("minimum")).flatMap(_.get("value")).map(_.toDouble)) }, - ${ Expr(context.flatMap(_.annotations.get("maximum")).flatMap(_.get("value")).map(_.toDouble)) }, - ${ Expr(context.flatMap(_.annotations.get("exclusiveMinimum")).flatMap(_.get("value")).map(_.toDouble)) }, - ${ Expr(context.flatMap(_.annotations.get("exclusiveMaximum")).flatMap(_.get("value")).map(_.toDouble)) }, - ${ Expr(context.flatMap(_.annotations.get("multipleOf")).flatMap(_.get("value")).map(_.toInt)) }, - ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, - ${ - ofOption(defaultValue.map(v => '{ ${ v.asExprOf[Double] }.toString.asInstanceOf[RawJson] })) - } - ) - } - case t: IntRef => - '{ - IntegerSchema( - ${ Expr(context.flatMap(_.annotations.get("minimum")).flatMap(_.get("value")).map(_.toInt)) }, - ${ Expr(context.flatMap(_.annotations.get("maximum")).flatMap(_.get("value")).map(_.toInt)) }, - ${ Expr(context.flatMap(_.annotations.get("exclusiveMinimum")).flatMap(_.get("value")).map(_.toInt)) }, - ${ Expr(context.flatMap(_.annotations.get("exclusiveMaximum")).flatMap(_.get("value")).map(_.toInt)) }, - ${ Expr(context.flatMap(_.annotations.get("multipleOf")).flatMap(_.get("value")).map(_.toInt)) }, - ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, - ${ - ofOption(defaultValue.map(v => '{ ${ v.asExprOf[Int] }.toString.asInstanceOf[RawJson] })) - } - ) - } - case t: LongRef => - '{ - IntegerSchema( - ${ Expr(context.flatMap(_.annotations.get("minimum")).flatMap(_.get("value")).map(_.toLong)) }, - ${ Expr(context.flatMap(_.annotations.get("maximum")).flatMap(_.get("value")).map(_.toLong)) }, - ${ Expr(context.flatMap(_.annotations.get("exclusiveMinimum")).flatMap(_.get("value")).map(_.toLong)) }, - ${ Expr(context.flatMap(_.annotations.get("exclusiveMaximum")).flatMap(_.get("value")).map(_.toLong)) }, - ${ Expr(context.flatMap(_.annotations.get("multipleOf")).flatMap(_.get("value")).map(_.toInt)) }, - ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, - ${ - ofOption(defaultValue.map(v => '{ ${ v.asExprOf[Long] }.toString.asInstanceOf[RawJson] })) - } - ) - } - case t: StringRef => - '{ - StringSchema( - ${ Expr(context.flatMap(_.annotations.get("minLength")).flatMap(_.get("value")).map(_.toInt)) }, - ${ Expr(context.flatMap(_.annotations.get("maxLength")).flatMap(_.get("value")).map(_.toInt)) }, - ${ Expr(context.flatMap(_.annotations.get("pattern")).flatMap(_.get("value")).map(v => StringEscapeUtils.escapeJson(v))) }, - ${ Expr(context.flatMap(_.annotations.get("format")).flatMap(_.get("value"))) }.map(v => StringFormat.valueOf(v)), - ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, - ${ - ofOption(defaultValue.map(v => '{ ("\"" + StringEscapeUtils.escapeJson(${ v.asExprOf[String] }) + "\"").asInstanceOf[RawJson] })) - } - ) - } - case t: ZonedDateTimeRef => - '{ - StringSchema( - ${ Expr(context.flatMap(_.annotations.get("minLength")).flatMap(_.get("value")).map(_.toInt)) }, - ${ Expr(context.flatMap(_.annotations.get("maxLength")).flatMap(_.get("value")).map(_.toInt)) }, - ${ Expr(context.flatMap(_.annotations.get("pattern")).flatMap(_.get("value")).map(v => StringEscapeUtils.escapeJson(v))) }, - Some(StringFormat.`date-time`), - ${ Expr(context.flatMap(_.annotations.get("description")).flatMap(_.get("value"))) }, - ${ - ofOption(defaultValue.map(v => '{ ${ v.asExprOf[java.time.ZonedDateTime] }.toString.asInstanceOf[RawJson] })) - } - ) - } - case t: ScalaClassRef[?] => - t.refType match - case '[u] => - val requiredFields = Expr(t.fields.collect { - case f: FieldInfoRef if !f.fieldRef.isInstanceOf[OptionRef[?]] => f.name - }) - '{ - ObjectSchema( - ${ - Expr.ofList( - t.fields.map(f => - f.fieldRef.refType match - case '[b] => - // Get default value if any - val tpe = TypeRepr.of[u] - val classCompanion = tpe.typeSymbol.companionClass - val companionModule = tpe.typeSymbol.companionModule - val dvMembers = classCompanion.methodMember("$lessinit$greater$default$" + (f.index + 1)) - val fieldDefault = - if dvMembers.isEmpty then None - else - val methodSymbol = dvMembers.head - val dvSelectNoTArgs = Ref(companionModule).select(methodSymbol) - val dvSelect = methodSymbol.paramSymss match - case Nil => dvSelectNoTArgs - case List(params) if (params.exists(_.isTypeParam)) => - typeArgs(tpe) match - case Nil => ??? // throw JsonParseError("Expected an applied type", ???) - case typeArgs => TypeApply(dvSelectNoTArgs, typeArgs.map(Inferred(_))) - case _ => ??? // fail(s"Default method for ${symbol.name} of class ${tpe.show} have a complex " + - Some(dvSelect) - Expr.ofTuple((Expr(f.name), genSchema[b](quotes)(f.fieldRef.asInstanceOf[RTypeRef[b]], Some(f.asInstanceOf[ScalaFieldInfoRef]), overrides, fieldDefault))) - ) - ) - }.toMap, - $requiredFields, - ${ Expr(t.annotations.get("co.blocke.scalajack.schema.additionalProperties").flatMap(_.get("value")).map(_.toBoolean)) }, - ${ - if isInitialSchema then '{ Some(new URL("http://jsons-schema.org/draft-04/schema#")) } - else '{ None } - }, - ${ - if isInitialSchema then Expr(t.annotations.get("co.blocke.scalajack.schema.id").flatMap(_.get("value"))) - else '{ None } - }, - ${ - if isInitialSchema then Expr(t.annotations.get("co.blocke.scalajack.schema.title").flatMap(_.get("value"))) - else '{ None } - }, - ${ Expr(t.annotations.get("co.blocke.scalajack.schema.description").flatMap(_.get("value"))) }, - ${ - if defaultValue.isDefined then - val codec = JsonCodecMaker.generateCodecFor[u](t.asInstanceOf[RTypeRef[u]], SJConfig.suppressTypeHints()) - '{ - val out = new writing.JsonOutput() - $codec.encodeValue(${ defaultValue.get.asExprOf[u] }, out) - Some(out.result.asInstanceOf[RawJson]) - } - else Expr(None) - } - ) - } - case t: TraitRef[?] => - t.refType match - case '[u] => - if t.childrenAreObject then '{ EnumSchema(${ Expr(t.sealedChildren.map(_.name.split("\\.").last)) }) } - else throw new Exception(s"Unsupported type ${rt.name} of type ${rt.getClass.getName} for JSON schema generation") - case x => throw new Exception(s"Unsupported type ${rt.name} of type ${rt.getClass.getName} for JSON schema generation") - }) - } diff --git a/src/main/scala/co.blocke.scalajack/json/schema/Schema.scala b/src/main/scala/co.blocke.scalajack/json/schema/Schema.scala deleted file mode 100644 index 0ae149cb..00000000 --- a/src/main/scala/co.blocke.scalajack/json/schema/Schema.scala +++ /dev/null @@ -1,114 +0,0 @@ -package co.blocke.scalajack -package json -package schema - -import java.net.URL - -/** A *very* sparse implementation of JSON Schema Draft 4 (model only). It is full of holes, but is just - * enough for what I needed at the time--and had the advantage of leveraging scala-reflection - * to generate the schema from a type. At the time of this writing, no other Scala 3-capable JSON - * library generated a JSON Schema document. (ZIO-Json genrated their own Schema structure, however.) - * - * If there is strong utility for a full-blown JSON Schema utility, that might be something I look - * at later, unless someone wants to take it up. I would suggest development of "helper" objects - * for the more advanced schema operaitons (allOf, anyOf, if/then/else, etc) vs trying to do all that - * in annotations. - */ - -// Reference: https://json-schema.org/UnderstandingJSONSchema.pdf - -enum SchemaType: - case `null`, `boolean`, `object`, `array`, `string`, `number`, `integer` - -enum StringFormat: - case `date-time`, email, hostname, ipv4, ipv6, uuid, uri, url - -// opaque type JSON_LITERAL = String - -type Schema = StdSchema | EnumSchema - -sealed trait StdSchema: - val `type`: SchemaType - val description: Option[String] - val default: Option[RawJson] - -// Formats: Dates & Times, Email, Hostnames -case class StringSchema( - minLength: Option[Int] = None, - maxLength: Option[Int] = None, - pattern: Option[String] = None, - format: Option[StringFormat] = None, - description: Option[String] = None, - default: Option[RawJson] = None, - `type`: SchemaType = SchemaType.`string` -) extends StdSchema - -case class IntegerSchema( - minimum: Option[Long] = None, - maximum: Option[Long] = None, - exclusiveMinimum: Option[Long] = None, - exclusiveMaximum: Option[Long] = None, - multipleOf: Option[Int] = None, - description: Option[String] = None, - default: Option[RawJson] = None, - `type`: SchemaType = SchemaType.`integer` -) extends StdSchema - -case class NumberSchema( - minimum: Option[Double] = None, - maximum: Option[Double] = None, - exclusiveMinimum: Option[Double] = None, - exclusiveMaximum: Option[Double] = None, - multipleOf: Option[Int] = None, - description: Option[String] = None, - default: Option[RawJson] = None, - `type`: SchemaType = SchemaType.`number` -) extends StdSchema - -case class BooleanSchema( - description: Option[String] = None, - default: Option[RawJson] = None, - `type`: SchemaType = SchemaType.`boolean` -) extends StdSchema - -case class NullSchema(description: Option[String] = None, `type`: SchemaType = SchemaType.`null`) extends StdSchema: - val default: Option[RawJson] = None // no default for null possible - -case class ArraySchema( - items: Schema, - minItems: Option[Int] = None, - maxItems: Option[Int] = None, - uniqueItems: Option[Boolean] = None, - description: Option[String] = None, - default: Option[RawJson] = None, - `type`: SchemaType = SchemaType.`array` -) extends StdSchema - -case class TupleSchema( - prefixItems: List[Schema], - items: Option[Boolean] = None, - minItems: Option[Int] = None, - maxItems: Option[Int] = None, - uniqueItems: Option[Boolean] = None, - description: Option[String] = None, - default: Option[RawJson] = None, - `type`: SchemaType = SchemaType.`array` -) extends StdSchema - -// Note: patternProperties not implemented at this time (I didn't need them) -case class ObjectSchema( - properties: Map[String, Schema], - required: List[String], - additionalProperties: Option[Boolean] = None, - `$schema`: Option[URL] = Some(new URL("http://jsons-schema.org/draft-04/schema#")), - `$id`: Option[String] = None, - title: Option[String] = None, - description: Option[String] = None, - default: Option[RawJson] = None, - `type`: SchemaType = SchemaType.`object` -) extends StdSchema - -// Weird exception to other schemas--no type, or other decorating feature... just the enum values -case class EnumSchema( - `enum`: List[String] -) diff --git a/src/main/scala/co.blocke.scalajack/json/writing/JsonOutput.scala b/src/main/scala/co.blocke.scalajack/json/writing/JsonOutput.scala index 108c39ef..b1417626 100644 --- a/src/main/scala/co.blocke.scalajack/json/writing/JsonOutput.scala +++ b/src/main/scala/co.blocke.scalajack/json/writing/JsonOutput.scala @@ -245,11 +245,6 @@ case class JsonOutput(): internal.append('"') comma = true - inline def valueRaw(v: RawJson): Unit = - maybeComma() - internal.append(v.asInstanceOf[String]) - comma = true - inline def value(v: java.lang.Boolean): Unit = maybeComma() if v == null then internal.append("null") diff --git a/src/test/scala/co.blocke.scalajack/json/collections/MapSpec.scala b/src/test/scala/co.blocke.scalajack/json/collections/MapSpec.scala index a9e89727..3e1ad9da 100644 --- a/src/test/scala/co.blocke.scalajack/json/collections/MapSpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/collections/MapSpec.scala @@ -155,12 +155,134 @@ class MapSpec() extends AnyFunSpec with JsonMatchers: js should matchJson("""{"a":{"w":1.23,"y":4.56}}""") sj.fromJson(js) shouldEqual (inst) } - it("Map value must work - SeqMap (examplar for all other immutable Maps)") { + it("Map value must work - SeqMap") { val inst = MapHolder3[String, Distance](scala.collection.immutable.SeqMap("w" -> new Distance(1.23), "y" -> Distance(4.56))) val sj = sjCodecOf[MapHolder3[String, Distance]] val js = sj.toJson(inst) js should matchJson("""{"a":{"w":1.23,"y":4.56}}""") sj.fromJson(js) shouldEqual (inst) } + it("Map value must work - VectorMap (examplar for all other immutable Maps)") { + val inst = MapHolder4[String, Distance](scala.collection.immutable.TreeMap("w" -> new Distance(1.23), "y" -> Distance(4.56))) + val sj = sjCodecOf[MapHolder4[String, Distance]] + val js = sj.toJson(inst) + js should matchJson("""{"a":{"w":1.23,"y":4.56}}""") + sj.fromJson(js) shouldEqual (inst) + } + it("Map keys of remaining types must work (test coverage addition)") { + val inst = MapHolder[java.lang.Number, Int](Map(java.lang.Integer.valueOf(5).asInstanceOf[java.lang.Number] -> 12)) + val sj = sjCodecOf[MapHolder[java.lang.Number, Int]] + val js = sj.toJson(inst) + js should matchJson("""{"a":{"5":12}}""") + sj.fromJson(js) shouldEqual (inst) + + val inst2 = MapHolder[java.lang.Short, Int](Map(java.lang.Short.valueOf("5") -> 12)) + val sj2 = sjCodecOf[MapHolder[java.lang.Short, Int]] + val js2 = sj2.toJson(inst2) + js2 should matchJson("""{"a":{"5":12}}""") + sj2.fromJson(js2) shouldEqual (inst2) + + val inst3 = MapHolder[java.lang.Long, Int](Map(java.lang.Long.valueOf(5) -> 12)) + val sj3 = sjCodecOf[MapHolder[java.lang.Long, Int]] + val js3 = sj3.toJson(inst3) + js3 should matchJson("""{"a":{"5":12}}""") + sj3.fromJson(js3) shouldEqual (inst3) + + val inst4 = MapHolder[java.lang.Integer, Int](Map(java.lang.Integer.valueOf(5) -> 12)) + val sj4 = sjCodecOf[MapHolder[java.lang.Integer, Int]] + val js4 = sj4.toJson(inst4) + js4 should matchJson("""{"a":{"5",12}}""") + sj4.fromJson(js4) shouldEqual (inst4) + + val inst5 = MapHolder[java.lang.Float, Int](Map(java.lang.Float.valueOf(5) -> 12)) + val sj5 = sjCodecOf[MapHolder[java.lang.Float, Int]] + val js5 = sj5.toJson(inst5) + js5 should matchJson("""{"a":{"5.0":12}}""") + sj5.fromJson(js5) shouldEqual (inst5) + + val inst6 = MapHolder[java.lang.Double, Int](Map(java.lang.Double.valueOf(5) -> 12)) + val sj6 = sjCodecOf[MapHolder[java.lang.Double, Int]] + val js6 = sj6.toJson(inst6) + js6 should matchJson("""{"a":{"5.0":12}}""") + sj6.fromJson(js6) shouldEqual (inst6) + + val inst7 = MapHolder[java.lang.Byte, Int](Map(java.lang.Byte.valueOf("5") -> 12)) + val sj7 = sjCodecOf[MapHolder[java.lang.Byte, Int]] + val js7 = sj7.toJson(inst7) + js7 should matchJson("""{"a":{"5":12}}""") + sj7.fromJson(js7) shouldEqual (inst7) + + val inst8 = MapHolder[java.math.BigDecimal, Int](Map(java.math.BigDecimal.valueOf(5) -> 12)) + val sj8 = sjCodecOf[MapHolder[java.math.BigDecimal, Int]] + val js8 = sj8.toJson(inst8) + js8 should matchJson("""{"a":{"5":12}}""") + sj8.fromJson(js8) shouldEqual (inst8) + + val inst9 = MapHolder[java.math.BigInteger, Int](Map(java.math.BigInteger.valueOf(5) -> 12)) + val sj9 = sjCodecOf[MapHolder[java.math.BigInteger, Int]] + val js9 = sj9.toJson(inst9) + js9 should matchJson("""{"a":{"5":12}}""") + sj9.fromJson(js9) shouldEqual (inst9) + + val inst10 = MapHolder[java.lang.Boolean, Int](Map(java.lang.Boolean.valueOf(true) -> 12)) + val sj10 = sjCodecOf[MapHolder[java.lang.Boolean, Int]] + val js10 = sj10.toJson(inst10) + js10 should matchJson("""{"a":{"true":12}}""") + sj10.fromJson(js10) shouldEqual (inst10) + + val inst11 = MapHolder[Short, Int](Map(5.toShort -> 12)) + val sj11 = sjCodecOf[MapHolder[Short, Int]] + val js11 = sj11.toJson(inst11) + js11 should matchJson("""{"a":{"5":12}}""") + sj11.fromJson(js11) shouldEqual (inst11) + + val inst12 = MapHolder[Byte, Int](Map(5.toByte -> 12)) + val sj12 = sjCodecOf[MapHolder[Byte, Int]] + val js12 = sj12.toJson(inst12) + js12 should matchJson("""{"a":{"5":12}}""") + sj12.fromJson(js12) shouldEqual (inst12) + + val inst13 = MapHolder[Float, Int](Map(5.0.toFloat -> 12)) + val sj13 = sjCodecOf[MapHolder[Float, Int]] + val js13 = sj13.toJson(inst13) + js13 should matchJson("""{"a":{"5.0":12}}""") + sj13.fromJson(js13) shouldEqual (inst13) + + val inst14 = MapHolder[scala.math.BigDecimal, Int](Map(scala.math.BigDecimal(5) -> 12)) + val sj14 = sjCodecOf[MapHolder[scala.math.BigDecimal, Int]] + val js14 = sj14.toJson(inst14) + js14 should matchJson("""{"a":{"5":12}}""") + sj14.fromJson(js14) shouldEqual (inst14) + + val inst15 = MapHolder[scala.math.BigInt, Int](Map(scala.math.BigInt(5) -> 12)) + val sj15 = sjCodecOf[MapHolder[scala.math.BigInt, Int]] + val js15 = sj15.toJson(inst15) + js15 should matchJson("""{"a":{"5":12}}""") + sj15.fromJson(js15) shouldEqual (inst15) + + val inst16 = MapHolder[OnOff, OnOff](Map(true.asInstanceOf[OnOff] -> false.asInstanceOf[OnOff])) + val sj16 = sjCodecOf[MapHolder[OnOff, OnOff]] + val js16 = sj16.toJson(inst16) + js16 should matchJson("""{"a":{"true":false}}""") + sj16.fromJson(js16) shouldEqual (inst16) + + val inst17 = MapHolder[Boolean, OnOff](Map(true -> false.asInstanceOf[OnOff])) + val sj17 = sjCodecOf[MapHolder[Boolean, OnOff]] + val js17 = sj17.toJson(inst17) + js17 should matchJson("""{"a":{"true":false}}""") + sj17.fromJson(js17) shouldEqual (inst17) + + val inst18 = MapHolder[OnOff, Boolean](Map(true.asInstanceOf[OnOff] -> false)) + val sj18 = sjCodecOf[MapHolder[OnOff, Boolean]] + val js18 = sj18.toJson(inst18) + js18 should matchJson("""{"a":{"true":false}}""") + sj18.fromJson(js18) shouldEqual (inst18) + + val now = java.time.Instant.now() + val inst19 = MapHolder[java.time.Instant, Boolean](Map(now -> false)) + val sj19 = sjCodecOf[MapHolder[java.time.Instant, Boolean]] + val js19 = sj19.toJson(inst19) + sj19.fromJson(js19) shouldEqual (inst19) + } } } diff --git a/src/test/scala/co.blocke.scalajack/json/collections/Model.scala b/src/test/scala/co.blocke.scalajack/json/collections/Model.scala index b097c90c..9ffc6e79 100644 --- a/src/test/scala/co.blocke.scalajack/json/collections/Model.scala +++ b/src/test/scala/co.blocke.scalajack/json/collections/Model.scala @@ -11,12 +11,18 @@ opaque type Counter = Short case class SeqHolder[T](a: Seq[T]) case class SetHolder[T](a: Set[T]) +case class MSeqHolder[T](a: scala.collection.mutable.Seq[T]) +case class MSetHolder[T](a: scala.collection.mutable.Set[T]) +case class VectorHolder[T](a: Vector[T]) +case class IndexedSeqHolder[T](a: IndexedSeq[T]) +case class IterableHolder[T](a: Iterable[T]) case class ArrayHolder[T](a: Array[T]) case class Holder[T](a: T) case class MapHolder[T, V](a: Map[T, V]) case class MapHolder2[T, V](a: scala.collection.immutable.HashMap[T, V]) // specific -case class MapHolder3[T, V](a: scala.collection.immutable.SeqMap[T, V]) // open coersion +case class MapHolder3[T, V](a: scala.collection.immutable.SeqMap[T, V]) // specific +case class MapHolder4[T, V](a: scala.collection.immutable.TreeMap[T, V]) // open coersion case class MMapHolder[T, V](a: scala.collection.mutable.Map[T, V]) // specific case class MMapHolder2[T, V](a: scala.collection.mutable.HashMap[T, V]) // specific case class MMapHolder3[T, V](a: scala.collection.mutable.SeqMap[T, V]) // open coersion @@ -25,6 +31,7 @@ case class JMapHolder[T, V](a: JMap[T, V]) class Distance(val meters: Double) extends AnyVal case class TupleHolder[A, B, C](a: (A, B, C)) +case class TupleOneHolder[A](a: Tuple1[A]) case class ArrayListHolder[T](a: ArrayList[T]) case class JSetHolder[T](a: JSet[T]) diff --git a/src/test/scala/co.blocke.scalajack/json/collections/SeqSetArraySpec.scala b/src/test/scala/co.blocke.scalajack/json/collections/SeqSetArraySpec.scala index 0313b347..a405b245 100644 --- a/src/test/scala/co.blocke.scalajack/json/collections/SeqSetArraySpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/collections/SeqSetArraySpec.scala @@ -36,6 +36,13 @@ class SeqSetArraySpec() extends AnyFunSpec with JsonMatchers: js should matchJson("""{"a":["a","b","c"]}""") sj.fromJson(js) shouldEqual (inst) } + it("Mutable Seq of string must work") { + val inst = MSeqHolder[String](scala.collection.mutable.ListBuffer("a", "b", "c")) + val sj = sjCodecOf[MSeqHolder[String]] + val js = sj.toJson(inst) + js should matchJson("""{"a":["a","b","c"]}""") + sj.fromJson(js) shouldEqual (inst) + } it("Seq of boolean must work") { val inst = SeqHolder[Boolean](List(true, false, true)) val sj = sjCodecOf[SeqHolder[Boolean]] @@ -107,6 +114,13 @@ class SeqSetArraySpec() extends AnyFunSpec with JsonMatchers: js should matchJson("""{"a":["a","b","c"]}""") sj.fromJson(js) shouldEqual (inst) } + it("Mutable Set of string must work") { + val inst = MSetHolder[String](scala.collection.mutable.HashSet("a", "b", "c")) + val sj = sjCodecOf[MSetHolder[String]] + val js = sj.toJson(inst) + js should matchJson("""{"a":["a","b","c"]}""") + sj.fromJson(js) shouldEqual (inst) + } it("Set of boolean must work") { val inst = SetHolder[Boolean](HashSet(true, false, true)) val sj = sjCodecOf[SetHolder[Boolean]] @@ -227,5 +241,28 @@ class SeqSetArraySpec() extends AnyFunSpec with JsonMatchers: js should matchJson("""{"a":[{"name":"Bob","age",35},{"name":"Sally","age",54}]}""") sj.fromJson(js).a.toList shouldEqual (inst.a.toList) } + + it("Vector of class must work") { + val inst = VectorHolder[Person](Vector(Person("Bob", 35), Person("Sally", 54))) + val sj = sjCodecOf[VectorHolder[Person]] + val js = sj.toJson(inst) + js should matchJson("""{"a":[{"name":"Bob","age",35},{"name":"Sally","age",54}]}""") + sj.fromJson(js).a.toList shouldEqual (inst.a.toList) + } + it("IndexedSeq of class must work") { + val inst = IndexedSeqHolder[Person](IndexedSeq(Person("Bob", 35), Person("Sally", 54))) + val sj = sjCodecOf[IndexedSeqHolder[Person]] + val js = sj.toJson(inst) + js should matchJson("""{"a":[{"name":"Bob","age",35},{"name":"Sally","age",54}]}""") + sj.fromJson(js).a.toList shouldEqual (inst.a.toList) + } + it("Iterable of class must work") { + val inst = IterableHolder[Person](Seq(Person("Bob", 35), Person("Sally", 54))) + val sj = sjCodecOf[IterableHolder[Person]] + val js = sj.toJson(inst) + js should matchJson("""{"a":[{"name":"Bob","age",35},{"name":"Sally","age",54}]}""") + sj.fromJson(js).a.toList shouldEqual (inst.a.toList) + } + } } diff --git a/src/test/scala/co.blocke.scalajack/json/collections/TupleSpec.scala b/src/test/scala/co.blocke.scalajack/json/collections/TupleSpec.scala index b74f8045..bbc2947e 100644 --- a/src/test/scala/co.blocke.scalajack/json/collections/TupleSpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/collections/TupleSpec.scala @@ -36,6 +36,13 @@ class TupleSpec() extends AnyFunSpec with JsonMatchers: js should matchJson("""{"a":[[1,2],{"a":3,"b":4},[1.23,"X",true]]}""") sj.fromJson(js) shouldEqual inst } + it("Tuple of one element must work") { + val inst = TupleOneHolder[Int](Tuple1(15)) + val sj = sjCodecOf[TupleOneHolder[Int]] + val js = sj.toJson(inst) + js should matchJson("""{"a":[15]}""") + sj.fromJson(js) shouldEqual inst + } } describe(colorString("--- Negative Tests ---")) { diff --git a/src/test/scala/co.blocke.scalajack/json/misc/Model.scala b/src/test/scala/co.blocke.scalajack/json/misc/Model.scala index 043756ab..4e27fa5b 100644 --- a/src/test/scala/co.blocke.scalajack/json/misc/Model.scala +++ b/src/test/scala/co.blocke.scalajack/json/misc/Model.scala @@ -34,6 +34,7 @@ case class OptionalHolder[T]( j: Either[Optional[T], T] // Either of Optional (L) ) +case class SimpleOptionHolder[T](a: Option[T]) case class TryHolder[T](a: Try[T]) case class TryHolder2[T](a: Seq[Try[T]], b: (Try[T], Try[T])) diff --git a/src/test/scala/co.blocke.scalajack/json/misc/OptionSpec.scala b/src/test/scala/co.blocke.scalajack/json/misc/OptionSpec.scala index 3fa16ac6..4d0171b9 100644 --- a/src/test/scala/co.blocke.scalajack/json/misc/OptionSpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/misc/OptionSpec.scala @@ -176,5 +176,12 @@ class OptionSpec() extends AnyFunSpec with JsonMatchers: js should matchJson("""{"a":null}""") sj.fromJson(js) shouldEqual (EitherRecipeJ[Int](null)) } + it("Option of Either should work") { + val inst = SimpleOptionHolder[Either[Boolean, Int]](Some(Right(5))) + val sj = sjCodecOf[SimpleOptionHolder[Either[Boolean, Int]]] + val js = sj.toJson(inst) + js should matchJson("""{"a":5}""") + sj.fromJson(js) shouldEqual (inst) + } } } diff --git a/src/test/scala/co.blocke.scalajack/json/misc/TrySpec.scala b/src/test/scala/co.blocke.scalajack/json/misc/TrySpec.scala index df1c1fcf..c3f59374 100644 --- a/src/test/scala/co.blocke.scalajack/json/misc/TrySpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/misc/TrySpec.scala @@ -52,6 +52,13 @@ class TrySpec() extends AnyFunSpec with JsonMatchers: js should matchJson("""{}""") sj.fromJson(js) shouldEqual (inst) } + it("Try of Either must work (Success)") { + val inst = TryHolder[Either[Boolean, Int]](Success(Right(5))) + val sj = sjCodecOf[TryHolder[Either[Boolean, Int]]] + val js = sj.toJson(inst) + js should matchJson("""{"a":5}""") + sj.fromJson(js) shouldEqual (inst) + } it("Try w/policy AS_NULL must work (Failure)") { val inst = TryHolder[Int](Failure(new Exception("boom"))) val sj = sjCodecOf[TryHolder[Int]](SJConfig.withTryFailureHandling(TryPolicy.AS_NULL)) diff --git a/src/test/scala/co.blocke.scalajack/json/primitives/ScalaPrimSpec.scala b/src/test/scala/co.blocke.scalajack/json/primitives/ScalaPrimSpec.scala index 49ede9e6..66ae8799 100644 --- a/src/test/scala/co.blocke.scalajack/json/primitives/ScalaPrimSpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/primitives/ScalaPrimSpec.scala @@ -119,6 +119,56 @@ class ScalaPrimSpec() extends AnyFunSpec with JsonMatchers: js should matchJson("""{"s1":"something\b\n\f\r\t\""" + """u2606","s2":"","s3":null}""") sj.fromJson(js) shouldEqual inst } + + it("Any type for all primitives must work") { + val sj = sjCodecOf[AnyShell] + val prims: List[(Any, String, Option[Any => String])] = List( + (null, """{"a":null}""", None), + (scala.math.BigDecimal(5), """{"a":5}""", None), + (scala.math.BigInt(5), """{"a":5}""", None), + (true, """{"a":true}""", None), + (5.toByte, """{"a":5}""", None), + ('x', """{"a":"x"}""", Some((c: Any) => c.toString)), + (5.0, """{"a":5.0}""", None), + (5.0.toFloat, """{"a":5.0}""", None), + (5, """{"a":5}""", None), + (5L, """{"a":5}""", None), + (5.toShort, """{"a":5}""", None), + ("foo", """{"a":"foo"}""", None), + (java.lang.Boolean.valueOf(true), """{"a":true}""", None), + (java.lang.Byte.valueOf(5.toByte), """{"a":5}""", None), + (java.lang.Character.valueOf('x'), """{"a":"x"}""", Some((c: Any) => c.toString)), + (java.lang.Double.valueOf(5.0), """{"a":5.0}""", None), + (java.lang.Float.valueOf(5.0.toFloat), """{"a":5.0}""", None), + (java.lang.Integer.valueOf(5), """{"a":5}""", None), + (java.lang.Long.valueOf(5), """{"a":5}""", None), + (java.lang.Short.valueOf(5.toShort), """{"a":5}""", None), + (java.lang.Integer.valueOf(5).asInstanceOf[java.lang.Number], """{"a":5}""", None), + (java.time.Duration.ofHours(5), """{"a":"PT5H"}""", Some((c: Any) => c.toString)), + (java.time.Instant.ofEpochSecond(1234567), """{"a":"1970-01-15T06:56:07Z"}""", Some((c: Any) => c.toString)), + (java.time.LocalDate.of(2024, 3, 15), """{"a":"2024-03-15"}""", Some((c: Any) => c.toString)), + (java.time.LocalDateTime.of(2024, 3, 15, 4, 15, 3), """{"a":"2024-03-15T04:15:03"}""", Some((c: Any) => c.toString)), + (java.time.LocalTime.of(4, 15, 3), """{"a":"04:15:03"}""", Some((c: Any) => c.toString)), + (java.time.MonthDay.of(12, 25), """{"a":"--12-25"}""", Some((c: Any) => c.toString)), + (java.time.OffsetDateTime.of(2024, 3, 15, 9, 15, 1, 0, java.time.ZoneOffset.ofHours(5)), """{"a":"2024-03-15T09:15:01+05:00"}""", Some((c: Any) => c.toString)), + (java.time.OffsetTime.of(9, 15, 1, 0, java.time.ZoneOffset.ofHours(5)), """{"a":"09:15:01+05:00"}""", Some((c: Any) => c.toString)), + (java.time.Period.ofDays(5), """{"a":"P5D"}""", Some((c: Any) => c.toString)), + (java.time.Year.of(2024), """{"a":"2024"}""", Some((c: Any) => c.toString)), + (java.time.YearMonth.of(2024, 3), """{"a":"2024-03"}""", Some((c: Any) => c.toString)), + (java.time.ZoneOffset.ofHours(5), """{"a":"+05:00"}""", Some((c: Any) => c.toString)), + (java.time.ZonedDateTime.parse("2007-12-03T10:15:30+01:00"), """{"a":"2007-12-03T10:15:30+01:00"}""", Some((c: Any) => c.toString)), + (java.time.ZoneId.of("GMT+2"), """{"a":"GMT+02:00"}""", Some((c: Any) => c.toString)) + ) + prims.map { case (v, j, fn) => + val inst = AnyShell(v) + val js = sj.toJson(inst) + js should matchJson(j) + fn match { + case Some(f) => sj.fromJson(js) shouldEqual (AnyShell(f(v))) + case None => sj.fromJson(js) shouldEqual inst + } + } + } } // -------------------------------------------------------- diff --git a/src/test/scala/co.blocke.scalajack/json/primitives/SimpleSpec.scala b/src/test/scala/co.blocke.scalajack/json/primitives/SimpleSpec.scala index d47e767d..b2b7452f 100644 --- a/src/test/scala/co.blocke.scalajack/json/primitives/SimpleSpec.scala +++ b/src/test/scala/co.blocke.scalajack/json/primitives/SimpleSpec.scala @@ -166,7 +166,7 @@ class SimpleSpec() extends AnyFunSpec with JsonMatchers: it("Net types URL and URI must work") { val inst = SampleNet( null, - new URL("https://www.foom.com"), + new URI("https://www.foom.com").toURL(), null, new URI("https://www.foom.com") ) @@ -360,7 +360,7 @@ class SimpleSpec() extends AnyFunSpec with JsonMatchers: val ex = intercept[co.blocke.scalajack.json.JsonParseError](sjCodecOf[SampleNet].fromJson(js)) ex.show shouldEqual msg val js2 = """{"u1":null,"u2":"httpwww.foom.com","u3":null,"u4":"https://www.foom.com"}""" - the[java.net.MalformedURLException] thrownBy sjCodecOf[SampleNet].fromJson(js2) should have message """no protocol: httpwww.foom.com""" + the[java.lang.IllegalArgumentException] thrownBy sjCodecOf[SampleNet].fromJson(js2) should have message """URI is not absolute""" } it("UUID must break") { From cbbaafd53fcb22efb1a583dab99d54dc3150c67b Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Wed, 29 May 2024 23:34:53 -0500 Subject: [PATCH 64/65] coveralls tweak --- .github/workflows/coveralls.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/coveralls.yml b/.github/workflows/coveralls.yml index b6d50810..eea5f11c 100644 --- a/.github/workflows/coveralls.yml +++ b/.github/workflows/coveralls.yml @@ -2,7 +2,7 @@ name: Coveralls Publish on: push: - branches: [main] + branches: [master] tags: ["v*"] env: From 3dc398a5e5149d546dc648f93bace55a06dfaf19 Mon Sep 17 00:00:00 2001 From: Greg Zoller Date: Wed, 29 May 2024 23:37:02 -0500 Subject: [PATCH 65/65] tweak readme --- benchmark/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/benchmark/README.md b/benchmark/README.md index 49115186..a0662989 100644 --- a/benchmark/README.md +++ b/benchmark/README.md @@ -47,13 +47,13 @@ processing using ```scala sj[Foo](JsonConfig.withSuppressEscapedStrings()) ``` -Its for when you're 100% sure there's 0 chance of any double-quotes, newlines, tabs, or -any other non-character/digit unicode special characters in your String values that require escaping. When you need the maximum possible performance. +Its for when you need that absolute maximum performance and you're 100% sure there's 0 chance of any double-quotes, newlines, tabs, or +any other non-character/digit unicode special characters in your String values that require escaping. ### Interpretation Performance for ScalaJack has been a journey. ScalaJack is a mature product--over 10 yrs old! -Long ago it was quite fast vs its competition, but its performance lagged badly as its peers improved, +Long ago it was quite fast vs its competition, but as its peers improved its performance lagged badly, to the point that it became one of the slower serialization libraries. ScalaJack 8 changes that! I was sampling and testing against a collection of popular serializers for Scala util