Add Decoder#ofDictionary.
Change-Id: I53873f743ce84d9bf50da4cb5238a6f4d82de986
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 96e1332..8297d79 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
@@ -1,6 +1,7 @@
package eu.mulk.jgvariant.core;
import static java.nio.ByteOrder.LITTLE_ENDIAN;
+import static java.util.stream.Collectors.toMap;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.RecordComponent;
@@ -13,6 +14,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Optional;
import java.util.function.Function;
import org.apiguardian.api.API;
@@ -110,6 +112,18 @@
}
/**
+ * Creates a {@link Decoder} for a {@code Dictionary} type.
+ *
+ * @param keyDecoder a {@link Decoder} for the key component of the dictionary entry.
+ * @param valueDecoder a {@link Decoder} for the value component of the dictionary entry.
+ * @return a new {@link Decoder}.
+ */
+ public static <K, V> Decoder<Map<K, V>> ofDictionary(
+ Decoder<K> keyDecoder, Decoder<V> valueDecoder) {
+ return new DictionaryDecoder<>(keyDecoder, valueDecoder);
+ }
+
+ /**
* Creates a {@link Decoder} for an {@code Array} type of element type {@code byte} into a
* primitive {@code byte[]} array.
*
@@ -346,6 +360,32 @@
}
}
+ private static class DictionaryDecoder<K, V> extends Decoder<Map<K, V>> {
+
+ private final ArrayDecoder<Map.Entry<K, V>> entryArrayDecoder;
+
+ DictionaryDecoder(Decoder<K> keyDecoder, Decoder<V> valueDecoder) {
+ this.entryArrayDecoder =
+ new ArrayDecoder<>(new DictionaryEntryDecoder<>(keyDecoder, valueDecoder));
+ }
+
+ @Override
+ public byte alignment() {
+ return entryArrayDecoder.alignment();
+ }
+
+ @Override
+ public Integer fixedSize() {
+ return entryArrayDecoder.fixedSize();
+ }
+
+ @Override
+ public Map<K, V> decode(ByteBuffer byteSlice) {
+ List<Map.Entry<K, V>> entries = entryArrayDecoder.decode(byteSlice);
+ return entries.stream().collect(toMap(Entry::getKey, Entry::getValue));
+ }
+ }
+
private static class ByteArrayDecoder extends Decoder<byte[]> {
private static final int ELEMENT_SIZE = 1;
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 ab7de44..efbcafa 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
@@ -99,6 +99,40 @@
}
@Test
+ void testDictionary() {
+ var data =
+ new byte[] {
+ 0x68,
+ 0x69,
+ 0x00,
+ 0x00,
+ (byte) 0xfe,
+ (byte) 0xff,
+ (byte) 0xff,
+ (byte) 0xff,
+ 0x03,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x62,
+ 0x79,
+ 0x65,
+ 0x00,
+ (byte) 0xff,
+ (byte) 0xff,
+ (byte) 0xff,
+ (byte) 0xff,
+ 0x04,
+ 0x09,
+ 0x15
+ };
+
+ var decoder =
+ Decoder.ofDictionary(Decoder.ofString(UTF_8), Decoder.ofInt().withByteOrder(LITTLE_ENDIAN));
+ assertEquals(Map.of("hi", -2, "bye", -1), decoder.decode(ByteBuffer.wrap(data)));
+ }
+
+ @Test
void testStringArray() {
var data =
new byte[] {