diff options
Diffstat (limited to 'src/main/java/eu')
| -rw-r--r-- | src/main/java/eu/mulk/jgvariant/core/Decoder.java | 104 | ||||
| -rw-r--r-- | src/main/java/eu/mulk/jgvariant/core/Value.java | 83 | ||||
| -rw-r--r-- | src/main/java/eu/mulk/jgvariant/core/package-info.java | 32 | 
3 files changed, 215 insertions, 4 deletions
diff --git a/src/main/java/eu/mulk/jgvariant/core/Decoder.java b/src/main/java/eu/mulk/jgvariant/core/Decoder.java index 2ebd0af..c51a723 100644 --- a/src/main/java/eu/mulk/jgvariant/core/Decoder.java +++ b/src/main/java/eu/mulk/jgvariant/core/Decoder.java @@ -30,6 +30,25 @@ import org.jetbrains.annotations.Nullable;   * <p>Use the {@code of*} family of constructor methods to acquire a suitable {@link Decoder} for   * the type you wish to decode.   * + * <p><strong>Example</strong> + * + * <p>To parse a GVariant of type {@code "a(si)"}, which is an array of pairs of {@link String} and + * {@code int}, you can use the following code: + * + * <pre>{@code + * record ExampleRecord(Value.Str s, Value.Int32 i) {} + * + * var decoder = + *   Decoder.ofArray( + *     Decoder.ofStructure( + *       ExampleRecord.class, + *       Decoder.ofStr(UTF_8), + *       Decoder.ofInt32().withByteOrder(LITTLE_ENDIAN))); + * + * byte[] bytes = ...; + * Value.Array<Value.Structure<ExampleRecord>> example = decoder.decode(ByteBuffer.wrap(bytes)); + * }</pre> + *   * @param <T> the type that the {@link Decoder} can decode.   */  @SuppressWarnings("java:S1610") @@ -52,6 +71,12 @@ public abstract class Decoder<T extends Value> {      return fixedSize() != null;    } +  /** +   * Switches the input {@link ByteBuffer} to a given {@link ByteOrder} before reading from it. +   * +   * @param byteOrder the byte order to use. +   * @return a new, decorated {@link Decoder}. +   */    public Decoder<T> withByteOrder(ByteOrder byteOrder) {      var delegate = this; @@ -74,6 +99,13 @@ public abstract class Decoder<T extends Value> {      };    } +  /** +   * Creates a {@link Decoder} for an {@link Array} type. +   * +   * @param elementDecoder a {@link Decoder} for the elements of the array. +   * @param <U> the element type. +   * @return a new {@link Decoder}. +   */    public static <U extends Value> Decoder<Array<U>> ofArray(Decoder<U> elementDecoder) {      return new Decoder<>() {        @Override @@ -138,6 +170,13 @@ public abstract class Decoder<T extends Value> {      return n < (1 << 8) ? 1 : n < (1 << 16) ? 2 : 4;    } +  /** +   * Creates a {@link Decoder} for a {@link Maybe} type. +   * +   * @param elementDecoder a {@link Decoder} for the contained element. +   * @param <U> the element type. +   * @return a new {@link Decoder}. +   */    public static <U extends Value> Decoder<Maybe<U>> ofMaybe(Decoder<U> elementDecoder) {      return new Decoder<>() {        @Override @@ -167,6 +206,14 @@ public abstract class Decoder<T extends Value> {      };    } +  /** +   * Creates a {@link Decoder} for a {@link 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}. +   */    @SafeVarargs    public static <U extends Record> Decoder<Structure<U>> ofStructure(        Class<U> recordType, Decoder<? extends Value>... componentDecoders) { @@ -260,6 +307,11 @@ public abstract class Decoder<T extends Value> {      };    } +  /** +   * Creates a {@link Decoder} for the {@link Variant} type. +   * +   * @return a new {@link Decoder}. +   */    public static Decoder<Variant> ofVariant() {      return new Decoder<>() {        @Override @@ -281,7 +333,12 @@ public abstract class Decoder<T extends Value> {      };    } -  public static Decoder<Bool> ofBoolean() { +  /** +   * Creates a {@link Decoder} for the {@link Bool} type. +   * +   * @return a new {@link Decoder}. +   */ +  public static Decoder<Bool> ofBool() {      return new Decoder<>() {        @Override        public byte alignment() { @@ -300,6 +357,14 @@ public abstract class Decoder<T extends Value> {      };    } +  /** +   * Creates a {@link Decoder} for the {@link Int8} 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<Int8> ofInt8() {      return new Decoder<>() {        @Override @@ -319,6 +384,14 @@ public abstract class Decoder<T extends Value> {      };    } +  /** +   * Creates a {@link Decoder} for the {@link Int16} 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<Int16> ofInt16() {      return new Decoder<>() {        @Override @@ -338,6 +411,14 @@ public abstract class Decoder<T extends Value> {      };    } +  /** +   * Creates a {@link Decoder} for the {@link Int32} 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<Int32> ofInt32() {      return new Decoder<>() {        @Override @@ -357,6 +438,14 @@ public abstract class Decoder<T extends Value> {      };    } +  /** +   * Creates a {@link Decoder} for the {@link Int64} 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<Int64> ofInt64() {      return new Decoder<>() {        @Override @@ -376,6 +465,11 @@ public abstract class Decoder<T extends Value> {      };    } +  /** +   * Creates a {@link Decoder} for the {@link Float64} type. +   * +   * @return a new {@link Decoder}. +   */    public static Decoder<Float64> ofFloat64() {      return new Decoder<>() {        @Override @@ -395,6 +489,14 @@ public abstract class Decoder<T extends Value> {      };    } +  /** +   * Creates a {@link Decoder} for the {@link Str} 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<Str> ofStr(Charset charset) {      return new Decoder<>() {        @Override diff --git a/src/main/java/eu/mulk/jgvariant/core/Value.java b/src/main/java/eu/mulk/jgvariant/core/Value.java index 969f240..54c0b79 100644 --- a/src/main/java/eu/mulk/jgvariant/core/Value.java +++ b/src/main/java/eu/mulk/jgvariant/core/Value.java @@ -3,33 +3,110 @@ package eu.mulk.jgvariant.core;  import java.util.List;  import java.util.Optional; -/** A value representable by the GVariant serialization format. */ +/** + * A value representable by the <a href="https://docs.gtk.org/glib/struct.Variant.html">GVariant</a> + * serialization format. + * + * <p>{@link Value} is a sum type (sealed interface) that represents a GVariant value. Its subtypes + * represent the different types of values that GVariant supports. + * + * @see Decoder + */  public sealed interface Value { -  // Composite types +  /** +   * A homogeneous sequence of GVariant values. +   * +   * <p>Arrays of fixed width (i.e. of values of fixed size) are represented in a similar way to +   * plain C arrays. Arrays of variable width require additional space for padding and framing. +   * +   * <p>Heterogeneous sequences are represented by {@code Array<Variant>}. +   * +   * @param <T> the type of the elements of the array. +   * @see Decoder#ofArray +   */    record Array<T extends Value>(List<T> values) implements Value {} +  /** +   * A value that is either present or absent. +   * +   * @param <T> the contained type. +   * @see Decoder#ofMaybe +   */    record Maybe<T extends Value>(Optional<T> value) implements Value {} +  /** +   * A tuple of values of fixed types. +   * +   * <p>GVariant structures are represented as {@link Record} types. For example, a two-element +   * structure consisting of a string and an int can be modelled as follows: +   * +   * <pre>{@code +   * record TestRecord(Str s, Int32 i) {} +   * var testStruct = new Structure<>(new TestRecord(new Str("hello"), new Int32(123)); +   * }</pre> +   * +   * @param <T> the {@link Record} type that represents the components of the structure. +   * @see Decoder#ofStructure +   */    record Structure<T extends Record>(T values) implements Value {} +  /** +   * A dynamically typed box that can hold a single value of any GVariant type. +   * +   * @see Decoder#ofVariant +   */    record Variant(Class<? extends Value> type, Value value) implements Value {} -  // Primitive types +  /** +   * Either true or false. +   * +   * @see Decoder#ofBool() +   */    record Bool(boolean value) implements Value {      static Bool TRUE = new Bool(true);      static Bool FALSE = new Bool(false);    } +  /** +   * A {@code byte}-sized integer. +   * +   * @see Decoder#ofInt8() +   */    record Int8(byte value) implements Value {} +  /** +   * A {@code short}-sized integer. +   * +   * @see Decoder#ofInt16() +   */    record Int16(short value) implements Value {} +  /** +   * An {@code int}-sized integer. +   * +   * @see Decoder#ofInt32() +   */    record Int32(int value) implements Value {} +  /** +   * A {@code long}-sized integer. +   * +   * @see Decoder#ofInt64() +   */    record Int64(long value) implements Value {} +  /** +   * A double-precision floating point number. +   * +   * @see Decoder#ofFloat64() +   */    record Float64(double value) implements Value {} +  /** +   * A character string. +   * +   * @see Decoder#ofStr +   */    record Str(String value) implements Value {}  } diff --git a/src/main/java/eu/mulk/jgvariant/core/package-info.java b/src/main/java/eu/mulk/jgvariant/core/package-info.java new file mode 100644 index 0000000..dba7a09 --- /dev/null +++ b/src/main/java/eu/mulk/jgvariant/core/package-info.java @@ -0,0 +1,32 @@ +/** + * Provides {@link eu.mulk.jgvariant.core.Value} and {@link eu.mulk.jgvariant.core.Decoder}, the + * foundational classes for <a href="https://docs.gtk.org/glib/struct.Variant.html">GVariant</a> + * parsing. + * + * <p>{@link eu.mulk.jgvariant.core.Value} is a sum type (sealed interface) that represents a + * GVariant value. Its subtypes represent the different types of values that GVariant supports. + * + * <p>Instances of {@link eu.mulk.jgvariant.core.Decoder} read a given concrete subtype of {@link + * eu.mulk.jgvariant.core.Value} from a {@link java.nio.ByteBuffer}. The class also contains factory + * methods to create those instances. + * + * <p><strong>Example</strong> + * + * <p>To parse a GVariant of type {@code "a(si)"}, which is an array of pairs of {@link String} and + * {@code int}, you can use the following code: + * + * <pre>{@code + * record ExampleRecord(Value.Str s, Value.Int32 i) {} + * + * var decoder = + *   Decoder.ofArray( + *     Decoder.ofStructure( + *       ExampleRecord.class, + *       Decoder.ofStr(UTF_8), + *       Decoder.ofInt32().withByteOrder(LITTLE_ENDIAN))); + * + * byte[] bytes = ...; + * Value.Array<Value.Structure<ExampleRecord>> example = decoder.decode(ByteBuffer.wrap(bytes)); + * }</pre> + */ +package eu.mulk.jgvariant.core;  | 
