From 34430180f83a9e52ead4f919731f955bbc3b3b79 Mon Sep 17 00:00:00 2001
From: Matthias Andreas Benkard <code@mail.matthias.benkard.de>
Date: Tue, 14 Dec 2021 20:00:36 +0100
Subject: Decoder: Make anonymous classes into named inner classes.

Change-Id: I3150b262512cafe42f139d56c9ecee165da4e1df
---
 src/main/java/eu/mulk/jgvariant/core/Decoder.java | 663 ++++++++++++----------
 1 file changed, 362 insertions(+), 301 deletions(-)

(limited to 'src/main/java/eu')

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,67 +96,7 @@ public abstract class Decoder<T> {
    * @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();
-      }
-
-      @Override
-      @Nullable
-      Integer fixedSize() {
-        return null;
-      }
-
-      @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);
-          }
-        } 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;
-
-          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());
-          }
-        }
-
-        return elements;
-      }
-    };
-  }
-
-  private static int align(int offset, byte alignment) {
-    return offset % alignment == 0 ? offset : offset + alignment - (offset % alignment);
-  }
-
-  private static int getIntN(ByteBuffer byteSlice) {
-    var intBytes = new byte[4];
-    byteSlice.get(intBytes, 0, Math.min(4, byteSlice.limit()));
-    return ByteBuffer.wrap(intBytes).order(LITTLE_ENDIAN).getInt();
-  }
-
-  @SuppressWarnings("java:S3358")
-  private static int byteCount(int n) {
-    return n < (1 << 8) ? 1 : n < (1 << 16) ? 2 : 4;
+    return new ArrayDecoder<>(elementDecoder);
   }
 
   /**
@@ -167,32 +107,7 @@ public abstract class Decoder<T> {
    * @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();
-      }
-
-      @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));
-        }
-      }
-    };
+    return new MaybeDecoder<>(elementDecoder);
   }
 
   /**
@@ -205,94 +120,7 @@ public abstract class Decoder<T> {
    */
   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));
-    }
-
-    return new Decoder<>() {
-      @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, 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;
-            }
-          }
-
-          ++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);
-        }
-      }
-    };
+    return new StructureDecoder<>(recordType, componentDecoders);
   }
 
   /**
@@ -301,24 +129,7 @@ public abstract class Decoder<T> {
    * @return a new {@link Decoder}.
    */
   public static Decoder<Variant> ofVariant() {
-    return new Decoder<>() {
-      @Override
-      public byte alignment() {
-        return 8;
-      }
-
-      @Override
-      @Nullable
-      Integer fixedSize() {
-        return null;
-      }
-
-      @Override
-      public Variant decode(ByteBuffer byteSlice) {
-        // TODO
-        throw new UnsupportedOperationException("not implemented");
-      }
-    };
+    return new VariantDecoder();
   }
 
   /**
@@ -327,22 +138,7 @@ public abstract class Decoder<T> {
    * @return a new {@link Decoder}.
    */
   public static Decoder<Boolean> ofBoolean() {
-    return new Decoder<>() {
-      @Override
-      public byte alignment() {
-        return 1;
-      }
-
-      @Override
-      public Integer fixedSize() {
-        return 1;
-      }
-
-      @Override
-      public Boolean decode(ByteBuffer byteSlice) {
-        return byteSlice.get() != 0;
-      }
-    };
+    return new BooleanDecoder();
   }
 
   /**
@@ -354,22 +150,7 @@ public abstract class Decoder<T> {
    * @return a new {@link Decoder}.
    */
   public static Decoder<Byte> ofByte() {
-    return new Decoder<>() {
-      @Override
-      public byte alignment() {
-        return 1;
-      }
-
-      @Override
-      public Integer fixedSize() {
-        return 1;
-      }
-
-      @Override
-      public Byte decode(ByteBuffer byteSlice) {
-        return byteSlice.get();
-      }
-    };
+    return new ByteDecoder();
   }
 
   /**
@@ -381,22 +162,7 @@ public abstract class Decoder<T> {
    * @return a new {@link Decoder}.
    */
   public static Decoder<Short> ofShort() {
-    return new Decoder<>() {
-      @Override
-      public byte alignment() {
-        return 2;
-      }
-
-      @Override
-      public Integer fixedSize() {
-        return 2;
-      }
-
-      @Override
-      public Short decode(ByteBuffer byteSlice) {
-        return byteSlice.getShort();
-      }
-    };
+    return new ShortDecoder();
   }
 
   /**
@@ -408,22 +174,7 @@ public abstract class Decoder<T> {
    * @return a new {@link Decoder}.
    */
   public static Decoder<Integer> ofInt() {
-    return new Decoder<>() {
-      @Override
-      public byte alignment() {
-        return 4;
-      }
-
-      @Override
-      public Integer fixedSize() {
-        return 4;
-      }
-
-      @Override
-      public Integer decode(ByteBuffer byteSlice) {
-        return byteSlice.getInt();
-      }
-    };
+    return new IntegerDecoder();
   }
 
   /**
@@ -435,22 +186,7 @@ public abstract class Decoder<T> {
    * @return a new {@link Decoder}.
    */
   public static Decoder<Long> ofLong() {
-    return new Decoder<>() {
-      @Override
-      public byte alignment() {
-        return 8;
-      }
-
-      @Override
-      public Integer fixedSize() {
-        return 8;
-      }
-
-      @Override
-      public Long decode(ByteBuffer byteSlice) {
-        return byteSlice.getLong();
-      }
-    };
+    return new LongDecoder();
   }
 
   /**
@@ -459,22 +195,7 @@ public abstract class Decoder<T> {
    * @return a new {@link Decoder}.
    */
   public static Decoder<Double> ofDouble() {
-    return new Decoder<>() {
-      @Override
-      public byte alignment() {
-        return 8;
-      }
-
-      @Override
-      public Integer fixedSize() {
-        return 8;
-      }
-
-      @Override
-      public Double decode(ByteBuffer byteSlice) {
-        return byteSlice.getDouble();
-      }
-    };
+    return new DoubleDecoder();
   }
 
   /**
@@ -486,23 +207,363 @@ public abstract class Decoder<T> {
    * @return a new {@link Decoder}.
    */
   public static Decoder<String> ofString(Charset charset) {
-    return new Decoder<>() {
-      @Override
-      public byte alignment() {
+    return new StringDecoder(charset);
+  }
+
+  private static int align(int offset, byte alignment) {
+    return offset % alignment == 0 ? offset : offset + alignment - (offset % alignment);
+  }
+
+  private static int getIntN(ByteBuffer byteSlice) {
+    var intBytes = new byte[4];
+    byteSlice.get(intBytes, 0, Math.min(4, byteSlice.limit()));
+    return ByteBuffer.wrap(intBytes).order(LITTLE_ENDIAN).getInt();
+  }
+
+  @SuppressWarnings("java:S3358")
+  private static int byteCount(int n) {
+    return n < (1 << 8) ? 1 : n < (1 << 16) ? 2 : 4;
+  }
+
+  private static class ArrayDecoder<U> extends Decoder<List<U>> {
+
+    private final Decoder<U> elementDecoder;
+
+    ArrayDecoder(Decoder<U> elementDecoder) {
+      this.elementDecoder = elementDecoder;
+    }
+
+    @Override
+    public byte alignment() {
+      return elementDecoder.alignment();
+    }
+
+    @Override
+    @Nullable
+    Integer fixedSize() {
+      return null;
+    }
+
+    @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);
+        }
+      } 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;
+
+        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());
+        }
+      }
+
+      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;
       }
 
