Remove Checker Framework; add NullAway, Picnic Error Prone Support.

Change-Id: I6558f0b4db0f9a192c18bbe45e2eaf10595bc666
diff --git a/jgvariant-core/src/main/java/eu/mulk/jgvariant/core/Decoder.java b/jgvariant-core/src/main/java/eu/mulk/jgvariant/core/Decoder.java
index 2cb36c6..4538900 100644
--- a/jgvariant-core/src/main/java/eu/mulk/jgvariant/core/Decoder.java
+++ b/jgvariant-core/src/main/java/eu/mulk/jgvariant/core/Decoder.java
@@ -5,15 +5,16 @@
 package eu.mulk.jgvariant.core;
 
 import static java.nio.ByteOrder.LITTLE_ENDIAN;
+import static java.util.Objects.requireNonNullElse;
 import static java.util.stream.Collectors.toMap;
 
+import com.google.errorprone.annotations.Immutable;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.RecordComponent;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.nio.charset.Charset;
 import java.text.ParseException;
-import java.util.AbstractMap.SimpleImmutableEntry;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -56,8 +57,9 @@
  *
  * @param <T> the type that the {@link Decoder} can decode.
  */
-@SuppressWarnings("java:S1610")
 @API(status = Status.EXPERIMENTAL)
+@Immutable
+@SuppressWarnings({"ImmutableListOf", "InvalidInlineTag", "java:S1610", "UnescapedEntity"})
 public abstract class Decoder<T> {
 
   private Decoder() {}
@@ -523,6 +525,7 @@
     }
   }
 
+  @SuppressWarnings("Immutable")
   private static class TupleDecoder extends Decoder<Object[]> {
 
     private final Decoder<?>[] componentDecoders;
@@ -625,7 +628,7 @@
     @SuppressWarnings("unchecked")
     public Map.@NotNull Entry<K, V> decode(ByteBuffer byteSlice) {
       Object[] components = tupleDecoder.decode(byteSlice);
-      return new SimpleImmutableEntry<>((K) components[0], (V) components[1]);
+      return Map.entry((K) components[0], (V) components[1]);
     }
   }
 
@@ -800,6 +803,7 @@
     }
   }
 
+  @SuppressWarnings("Immutable")
   private class MappingDecoder<U> extends Decoder<U> {
 
     private final Function<@NotNull T, @NotNull U> function;
@@ -824,6 +828,7 @@
     }
   }
 
+  @SuppressWarnings("Immutable")
   private class ContramappingDecoder extends Decoder<T> {
 
     private final UnaryOperator<ByteBuffer> function;
@@ -879,6 +884,7 @@
     return byteSlice.slice(index, length).order(byteSlice.order());
   }
 
+  @SuppressWarnings("Immutable")
   private static class PredicateDecoder<U> extends Decoder<U> {
 
     private final Predicate<ByteBuffer> selector;
@@ -900,8 +906,8 @@
         throw new IllegalArgumentException(
             "incompatible sizes in predicate branches: then=%s, else=%s"
                 .formatted(
-                    Objects.requireNonNullElse(thenDecoder.fixedSize(), "(null)"),
-                    Objects.requireNonNullElse(elseDecoder.fixedSize(), "(null)")));
+                    requireNonNullElse(thenDecoder.fixedSize(), "(null)"),
+                    requireNonNullElse(elseDecoder.fixedSize(), "(null)")));
       }
     }
 
diff --git a/jgvariant-core/src/main/java/eu/mulk/jgvariant/core/Signature.java b/jgvariant-core/src/main/java/eu/mulk/jgvariant/core/Signature.java
index cc9674d..4c8cd65 100644
--- a/jgvariant-core/src/main/java/eu/mulk/jgvariant/core/Signature.java
+++ b/jgvariant-core/src/main/java/eu/mulk/jgvariant/core/Signature.java
@@ -4,8 +4,10 @@
 
 package eu.mulk.jgvariant.core;
 
