Decoder: Make anonymous classes into named inner classes.
Change-Id: I3150b262512cafe42f139d56c9ecee165da4e1df
diff --git a/src/main/java/eu/mulk/jgvariant/core/Decoder.java b/src/main/java/eu/mulk/jgvariant/core/Decoder.java
index 8134d45..bb479ff 100644
--- a/src/main/java/eu/mulk/jgvariant/core/Decoder.java
+++ b/src/main/java/eu/mulk/jgvariant/core/Decoder.java
@@ -96,52 +96,118 @@
* @return a new {@link Decoder}.
*/
public static <U> Decoder<List<U>> ofArray(Decoder<U> elementDecoder) {
- return new Decoder<>() {
- @Override
- public byte alignment() {
- return elementDecoder.alignment();
- }
+ return new ArrayDecoder<>(elementDecoder);
+ }
- @Override
- @Nullable
- Integer fixedSize() {
- return null;
- }
+ /**
+ * Creates a {@link Decoder} for a {@code Maybe} type.
+ *
+ * @param elementDecoder a {@link Decoder} for the contained element.
+ * @param <U> the element type.
+ * @return a new {@link Decoder}.
+ */
+ public static <U> Decoder<Optional<U>> ofMaybe(Decoder<U> elementDecoder) {
+ return new MaybeDecoder<>(elementDecoder);
+ }
- @Override
- public List<U> decode(ByteBuffer byteSlice) {
- List<U> elements;
+ /**
+ * Creates a {@link Decoder} for a {@code Structure} type.
+ *
+ * @param recordType the {@link Record} type that represents the components of the structure.
+ * @param componentDecoders a {@link Decoder} for each component of the structure.
+ * @param <U> the {@link Record} type that represents the components of the structure.
+ * @return a new {@link Decoder}.
+ */
+ public static <U extends Record> Decoder<U> ofStructure(
+ Class<U> recordType, Decoder<?>... componentDecoders) {
+ return new StructureDecoder<>(recordType, componentDecoders);
+ }
- var elementSize = elementDecoder.fixedSize();
- if (elementSize != null) {
- // A simple C-style array.
- elements = new ArrayList<>(byteSlice.limit() / elementSize);
- for (int i = 0; i < byteSlice.limit(); i += elementSize) {
- var element = elementDecoder.decode(byteSlice.slice(i, elementSize));
- elements.add(element);
- }
- } else {
- // An array with aligned elements and a vector of framing offsets in the end.
- int framingOffsetSize = byteCount(byteSlice.limit());
- int lastFramingOffset =
- getIntN(byteSlice.slice(byteSlice.limit() - framingOffsetSize, framingOffsetSize));
- int elementCount = (byteSlice.limit() - lastFramingOffset) / framingOffsetSize;
+ /**
+ * Creates a {@link Decoder} for the {@link Variant} type.
+ *
+ * @return a new {@link Decoder}.
+ */
+ public static Decoder<Variant> ofVariant() {
+ return new VariantDecoder();
+ }
- elements = new ArrayList<>(elementCount);
- int position = 0;
- for (int i = 0; i < elementCount; i++) {
- int framingOffset =
- getIntN(
- byteSlice.slice(lastFramingOffset + i * framingOffsetSize, framingOffsetSize));
- elements.add(
- elementDecoder.decode(byteSlice.slice(position, framingOffset - position)));
- position = align(framingOffset, alignment());
- }
- }
+ /**
+ * Creates a {@link Decoder} for the {@code Boolean} type.
+ *
+ * @return a new {@link Decoder}.
+ */
+ public static Decoder<Boolean> ofBoolean() {
+ return new BooleanDecoder();
+ }
- return elements;
- }
- };
+ /**
+ * Creates a {@link Decoder} for the 8-bit {@ode byte} type.
+ *
+ * <p><strong>Note:</strong> It is often useful to apply {@link #withByteOrder(ByteOrder)} to the
+ * result of this method.
+ *
+ * @return a new {@link Decoder}.
+ */
+ public static Decoder<Byte> ofByte() {
+ return new ByteDecoder();
+ }
+
+ /**
+ * Creates a {@link Decoder} for the 16-bit {@code short} type.
+ *
+ * <p><strong>Note:</strong> It is often useful to apply {@link #withByteOrder(ByteOrder)} to the
+ * result of this method.
+ *
+ * @return a new {@link Decoder}.
+ */
+ public static Decoder<Short> ofShort() {
+ return new ShortDecoder();
+ }
+
+ /**
+ * Creates a {@link Decoder} for the 32-bit {@code int} type.
+ *
+ * <p><strong>Note:</strong> It is often useful to apply {@link #withByteOrder(ByteOrder)} to the
+ * result of this method.
+ *
+ * @return a new {@link Decoder}.
+ */
+ public static Decoder<Integer> ofInt() {
+ return new IntegerDecoder();
+ }
+
+ /**
+ * Creates a {@link Decoder} for the 64-bit {@code long} type.
+ *
+ * <p><strong>Note:</strong> It is often useful to apply {@link #withByteOrder(ByteOrder)} to the
+ * result of this method.
+ *
+ * @return a new {@link Decoder}.
+ */
+ public static Decoder<Long> ofLong() {
+ return new LongDecoder();
+ }
+
+ /**
+ * Creates a {@link Decoder} for the {@code double} type.
+ *
+ * @return a new {@link Decoder}.
+ */
+ public static Decoder<Double> ofDouble() {
+ return new DoubleDecoder();
+ }
+
+ /**
+ * Creates a {@link Decoder} for the {@link String} type.
+ *
+ * <p><strong>Note:</strong> While GVariant does not prescribe any particular encoding, {@link
+ * java.nio.charset.StandardCharsets#UTF_8} is the most common choice.
+ *
+ * @return a new {@link Decoder}.
+ */
+ public static Decoder<String> ofString(Charset charset) {
+ return new StringDecoder(charset);
}
private static int align(int offset, byte alignment) {
@@ -159,350 +225,345 @@
return n < (1 << 8) ? 1 : n < (1 << 16) ? 2 : 4;
}
- /**
- * Creates a {@link Decoder} for a {@code Maybe} type.
- *
- * @param elementDecoder a {@link Decoder} for the contained element.
- * @param <U> the element type.
- * @return a new {@link Decoder}.
- */
- public static <U> Decoder<Optional<U>> ofMaybe(Decoder<U> elementDecoder) {
- return new Decoder<>() {
- @Override
- public byte alignment() {
- return elementDecoder.alignment();
- }
+ private static class ArrayDecoder<U> extends Decoder<List<U>> {
- @Override
- @Nullable
- Integer fixedSize() {
- return null;
- }
+ private final Decoder<U> elementDecoder;
- @Override
- public Optional<U> decode(ByteBuffer byteSlice) {
- if (!byteSlice.hasRemaining()) {
- return Optional.empty();
- } else {
- if (!elementDecoder.hasFixedSize()) {
- // Remove trailing zero byte.
- byteSlice.limit(byteSlice.limit() - 1);
- }
-
- return Optional.of(elementDecoder.decode(byteSlice));
- }
- }
- };
- }
-
- /**
- * Creates a {@link Decoder} for a {@code Structure} type.
- *
- * @param recordType the {@link Record} type that represents the components of the structure.
- * @param componentDecoders a {@link Decoder} for each component of the structure.
- * @param <U> the {@link Record} type that represents the components of the structure.
- * @return a new {@link Decoder}.
- */
- public static <U extends Record> Decoder<U> ofStructure(
- Class<U> recordType, Decoder<?>... componentDecoders) {
- var recordComponents = recordType.getRecordComponents();
- if (componentDecoders.length != recordComponents.length) {
- throw new IllegalArgumentException(
- "number of decoders (%d) does not match number of structure components (%d)"
- .formatted(componentDecoders.length, recordComponents.length));
+ ArrayDecoder(Decoder<U> elementDecoder) {
+ this.elementDecoder = elementDecoder;
}
- return new Decoder<>() {
- @Override
- public byte alignment() {
- return (byte) Arrays.stream(componentDecoders).mapToInt(Decoder::alignment).max().orElse(1);
- }
+ @Override
+ public byte alignment() {
+ return elementDecoder.alignment();
+ }
- @Override
- public Integer fixedSize() {
- int position = 0;
- for (var componentDecoder : componentDecoders) {
- var fixedComponentSize = componentDecoder.fixedSize();
- if (fixedComponentSize == null) {
- return null;
- }
+ @Override
+ @Nullable
+ Integer fixedSize() {
+ return null;
+ }
- position = align(position, componentDecoder.alignment());
- position += fixedComponentSize;
+ @Override
+ public List<U> decode(ByteBuffer byteSlice) {
+ List<U> elements;
+
+ var elementSize = elementDecoder.fixedSize();
+ if (elementSize != null) {
+ // A simple C-style array.
+ elements = new ArrayList<>(byteSlice.limit() / elementSize);
+ for (int i = 0; i < byteSlice.limit(); i += elementSize) {
+ var element = elementDecoder.decode(byteSlice.slice(i, elementSize));
+ elements.add(element);
}
-
- if (position == 0) {
- return 1;
- }
-
- return align(position, alignment());
- }
-
- @Override
- public U decode(ByteBuffer byteSlice) {
+ } else {
+ // An array with aligned elements and a vector of framing offsets in the end.
int framingOffsetSize = byteCount(byteSlice.limit());
+ int lastFramingOffset =
+ getIntN(byteSlice.slice(byteSlice.limit() - framingOffsetSize, framingOffsetSize));
+ int elementCount = (byteSlice.limit() - lastFramingOffset) / framingOffsetSize;
- var recordConstructorArguments = new Object[recordComponents.length];
-
+ elements = new ArrayList<>(elementCount);
int position = 0;
- int framingOffsetIndex = 0;
- int componentIndex = 0;
- for (var componentDecoder : componentDecoders) {
- position = align(position, componentDecoder.alignment());
+ for (int i = 0; i < elementCount; i++) {
+ int framingOffset =
+ getIntN(
+ byteSlice.slice(lastFramingOffset + i * framingOffsetSize, framingOffsetSize));
+ elements.add(elementDecoder.decode(byteSlice.slice(position, framingOffset - position)));
+ position = align(framingOffset, alignment());
+ }
+ }
- var fixedComponentSize = componentDecoder.fixedSize();
- if (fixedComponentSize != null) {
+ return elements;
+ }
+ }
+
+ private static class MaybeDecoder<U> extends Decoder<Optional<U>> {
+
+ private final Decoder<U> elementDecoder;
+
+ MaybeDecoder(Decoder<U> elementDecoder) {
+ this.elementDecoder = elementDecoder;
+ }
+
+ @Override
+ public byte alignment() {
+ return elementDecoder.alignment();
+ }
+
+ @Override
+ @Nullable
+ Integer fixedSize() {
+ return null;
+ }
+
+ @Override
+ public Optional<U> decode(ByteBuffer byteSlice) {
+ if (!byteSlice.hasRemaining()) {
+ return Optional.empty();
+ } else {
+ if (!elementDecoder.hasFixedSize()) {
+ // Remove trailing zero byte.
+ byteSlice.limit(byteSlice.limit() - 1);
+ }
+
+ return Optional.of(elementDecoder.decode(byteSlice));
+ }
+ }
+ }
+
+ private static class StructureDecoder<U extends Record> extends Decoder<U> {
+
+ private final RecordComponent[] recordComponents;
+ private final Class<U> recordType;
+ private final Decoder<?>[] componentDecoders;
+
+ StructureDecoder(Class<U> recordType, Decoder<?>... componentDecoders) {
+ var recordComponents = recordType.getRecordComponents();
+
+ if (componentDecoders.length != recordComponents.length) {
+ throw new IllegalArgumentException(
+ "number of decoders (%d) does not match number of structure components (%d)"
+ .formatted(componentDecoders.length, recordComponents.length));
+ }
+
+ this.recordComponents = recordComponents;
+ this.recordType = recordType;
+ this.componentDecoders = componentDecoders;
+ }
+
+ @Override
+ public byte alignment() {
+ return (byte) Arrays.stream(componentDecoders).mapToInt(Decoder::alignment).max().orElse(1);
+ }
+
+ @Override
+ public Integer fixedSize() {
+ int position = 0;
+ for (var componentDecoder : componentDecoders) {
+ var fixedComponentSize = componentDecoder.fixedSize();
+ if (fixedComponentSize == null) {
+ return null;
+ }
+
+ position = align(position, componentDecoder.alignment());
+ position += fixedComponentSize;
+ }
+
+ if (position == 0) {
+ return 1;
+ }
+
+ return align(position, alignment());
+ }
+
+ @Override
+ public U decode(ByteBuffer byteSlice) {
+ int framingOffsetSize = byteCount(byteSlice.limit());
+
+ var recordConstructorArguments = new Object[recordComponents.length];
+
+ int position = 0;
+ int framingOffsetIndex = 0;
+ int componentIndex = 0;
+ for (var componentDecoder : componentDecoders) {
+ position = align(position, componentDecoder.alignment());
+
+ var fixedComponentSize = componentDecoder.fixedSize();
+ if (fixedComponentSize != null) {
+ recordConstructorArguments[componentIndex] =
+ componentDecoder.decode(byteSlice.slice(position, fixedComponentSize));
+ position += fixedComponentSize;
+ } else {
+ if (componentIndex == recordComponents.length - 1) {
+ // The last component never has a framing offset.
+ int endPosition = byteSlice.limit() - framingOffsetIndex * framingOffsetSize;
recordConstructorArguments[componentIndex] =
- componentDecoder.decode(byteSlice.slice(position, fixedComponentSize));
- position += fixedComponentSize;
+ componentDecoder.decode(byteSlice.slice(position, endPosition - position));
+ position = endPosition;
} else {
- if (componentIndex == recordComponents.length - 1) {
- // The last component never has a framing offset.
- int endPosition = byteSlice.limit() - framingOffsetIndex * framingOffsetSize;
- recordConstructorArguments[componentIndex] =
- componentDecoder.decode(byteSlice.slice(position, endPosition - position));
- position = endPosition;
- } else {
- int framingOffset =
- getIntN(
- byteSlice.slice(
- byteSlice.limit() - (1 + framingOffsetIndex) * framingOffsetSize,
- framingOffsetSize));
- recordConstructorArguments[componentIndex] =
- componentDecoder.decode(byteSlice.slice(position, framingOffset - position));
- position = framingOffset;
- ++framingOffsetIndex;
- }
+ int framingOffset =
+ getIntN(
+ byteSlice.slice(
+ byteSlice.limit() - (1 + framingOffsetIndex) * framingOffsetSize,
+ framingOffsetSize));
+ recordConstructorArguments[componentIndex] =
+ componentDecoder.decode(byteSlice.slice(position, framingOffset - position));
+ position = framingOffset;
+ ++framingOffsetIndex;
}
-
- ++componentIndex;
}
- try {
- var recordComponentTypes =
- Arrays.stream(recordType.getRecordComponents())
- .map(RecordComponent::getType)
- .toArray(Class<?>[]::new);
- var recordConstructor = recordType.getDeclaredConstructor(recordComponentTypes);
- return recordConstructor.newInstance(recordConstructorArguments);
- } catch (NoSuchMethodException
- | InstantiationException
- | IllegalAccessException
- | InvocationTargetException e) {
- throw new IllegalStateException(e);
- }
+ ++componentIndex;
}
- };
+
+ try {
+ var recordComponentTypes =
+ Arrays.stream(recordType.getRecordComponents())
+ .map(RecordComponent::getType)
+ .toArray(Class<?>[]::new);
+ var recordConstructor = recordType.getDeclaredConstructor(recordComponentTypes);
+ return recordConstructor.newInstance(recordConstructorArguments);
+ } catch (NoSuchMethodException
+ | InstantiationException
+ | IllegalAccessException
+ | InvocationTargetException e) {
+ throw new IllegalStateException(e);
+ }
+ }
}
- /**
- * Creates a {@link Decoder} for the {@link Variant} type.
- *
- * @return a new {@link Decoder}.
- */
- public static Decoder<Variant> ofVariant() {
- return new Decoder<>() {
- @Override
- public byte alignment() {
- return 8;
- }
+ private static class VariantDecoder extends Decoder<Variant> {
- @Override
- @Nullable
- Integer fixedSize() {
- return null;
- }
+ @Override
+ public byte alignment() {
+ return 8;
+ }
- @Override
- public Variant decode(ByteBuffer byteSlice) {
- // TODO
- throw new UnsupportedOperationException("not implemented");
- }
- };
+ @Override
+ @Nullable
+ Integer fixedSize() {
+ return null;
+ }
+
+ @Override
+ public Variant decode(ByteBuffer byteSlice) {
+ // TODO
+ throw new UnsupportedOperationException("not implemented");
+ }
}
- /**
- * Creates a {@link Decoder} for the {@code Boolean} type.
- *
- * @return a new {@link Decoder}.
- */
- public static Decoder<Boolean> ofBoolean() {
- return new Decoder<>() {
- @Override
- public byte alignment() {
- return 1;
- }
+ private static class BooleanDecoder extends Decoder<Boolean> {
- @Override
- public Integer fixedSize() {
- return 1;
- }
+ @Override
+ public byte alignment() {
+ return 1;
+ }
- @Override
- public Boolean decode(ByteBuffer byteSlice) {
- return byteSlice.get() != 0;
- }
- };
+ @Override
+ public Integer fixedSize() {
+ return 1;
+ }
+
+ @Override
+ public Boolean decode(ByteBuffer byteSlice) {
+ return byteSlice.get() != 0;
+ }
}
- /**
- * Creates a {@link Decoder} for the 8-bit {@ode byte} type.
- *
- * <p><strong>Note:</strong> It is often useful to apply {@link #withByteOrder(ByteOrder)} to the
- * result of this method.
- *
- * @return a new {@link Decoder}.
- */
- public static Decoder<Byte> ofByte() {
- return new Decoder<>() {
- @Override
- public byte alignment() {
- return 1;
- }
+ private static class ByteDecoder extends Decoder<Byte> {
- @Override
- public Integer fixedSize() {
- return 1;
- }
+ @Override
+ public byte alignment() {
+ return 1;
+ }
- @Override
- public Byte decode(ByteBuffer byteSlice) {
- return byteSlice.get();
- }
- };
+ @Override
+ public Integer fixedSize() {
+ return 1;
+ }
+
+ @Override
+ public Byte decode(ByteBuffer byteSlice) {
+ return byteSlice.get();
+ }
}
- /**
- * Creates a {@link Decoder} for the 16-bit {@code short} type.
- *
- * <p><strong>Note:</strong> It is often useful to apply {@link #withByteOrder(ByteOrder)} to the
- * result of this method.
- *
- * @return a new {@link Decoder}.
- */
- public static Decoder<Short> ofShort() {
- return new Decoder<>() {
- @Override
- public byte alignment() {
- return 2;
- }
+ private static class ShortDecoder extends Decoder<Short> {
- @Override
- public Integer fixedSize() {
- return 2;
- }
+ @Override
+ public byte alignment() {
+ return 2;
+ }
- @Override
- public Short decode(ByteBuffer byteSlice) {
- return byteSlice.getShort();
- }
- };
+ @Override
+ public Integer fixedSize() {
+ return 2;
+ }
+
+ @Override
+ public Short decode(ByteBuffer byteSlice) {
+ return byteSlice.getShort();
+ }
}
- /**
- * Creates a {@link Decoder} for the 32-bit {@code int} type.
- *
- * <p><strong>Note:</strong> It is often useful to apply {@link #withByteOrder(ByteOrder)} to the
- * result of this method.
- *
- * @return a new {@link Decoder}.
- */
- public static Decoder<Integer> ofInt() {
- return new Decoder<>() {
- @Override
- public byte alignment() {
- return 4;
- }
+ private static class IntegerDecoder extends Decoder<Integer> {
- @Override
- public Integer fixedSize() {
- return 4;
- }
+ @Override
+ public byte alignment() {
+ return 4;
+ }
- @Override
- public Integer decode(ByteBuffer byteSlice) {
- return byteSlice.getInt();
- }
- };
+ @Override
+ public Integer fixedSize() {
+ return 4;
+ }
+
+ @Override
+ public Integer decode(ByteBuffer byteSlice) {
+ return byteSlice.getInt();
+ }
}
- /**
- * Creates a {@link Decoder} for the 64-bit {@code long} type.
- *
- * <p><strong>Note:</strong> It is often useful to apply {@link #withByteOrder(ByteOrder)} to the
- * result of this method.
- *
- * @return a new {@link Decoder}.
- */
- public static Decoder<Long> ofLong() {
- return new Decoder<>() {
- @Override
- public byte alignment() {
- return 8;
- }
+ private static class LongDecoder extends Decoder<Long> {
- @Override
- public Integer fixedSize() {
- return 8;
- }
+ @Override
+ public byte alignment() {
+ return 8;
+ }
- @Override
- public Long decode(ByteBuffer byteSlice) {
- return byteSlice.getLong();
- }
- };
+ @Override
+ public Integer fixedSize() {
+ return 8;
+ }
+
+ @Override
+ public Long decode(ByteBuffer byteSlice) {
+ return byteSlice.getLong();
+ }
}
- /**
- * Creates a {@link Decoder} for the {@code double} type.
- *
- * @return a new {@link Decoder}.
- */
- public static Decoder<Double> ofDouble() {
- return new Decoder<>() {
- @Override
- public byte alignment() {
- return 8;
- }
+ private static class DoubleDecoder extends Decoder<Double> {
- @Override
- public Integer fixedSize() {
- return 8;
- }
+ @Override
+ public byte alignment() {
+ return 8;
+ }
- @Override
- public Double decode(ByteBuffer byteSlice) {
- return byteSlice.getDouble();
- }
- };
+ @Override
+ public Integer fixedSize() {
+ return 8;
+ }
+
+ @Override
+ public Double decode(ByteBuffer byteSlice) {
+ return byteSlice.getDouble();
+ }
}
- /**
- * Creates a {@link Decoder} for the {@link String} type.
- *
- * <p><strong>Note:</strong> While GVariant does not prescribe any particular encoding, {@link
- * java.nio.charset.StandardCharsets#UTF_8} is the most common choice.
- *
- * @return a new {@link Decoder}.
- */
- public static Decoder<String> ofString(Charset charset) {
- return new Decoder<>() {
- @Override
- public byte alignment() {
- return 1;
- }
+ private static class StringDecoder extends Decoder<String> {
- @Override
- @Nullable
- Integer fixedSize() {
- return null;
- }
+ private final Charset charset;
- @Override
- public String decode(ByteBuffer byteSlice) {
- byteSlice.limit(byteSlice.limit() - 1);
- return charset.decode(byteSlice).toString();
- }
- };
+ public StringDecoder(Charset charset) {
+ this.charset = charset;
+ }
+
+ @Override
+ public byte alignment() {
+ return 1;
+ }
+
+ @Override
+ @Nullable
+ Integer fixedSize() {
+ return null;
+ }
+
+ @Override
+ public String decode(ByteBuffer byteSlice) {
+ byteSlice.limit(byteSlice.limit() - 1);
+ return charset.decode(byteSlice).toString();
+ }
}
}