-      @Override
-      @Nullable
-      Integer fixedSize() {
-        return null;
+      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, 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;
+          }
+        }
+
+        ++componentIndex;
       }
 
-      @Override
-      public String decode(ByteBuffer byteSlice) {
-        byteSlice.limit(byteSlice.limit() - 1);
-        return charset.decode(byteSlice).toString();
+      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);
       }
-    };
+    }
+  }
+
+  private static class VariantDecoder extends Decoder<Variant> {
+
+    @Override
+    public byte alignment() {
+      return 8;
+    }
+
+    @Override
+    @Nullable
+    Integer fixedSize() {
+      return null;
+    }
+
+    @Override
+    public Variant decode(ByteBuffer byteSlice) {
+      // TODO
+      throw new UnsupportedOperationException("not implemented");
+    }
+  }
+
+  private static class BooleanDecoder extends Decoder<Boolean> {
+
+    @Override
+    public byte alignment() {
+      return 1;
+    }
+
+    @Override
+    public Integer fixedSize() {
+      return 1;
+    }
+
+    @Override
+    public Boolean decode(ByteBuffer byteSlice) {
+      return byteSlice.get() != 0;
+    }
+  }
+
+  private static class ByteDecoder extends Decoder<Byte> {
+
+    @Override
+    public byte alignment() {
+      return 1;
+    }
+
+    @Override
+    public Integer fixedSize() {
+      return 1;
+    }
+
+    @Override
+    public Byte decode(ByteBuffer byteSlice) {
+      return byteSlice.get();
+    }
+  }
+
+  private static class ShortDecoder extends Decoder<Short> {
+
+    @Override
+    public byte alignment() {
+      return 2;
+    }
+
+    @Override
+    public Integer fixedSize() {
+      return 2;
+    }
+
+    @Override
+    public Short decode(ByteBuffer byteSlice) {
+      return byteSlice.getShort();
+    }
+  }
+
+  private static class IntegerDecoder extends Decoder<Integer> {
+
+    @Override
+    public byte alignment() {
+      return 4;
+    }
+
+    @Override
+    public Integer fixedSize() {
+      return 4;
+    }
+
+    @Override
+    public Integer decode(ByteBuffer byteSlice) {
+      return byteSlice.getInt();
+    }
+  }
+
+  private static class LongDecoder extends Decoder<Long> {
+
+    @Override
+    public byte alignment() {
+      return 8;
+    }
+
+    @Override
+    public Integer fixedSize() {
+      return 8;
+    }
+
+    @Override
+    public Long decode(ByteBuffer byteSlice) {
+      return byteSlice.getLong();
+    }
+  }
+
+  private static class DoubleDecoder extends Decoder<Double> {
+
+    @Override
+    public byte alignment() {
+      return 8;
+    }
+
+    @Override
+    public Integer fixedSize() {
+      return 8;
+    }
+
+    @Override
+    public Double decode(ByteBuffer byteSlice) {
+      return byteSlice.getDouble();
+    }
+  }
+
+  private static class StringDecoder extends Decoder<String> {
+
+    private final Charset charset;
+
+    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();
+    }
   }
 }
-- 
cgit v1.2.3