+import static java.nio.charset.StandardCharsets.US_ASCII;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
 import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
 import java.text.ParseException;
 import java.util.ArrayList;
 import java.util.List;
@@ -41,7 +43,7 @@
     this.decoder = parseSignature(signatureBytes);
 
     signatureBytes.rewind();
-    this.signatureString = StandardCharsets.US_ASCII.decode(signatureBytes).toString();
+    this.signatureString = US_ASCII.decode(signatureBytes).toString();
   }
 
   static Signature parse(ByteBuffer signatureBytes) throws ParseException {
@@ -49,7 +51,7 @@
   }
 
   public static Signature parse(String signatureString) throws ParseException {
-    var signatureBytes = ByteBuffer.wrap(signatureString.getBytes(StandardCharsets.US_ASCII));
+    var signatureBytes = ByteBuffer.wrap(signatureString.getBytes(US_ASCII));
     return parse(signatureBytes);
   }
 
@@ -93,7 +95,7 @@
       case 'i', 'u' -> Decoder.ofInt();
       case 'x', 't' -> Decoder.ofLong();
       case 'd' -> Decoder.ofDouble();
-      case 's', 'o', 'g' -> Decoder.ofString(StandardCharsets.UTF_8);
+      case 's', 'o', 'g' -> Decoder.ofString(UTF_8);
       case 'v' -> Decoder.ofVariant();
       case 'm' -> Decoder.ofMaybe(parseSignature(signature));
       case '(' -> Decoder.ofStructure(parseTupleTypes(signature).toArray(new Decoder<?>[0]));
diff --git a/jgvariant-core/src/test/java/eu/mulk/jgvariant/core/DecoderTest.java b/jgvariant-core/src/test/java/eu/mulk/jgvariant/core/DecoderTest.java
index 3f826be..6399f6e 100644
--- a/jgvariant-core/src/test/java/eu/mulk/jgvariant/core/DecoderTest.java
+++ b/jgvariant-core/src/test/java/eu/mulk/jgvariant/core/DecoderTest.java
@@ -23,17 +23,26 @@
  * Tests based on the examples given in <a
  * href="https://people.gnome.org/~desrt/gvariant-serialisation.pdf">~desrt/gvariant-serialisation.pdf</a>.
  */
