diff options
Diffstat (limited to 'src/main')
-rw-r--r-- | src/main/java/eu/mulk/jgvariant/core/Decoder.java | 652 | ||||
-rw-r--r-- | src/main/java/eu/mulk/jgvariant/core/Signature.java | 116 | ||||
-rw-r--r-- | src/main/java/eu/mulk/jgvariant/core/Variant.java | 30 | ||||
-rw-r--r-- | src/main/java/eu/mulk/jgvariant/core/package-info.java | 27 | ||||
-rw-r--r-- | src/main/java/module-info.java | 63 |
5 files changed, 0 insertions, 888 deletions
diff --git a/src/main/java/eu/mulk/jgvariant/core/Decoder.java b/src/main/java/eu/mulk/jgvariant/core/Decoder.java deleted file mode 100644 index d2f2403..0000000 --- a/src/main/java/eu/mulk/jgvariant/core/Decoder.java +++ /dev/null @@ -1,652 +0,0 @@ -package eu.mulk.jgvariant.core; - -import static java.nio.ByteOrder.LITTLE_ENDIAN; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.RecordComponent; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.charset.Charset; -import java.text.ParseException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; -import org.apiguardian.api.API; -import org.apiguardian.api.API.Status; -import org.jetbrains.annotations.Nullable; - -/** - * Type class for decodable types. - * - * <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(String s, int i) {} - * - * var decoder = - * Decoder.ofArray( - * Decoder.ofStructure( - * ExampleRecord.class, - * Decoder.ofString(UTF_8), - * Decoder.ofInt().withByteOrder(LITTLE_ENDIAN))); - * - * byte[] bytes = ...; - * List<ExampleRecord> example = decoder.decode(ByteBuffer.wrap(bytes)); - * }</pre> - * - * @param <T> the type that the {@link Decoder} can decode. - */ -@SuppressWarnings("java:S1610") -@API(status = Status.EXPERIMENTAL) -public abstract class Decoder<T> { - - private Decoder() {} - - /** - * Decodes a {@link ByteBuffer} holding a serialized GVariant into a value of type {@code T}. - * - * <p><strong>Note:</strong> Due to the way the GVariant serialization format works, it is - * important that the start and end boundaries of the passed byte slice correspond to the actual - * start and end of the serialized value. The format does generally not allow for the dynamic - * discovery of the end of the data structure. - * - * @param byteSlice a byte slice holding a serialized GVariant. - * @return the deserialized value. - * @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); - - abstract byte alignment(); - - @Nullable - abstract Integer fixedSize(); - - final boolean hasFixedSize() { - 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; - - return new Decoder<>() { - @Override - public byte alignment() { - return delegate.alignment(); - } - - @Override - public @Nullable Integer fixedSize() { - return delegate.fixedSize(); - } - - @Override - public T decode(ByteBuffer byteSlice) { - byteSlice.order(byteOrder); - return delegate.decode(byteSlice); - } - }; - } - - /** - * Creates a {@link Decoder} for an {@code 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> Decoder<List<U>> ofArray(Decoder<U> elementDecoder) { - return new ArrayDecoder<>(elementDecoder); - } - - /** - * 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); - } - - /** - * Creates a {@link Decoder} for a {@code Structure} type, decoding into a {@link Record}. - * - * @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); - } - - /** - * Creates a {@link Decoder} for a {@code Structure} type, decoding into a {@link List}. - * - * <p>Prefer {@link #ofStructure(Class, Decoder[])} if possible, which is both more type-safe and - * more convenient. - * - * @param componentDecoders a {@link Decoder} for each component of the structure. - * @return a new {@link Decoder}. - */ - public static Decoder<Object[]> ofStructure(Decoder<?>... componentDecoders) { - return new TupleDecoder(componentDecoders); - } - - /** - * Creates a {@link Decoder} for the {@link Variant} type. - * - * <p>The contained {@link Object} 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 Optional} (a GVariant {@code Maybe} type) - * <li>{@link List} (a GVariant array) - * <li>{@code Object[]} (a GVariant structure) - * <li>{@link Variant} (a nested variant) - * </ul> - * - * @return a new {@link Decoder}. - */ - public static Decoder<Variant> ofVariant() { - return new VariantDecoder(); - } - - /** - * Creates a {@link Decoder} for the {@code boolean} type. - * - * @return a new {@link Decoder}. - */ - public static Decoder<Boolean> ofBoolean() { - return new BooleanDecoder(); - } - - /** - * Creates a {@link Decoder} for the 8-bit {@code 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. - * - * @param charset the {@link Charset} the string is encoded in. - * @return a new {@link Decoder}. - */ - public static Decoder<String> ofString(Charset charset) { - 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 Class<U> recordType; - private final TupleDecoder tupleDecoder; - - 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.recordType = recordType; - this.tupleDecoder = new TupleDecoder(componentDecoders); - } - - @Override - public byte alignment() { - return tupleDecoder.alignment(); - } - - @Override - public Integer fixedSize() { - return tupleDecoder.fixedSize(); - } - - @Override - public U decode(ByteBuffer byteSlice) { - Object[] recordConstructorArguments = tupleDecoder.decode(byteSlice); - - 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 TupleDecoder extends Decoder<Object[]> { - - private final Decoder<?>[] componentDecoders; - - TupleDecoder(Decoder<?>... componentDecoders) { - 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 Object[] decode(ByteBuffer byteSlice) { - int framingOffsetSize = byteCount(byteSlice.limit()); - - var objects = new Object[componentDecoders.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) { - objects[componentIndex] = - componentDecoder.decode(byteSlice.slice(position, fixedComponentSize)); - position += fixedComponentSize; - } else { - if (componentIndex == componentDecoders.length - 1) { - // The last component never has a framing offset. - int endPosition = byteSlice.limit() - framingOffsetIndex * framingOffsetSize; - objects[componentIndex] = - componentDecoder.decode(byteSlice.slice(position, endPosition - position)); - position = endPosition; - } else { - int framingOffset = - getIntN( - byteSlice.slice( - byteSlice.limit() - (1 + framingOffsetIndex) * framingOffsetSize, - framingOffsetSize)); - objects[componentIndex] = - componentDecoder.decode(byteSlice.slice(position, framingOffset - position)); - position = framingOffset; - ++framingOffsetIndex; - } - } - - ++componentIndex; - } - - return objects; - } - } - - 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) { - for (int i = byteSlice.limit() - 1; i >= 0; --i) { - if (byteSlice.get(i) != 0) { - continue; - } - - var dataBytes = byteSlice.slice(0, i); - var signatureBytes = byteSlice.slice(i + 1, byteSlice.limit() - (i + 1)); - - Signature signature; - try { - signature = Signature.parse(signatureBytes); - } catch (ParseException e) { - throw new IllegalArgumentException(e); - } - - return new Variant(signature, signature.decoder().decode(dataBytes)); - } - - throw new IllegalArgumentException("variant signature not found"); - } - } - - 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(); - } - } -} diff --git a/src/main/java/eu/mulk/jgvariant/core/Signature.java b/src/main/java/eu/mulk/jgvariant/core/Signature.java deleted file mode 100644 index d9de5f1..0000000 --- a/src/main/java/eu/mulk/jgvariant/core/Signature.java +++ /dev/null @@ -1,116 +0,0 @@ -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; -import org.apiguardian.api.API; -import org.apiguardian.api.API.Status; - -/** - * 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> - */ -@API(status = Status.STABLE) -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 deleted file mode 100644 index d1c1049..0000000 --- a/src/main/java/eu/mulk/jgvariant/core/Variant.java +++ /dev/null @@ -1,30 +0,0 @@ -package eu.mulk.jgvariant.core; - -import org.apiguardian.api.API; -import org.apiguardian.api.API.Status; - -/** - * 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>{@code Object[]} (a GVariant structure) - * <li>{@link Variant} (a nested variant) - * </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}, - * {@code Object[]}, {@link Variant}. - */ -@API(status = Status.EXPERIMENTAL) -public record Variant(Signature signature, Object 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 deleted file mode 100644 index 1754096..0000000 --- a/src/main/java/eu/mulk/jgvariant/core/package-info.java +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Provides {@link eu.mulk.jgvariant.core.Decoder}, the foundational class for <a - * href="https://docs.gtk.org/glib/struct.Variant.html">GVariant</a> parsing. - * - * <p>Instances of {@link eu.mulk.jgvariant.core.Decoder} read a given value type 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 - * java.lang.String} and {@code int}, you can use the following code: - * - * <pre>{@code - * record ExampleRecord(String s, int i) {} - * - * var decoder = - * Decoder.ofArray( - * Decoder.ofStructure( - * ExampleRecord.class, - * Decoder.ofString(UTF_8), - * Decoder.ofInt().withByteOrder(LITTLE_ENDIAN))); - * - * byte[] bytes = ...; - * List<ExampleRecord> example = decoder.decode(ByteBuffer.wrap(bytes)); - * }</pre> - */ -package eu.mulk.jgvariant.core; diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java deleted file mode 100644 index 0282ff8..0000000 --- a/src/main/java/module-info.java +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Provides a parser for the <a href="https://docs.gtk.org/glib/struct.Variant.html">GVariant</a> - * serialization format. - * - * <ul> - * <li><a href="#sect-overview">Overview</a> - * <li><a href="#sect-installation">Installation</a> - * </ul> - * - * <h2 id="sect-overview">Overview</h2> - * - * <p>The {@link eu.mulk.jgvariant.core} package contains the {@link eu.mulk.jgvariant.core.Decoder} - * type, which contains classes to parse and represent serialized <a - * href="https://docs.gtk.org/glib/struct.Variant.html">GVariant</a> values. - * - * <h2 id="sect-installation">Installation</h2> - * - * <ul> - * <li><a href="#sect-installation-maven">Usage with Maven</a> - * <li><a href="#sect-installation-gradle">Usage with Gradle</a> - * </ul> - * - * <h3 id="sect-installation-maven">Usage with Maven</h3> - * - * <pre>{@code - * <project> - * ... - * - * <dependencies> - * ... - * - * <dependency> - * <groupId>eu.mulk.jgvariant</groupId> - * <artifactId>jgvariant-core</artifactId> - * <version>0.1.3</version> - * </dependency> - * - * ... - * </dependencies> - * - * ... - * </project> - * }</pre> - * - * <h3 id="sect-installation-gradle">Usage with Gradle</h3> - * - * <pre>{@code - * dependencies { - * ... - * - * implementation("eu.mulk.jgvariant:jgvariant-core:0.1.3") - * - * ... - * } - * }</pre> - */ -module eu.mulk.jgvariant.core { - requires com.google.errorprone.annotations; - requires org.jetbrains.annotations; - requires org.apiguardian.api; - - exports eu.mulk.jgvariant.core; -} |