Add Decoder#ofDictionaryEntry.
Change-Id: Ie78096e1a7cfd3bcfa446e3ababe15b910d0c23c
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 fee0407..96e1332 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
@@ -8,9 +8,11 @@
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;
+import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import org.apiguardian.api.API;
@@ -155,6 +157,19 @@
}
/**
+ * Creates a {@link Decoder} for a {@code Dictionary Entry} type, decoding into a {@link
+ * Map.Entry}.
+ *
+ * @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.Entry<K, V>> ofDictionaryEntry(
+ Decoder<K> keyDecoder, Decoder<V> valueDecoder) {
+ return new DictionaryEntryDecoder<>(keyDecoder, valueDecoder);
+ }
+
+ /**
* Creates a {@link Decoder} for the {@link Variant} type.
*
* <p>The contained {@link Object} can be of one of the following types:
@@ -169,6 +184,7 @@
* <li>{@link Optional} (a GVariant {@code Maybe} type)
* <li>{@link List} (a GVariant array)
* <li>{@code Object[]} (a GVariant structure)
+ * <li>{@link java.util.Map.Entry} (a dictionary entry)
* <li>{@link Variant} (a nested variant)
* </ul>
*
@@ -515,6 +531,32 @@
}
}
+ private static class DictionaryEntryDecoder<K, V> extends Decoder<Map.Entry<K, V>> {
+
+ private final TupleDecoder tupleDecoder;
+
+ DictionaryEntryDecoder(Decoder<K> keyDecoder, Decoder<V> valueDecoder) {
+ this.tupleDecoder = new TupleDecoder(keyDecoder, valueDecoder);
+ }
+
+ @Override
+ public byte alignment() {
+ return tupleDecoder.alignment();
+ }
+
+ @Override
+ public Integer fixedSize() {
+ return tupleDecoder.fixedSize();
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Map.Entry<K, V> decode(ByteBuffer byteSlice) {
+ Object[] components = tupleDecoder.decode(byteSlice);
+ return new SimpleImmutableEntry<>((K) components[0], (V) components[1]);
+ }
+ }
+
private static class VariantDecoder extends Decoder<Variant> {
@Override
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 78e4bdd..6a4d01d 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
@@ -92,7 +92,17 @@
case 'v' -> Decoder.ofVariant();
case 'm' -> Decoder.ofMaybe(parseSignature(signature));
case 'a' -> Decoder.ofArray(parseSignature(signature));
- case '(', '{' -> Decoder.ofStructure(parseTupleTypes(signature).toArray(new Decoder<?>[0]));
+ case '(' -> Decoder.ofStructure(parseTupleTypes(signature).toArray(new Decoder<?>[0]));
+ case '{' -> {
+ var tupleTypes = parseTupleTypes(signature);
+ if (tupleTypes.size() != 2) {
+ throw new ParseException(
+ String.format(
+ "dictionary entry type with %d components, expected 2", tupleTypes.size()),
+ signature.position());
+ }
+ yield Decoder.ofDictionaryEntry(tupleTypes.get(0), tupleTypes.get(1));
+ }
default -> throw new ParseException(
String.format("encountered unknown signature byte '%c'", c), signature.position());
};
diff --git a/jgvariant-core/src/main/java/eu/mulk/jgvariant/core/Variant.java b/jgvariant-core/src/main/java/eu/mulk/jgvariant/core/Variant.java
index d1c1049..34d3945 100644
--- a/jgvariant-core/src/main/java/eu/mulk/jgvariant/core/Variant.java
+++ b/jgvariant-core/src/main/java/eu/mulk/jgvariant/core/Variant.java
@@ -18,6 +18,7 @@
* <li>{@link java.util.Optional} (a GVariant {@code Maybe} type)
* <li>{@link java.util.List} (a GVariant array)
* <li>{@code Object[]} (a GVariant structure)
+ * <li>{@link java.util.Map.Entry} (a dictionary entry)
* <li>{@link Variant} (a nested variant)
* </ul>
*
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 d72a1b6..ab7de44 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
@@ -11,6 +11,7 @@
import java.nio.ByteBuffer;
import java.text.ParseException;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
import org.junit.jupiter.api.Test;
@@ -271,7 +272,18 @@
}
@Test
- void testDictionaryEntry() {
+ void testDictionaryEntryAsMapEntry() {
+ var data =
+ new byte[] {0x61, 0x20, 0x6B, 0x65, 0x79, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x06};
+
+ var decoder =
+ Decoder.ofDictionaryEntry(
+ Decoder.ofString(UTF_8), Decoder.ofInt().withByteOrder(LITTLE_ENDIAN));
+ assertEquals(Map.entry("a key", 514), decoder.decode(ByteBuffer.wrap(data)));
+ }
+
+ @Test
+ void testDictionaryEntryAsRecord() {
var data =
new byte[] {0x61, 0x20, 0x6B, 0x65, 0x79, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x06};