diff options
Diffstat (limited to 'src/main/java')
| -rw-r--r-- | src/main/java/eu/mulk/jgvariant/core/Decoder.java | 58 | ||||
| -rw-r--r-- | src/main/java/eu/mulk/jgvariant/core/Signature.java | 113 | ||||
| -rw-r--r-- | src/main/java/eu/mulk/jgvariant/core/Variant.java | 25 | 
3 files changed, 154 insertions, 42 deletions
| diff --git a/src/main/java/eu/mulk/jgvariant/core/Decoder.java b/src/main/java/eu/mulk/jgvariant/core/Decoder.java index 9833998..389ae85 100644 --- a/src/main/java/eu/mulk/jgvariant/core/Decoder.java +++ b/src/main/java/eu/mulk/jgvariant/core/Decoder.java @@ -7,7 +7,7 @@ import java.lang.reflect.RecordComponent;  import java.nio.ByteBuffer;  import java.nio.ByteOrder;  import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; +import java.text.ParseException;  import java.util.ArrayList;  import java.util.Arrays;  import java.util.List; @@ -47,8 +47,11 @@ public abstract class Decoder<T> {    private Decoder() {}    /** +   * Decodes a {@link ByteBuffer} holding a serialized GVariant into a value of type {@code T}. +   *     * @throws java.nio.BufferUnderflowException if the byte buffer is shorter than the requested     *     data. +   * @throws IllegalArgumentException if the serialized GVariant is ill-formed     */    public abstract T decode(ByteBuffer byteSlice); @@ -156,7 +159,7 @@ public abstract class Decoder<T> {     *     * @return a new {@link Decoder}.     */ -  public static Decoder<Object> ofVariant() { +  public static Decoder<Variant> ofVariant() {      return new VariantDecoder();    } @@ -464,7 +467,7 @@ public abstract class Decoder<T> {      }    } -  private static class VariantDecoder extends Decoder<Object> { +  private static class VariantDecoder extends Decoder<Variant> {      @Override      public byte alignment() { @@ -478,55 +481,26 @@ public abstract class Decoder<T> {      }      @Override -    public Object decode(ByteBuffer byteSlice) { +    public Variant decode(ByteBuffer byteSlice) {        for (int i = byteSlice.limit() - 1; i >= 0; --i) {          if (byteSlice.get(i) != 0) {            continue;          } -        var data = byteSlice.slice(0, i); -        var signature = byteSlice.slice(i + 1, byteSlice.limit() - (i + 1)); - -        Decoder<?> decoder = parseSignature(signature); -        return decoder.decode(data); -      } +        var dataBytes = byteSlice.slice(0, i); +        var signatureBytes = byteSlice.slice(i + 1, byteSlice.limit() - (i + 1)); -      throw new IllegalArgumentException("variant signature not found"); -    } - -    private static Decoder<?> parseSignature(ByteBuffer signature) { -      char c = (char) signature.get(); -      return switch (c) { -        case 'b' -> Decoder.ofBoolean(); -        case 'y' -> Decoder.ofByte(); -        case 'n', 'q' -> Decoder.ofShort(); -        case 'i', 'u' -> Decoder.ofInt(); -        case 'x', 't' -> Decoder.ofLong(); -        case 'd' -> Decoder.ofDouble(); -        case 's', 'o', 'g' -> Decoder.ofString(StandardCharsets.UTF_8); -        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])); -        default -> throw new IllegalArgumentException( -            String.format("encountered unknown signature byte '%c'", c)); -      }; -    } - -    private static List<Decoder<?>> parseTupleTypes(ByteBuffer signature) { -      List<Decoder<?>> decoders = new ArrayList<>(); - -      while (true) { -        char c = (char) signature.get(signature.position()); -        if (c == ')' || c == '}') { -          signature.get(); -          break; +        Signature signature; +        try { +          signature = Signature.parse(signatureBytes); +        } catch (ParseException e) { +          throw new IllegalArgumentException(e);          } -        decoders.add(parseSignature(signature)); +        return new Variant(signature, signature.decoder().decode(dataBytes));        } -      return decoders; +      throw new IllegalArgumentException("variant signature not found");      }    } diff --git a/src/main/java/eu/mulk/jgvariant/core/Signature.java b/src/main/java/eu/mulk/jgvariant/core/Signature.java new file mode 100644 index 0000000..bb03b94 --- /dev/null +++ b/src/main/java/eu/mulk/jgvariant/core/Signature.java @@ -0,0 +1,113 @@ +package eu.mulk.jgvariant.core; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * A GVariant signature string. + * + * <p>Describes a type in the GVariant type system. The type can be arbitrarily complex. + * + * <p><strong>Examples</strong> + * + * <dl> + *   <dt>{@code "i"} + *   <dd>a single 32-bit integer + *   <dt>{@code "ai"} + *   <dd>an array of 32-bit integers + *   <dt>{@code "(bbb(sai))"} + *   <dd>a record consisting of three booleans and a nested record, which consists of a string and + *       an array of 32-bit integers + * </dl> + */ +public final class Signature { + +  private final String signatureString; +  private final Decoder<?> decoder; + +  Signature(ByteBuffer signatureBytes) throws ParseException { +    this.decoder = parseSignature(signatureBytes); + +    signatureBytes.rewind(); +    this.signatureString = StandardCharsets.US_ASCII.decode(signatureBytes).toString(); +  } + +  static Signature parse(ByteBuffer signatureBytes) throws ParseException { +    return new Signature(signatureBytes); +  } + +  static Signature parse(String signatureString) throws ParseException { +    var signatureBytes = ByteBuffer.wrap(signatureString.getBytes(StandardCharsets.US_ASCII)); +    return parse(signatureBytes); +  } + +  /** +   * Returns a {@link Decoder} that can decode values conforming to this signature. +   * +   * @return a {@link Decoder} for this signature +   */ +  @SuppressWarnings("unchecked") +  Decoder<Object> decoder() { +    return (Decoder<Object>) decoder; +  } + +  /** +   * Returns the signature formatted as a GVariant signature string. +   * +   * @return a GVariant signature string. +   */ +  @Override +  public String toString() { +    return signatureString; +  } + +  @Override +  public boolean equals(Object o) { +    return (o instanceof Signature signature) +        && Objects.equals(signatureString, signature.signatureString); +  } + +  @Override +  public int hashCode() { +    return Objects.hash(signatureString); +  } + +  private static Decoder<?> parseSignature(ByteBuffer signature) throws ParseException { +    char c = (char) signature.get(); +    return switch (c) { +      case 'b' -> Decoder.ofBoolean(); +      case 'y' -> Decoder.ofByte(); +      case 'n', 'q' -> Decoder.ofShort(); +      case 'i', 'u' -> Decoder.ofInt(); +      case 'x', 't' -> Decoder.ofLong(); +      case 'd' -> Decoder.ofDouble(); +      case 's', 'o', 'g' -> Decoder.ofString(StandardCharsets.UTF_8); +      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])); +      default -> throw new ParseException( +          String.format("encountered unknown signature byte '%c'", c), signature.position()); +    }; +  } + +  private static List<Decoder<?>> parseTupleTypes(ByteBuffer signature) throws ParseException { +    List<Decoder<?>> decoders = new ArrayList<>(); + +    while (true) { +      char c = (char) signature.get(signature.position()); +      if (c == ')' || c == '}') { +        signature.get(); +        break; +      } + +      decoders.add(parseSignature(signature)); +    } + +    return decoders; +  } +} diff --git a/src/main/java/eu/mulk/jgvariant/core/Variant.java b/src/main/java/eu/mulk/jgvariant/core/Variant.java new file mode 100644 index 0000000..e2b4b68 --- /dev/null +++ b/src/main/java/eu/mulk/jgvariant/core/Variant.java @@ -0,0 +1,25 @@ +package eu.mulk.jgvariant.core; + +/** + * A dynamically typed GVariant value carrying a {@link Signature} describing its type. + * + * <p>{@link #value()} can be of one of the following types: + * + * <ul> + *   <li>{@link Boolean} + *   <li>{@link Byte} + *   <li>{@link Short} + *   <li>{@link Integer} + *   <li>{@link Long} + *   <li>{@link String} + *   <li>{@link java.util.Optional} (a GVariant {@code Maybe} type) + *   <li>{@link java.util.List} (a GVariant array) + *   <li>{@link Object[]} (a GVariant structure) + * </ul> + * + * @param signature the signature describing the type of the value. + * @param value the value itself; one of {@link Boolean}, {@link Byte}, {@link Short}, {@link + *     Integer}, {@link Long}, {@link String}, {@link java.util.Optional}, {@link java.util.List}, + *     {@link Object[]}. + */ +public record Variant(Signature signature, Object value) {} | 