+@SuppressWarnings({
+  "ImmutableListOf",
+  "ImmutableListOf1",
+  "ImmutableListOf2",
+  "ImmutableListOf3",
+  "ImmutableListOf4",
+  "ImmutableListOf5",
+  "ImmutableMapOf2"
+})
 class DecoderTest {
 
   @Test
-  void testString() {
+  void string() {
     var data = new byte[] {0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x00};
     var decoder = Decoder.ofString(UTF_8);
     assertEquals("hello world", decoder.decode(ByteBuffer.wrap(data)));
   }
 
   @Test
-  void testMaybe() {
+  void maybe() {
     var data =
         new byte[] {0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x00, 0x00};
     var decoder = Decoder.ofMaybe(Decoder.ofString(UTF_8));
@@ -41,14 +50,14 @@
   }
 
   @Test
-  void testBooleanArray() {
+  void booleanArray() {
     var data = new byte[] {0x01, 0x00, 0x00, 0x01, 0x01};
     var decoder = Decoder.ofArray(Decoder.ofBoolean());
     assertEquals(List.of(true, false, false, true, true), decoder.decode(ByteBuffer.wrap(data)));
   }
 
   @Test
-  void testStructure() {
+  void structure() {
     var data =
         new byte[] {
           0x66, 0x6F, 0x6F, 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, 0x04
@@ -61,7 +70,7 @@
   }
 
   @Test
-  void testComplexStructureArray() {
+  void complexStructureArray() {
     var data =
         new byte[] {
           0x68,
@@ -103,7 +112,7 @@
   }
 
   @Test
-  void testDictionary() {
+  void dictionary() {
     var data =
         new byte[] {
           0x68,
@@ -137,7 +146,7 @@
   }
 
   @Test
-  void testStringArray() {
+  void stringArray() {
     var data =
         new byte[] {
           0x69, 0x00, 0x63, 0x61, 0x6E, 0x00, 0x68, 0x61, 0x73, 0x00, 0x73, 0x74, 0x72, 0x69, 0x6E,
@@ -148,7 +157,7 @@
   }
 
   @Test
-  void testNestedStructure() {
+  void nestedStructure() {
     var data =
         new byte[] {
           0x69, 0x63, 0x61, 0x6E, 0x00, 0x68, 0x61, 0x73, 0x00, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67,
@@ -170,7 +179,7 @@
   }
 
   @Test
-  void testNestedStructureVariant() {
+  void nestedStructureVariant() {
     var data =
         new byte[] {
           0x69, 0x63, 0x61, 0x6E, 0x00, 0x68, 0x61, 0x73, 0x00, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67,
@@ -189,7 +198,7 @@
   }
 
   @Test
-  void testSimpleStructure() {
+  void simpleStructure() {
     var data = new byte[] {0x60, 0x70};
 
     record TestRecord(byte b1, byte b2) {}
@@ -204,7 +213,7 @@
   }
 
   @Test
-  void testPaddedStructureRight() {
+  void paddedStructureRight() {
     var data = new byte[] {0x60, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00};
 
     record TestRecord(int b1, byte b2) {}
@@ -219,7 +228,7 @@
   }
 
   @Test
-  void testPaddedStructureLeft() {
+  void paddedStructureLeft() {
     var data = new byte[] {0x60, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00};
 
     record TestRecord(byte b1, int b2) {}
@@ -234,7 +243,7 @@
   }
 
   @Test
-  void testSimpleStructureArray() {
+  void simpleStructureArray() {
     var data =
         new byte[] {
           0x60,
@@ -270,7 +279,7 @@
   }
 
   @Test
-  void testByteArray() {
+  void byteArray() {
     var data = new byte[] {0x04, 0x05, 0x06, 0x07};
 
     var decoder = Decoder.ofArray(Decoder.ofByte());
@@ -281,7 +290,7 @@
   }
 
   @Test
-  void testPrimitiveByteArray() {
+  void primitiveByteArray() {
     var data = new byte[] {0x04, 0x05, 0x06, 0x07};
 
     var decoder = Decoder.ofByteArray();
@@ -290,7 +299,7 @@
   }
 
   @Test
-  void testPrimitiveByteArrayRecord() {
+  void primitiveByteArrayRecord() {
     var data = new byte[] {0x04, 0x05, 0x06, 0x07};
 
     record TestRecord(byte[] bytes) {}
@@ -301,7 +310,7 @@
   }
 
   @Test
-  void testIntegerArray() {
+  void integerArray() {
     var data = new byte[] {0x04, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00};
 
     var decoder = Decoder.ofArray(Decoder.ofInt().withByteOrder(LITTLE_ENDIAN));
@@ -310,7 +319,7 @@
   }
 
   @Test
-  void testDictionaryEntryAsMapEntry() {
+  void dictionaryEntryAsMapEntry() {
     var data =
         new byte[] {0x61, 0x20, 0x6B, 0x65, 0x79, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x06};
 
@@ -321,7 +330,7 @@
   }
 
   @Test
-  void testDictionaryEntryAsRecord() {
+  void dictionaryEntryAsRecord() {
     var data =
         new byte[] {0x61, 0x20, 0x6B, 0x65, 0x79, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x06};
 
@@ -334,7 +343,7 @@
   }
 
   @Test
-  void testPaddedPrimitives() {
+  void paddedPrimitives() {
     var data =
         new byte[] {
           0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -353,7 +362,7 @@
   }
 
   @Test
-  void testEmbeddedMaybe() {
+  void embeddedMaybe() {
     var data = new byte[] {0x01, 0x01};
 
     record TestRecord(Optional<Byte> set, Optional<Byte> unset) {}
@@ -367,7 +376,7 @@
   }
 
   @Test
-  void testRecordComponentMismatch() {
+  void recordComponentMismatch() {
     record TestRecord(Optional<Byte> set) {}
 
     var maybeDecoder = Decoder.ofMaybe(Decoder.ofByte());
@@ -377,7 +386,7 @@
   }
 
   @Test
-  void testTrivialRecord() {
+  void trivialRecord() {
     var data = new byte[] {0x00};
 
     record TestRecord() {}
@@ -387,7 +396,7 @@
   }
 
   @Test
-  void testTwoElementTrivialRecordArray() {
+  void twoElementTrivialRecordArray() {
     var data = new byte[] {0x00, 0x00};
 
     record TestRecord() {}
@@ -398,7 +407,7 @@
   }
 
   @Test
-  void testSingletonTrivialRecordArray() {
+  void singletonTrivialRecordArray() {
     var data = new byte[] {0x00};
 
     record TestRecord() {}
@@ -408,7 +417,7 @@
   }
 
   @Test
-  void testEmptyTrivialRecordArray() {
+  void emptyTrivialRecordArray() {
     var data = new byte[] {};
 
     record TestRecord() {}
@@ -418,7 +427,7 @@
   }
 
   @Test
-  void testVariantArray() {
+  void variantArray() {
     var data = new byte[] {};
 
     record TestRecord() {}
@@ -428,7 +437,7 @@
   }
 
   @Test
-  void testInvalidVariantSignature() {
+  void invalidVariantSignature() {
     var data = new byte[] {0x00, 0x00, 0x2E};
 
     var decoder = Decoder.ofVariant();
@@ -436,7 +445,7 @@
   }
 
   @Test
-  void testMissingVariantSignature() {
+  void missingVariantSignature() {
     var data = new byte[] {0x01};
 
     var decoder = Decoder.ofVariant();
@@ -444,7 +453,7 @@
   }
 
   @Test
-  void testSimpleVariantRecord() throws ParseException {
+  void simpleVariantRecord() throws ParseException {
     // signature: "(bynqiuxtdsogvmiai)"
     var data =
         new byte[] {
@@ -494,7 +503,7 @@
   }
 
   @Test
-  void testSignatureString() throws ParseException {
+  void signatureString() throws ParseException {
     var data =
         new byte[] {
           0x28, 0x62, 0x79, 0x6E, 0x71, 0x69, 0x75, 0x78, 0x74, 0x64, 0x73, 0x6F, 0x67, 0x76, 0x6D,
@@ -506,21 +515,21 @@
   }
 
   @Test
-  void testMap() {
+  void map() {
     var data = new byte[] {0x0A, 0x0B, 0x0C};
     var decoder = Decoder.ofByteArray().map(bytes -> bytes.length);
     assertEquals(3, decoder.decode(ByteBuffer.wrap(data)));
   }
 
   @Test
-  void testContramap() {
+  void contramap() {
     var data = new byte[] {0x0A, 0x0B, 0x0C};
     var decoder = Decoder.ofByteArray().contramap(bytes -> bytes.slice(1, 1));
     assertArrayEquals(new byte[] {0x0B}, decoder.decode(ByteBuffer.wrap(data)));
   }
 
   @Test
-  void testPredicateTrue() {
+  void predicateTrue() {
     var data = new byte[] {0x00, 0x01, 0x00};
     var innerDecoder = Decoder.ofShort().contramap(bytes -> bytes.slice(1, 2).order(bytes.order()));
     var decoder =
@@ -532,7 +541,7 @@
   }
 
   @Test
-  void testPredicateFalse() {
+  void predicateFalse() {
     var data = new byte[] {0x01, 0x01, 0x00};
     var innerDecoder = Decoder.ofShort().contramap(bytes -> bytes.slice(1, 2).order(bytes.order()));
     var decoder =
@@ -544,7 +553,7 @@
   }
 
   @Test
-  void testByteOrder() {
+  void byteOrder() {
     var data =
         new byte[] {
           0x01, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x04, 0x05, 0x00, 0x00, 0x06, 0x00, 0x07, 0x08,