mirror of
https://github.com/apple/pkl.git
synced 2026-05-25 08:09:17 +02:00
pkl-config-java: Refine nullness handling in Config and JavaType (#1544)
Motivation:
Config.as() causes nullness warnings when its result is intentionally assigned
to a non-null variable
Changes:
* Introduce Config.asNullable(Class<T>), asNullable(JavaType<T>), and
asNullable(Type) to explicitly opt into nullable values
* Keep the signatures of Config.as(Class<T>) and Config.as(JavaType<T>)
unchanged from 0.31 by adding @NullUnmarked
* This gives users time to migrate from as() to asNullable() where appropriate
* Avoids introducing new spurious warnings
* Change `<T> T Config.as(Type)` to `<T extends @nullable Object> T Config.as(Type)`
* This overload is typically used by reflective code such as
pkl-config-kotlin's Config.to() rather than directly by user code
* Clarify that JavaType<T> represents a non-null top-level type whose type arguments may be nullable
* Restricting <T> to non-null keeps method signatures understandable for humans and tools
* Enables full symmetry between Class<T> and JavaType<T> overloads in Config and JavaType
* Enables future non-null runtime checks in both Config.as() overloads
* Simplify construction of `JavaType`s with nullable type arguments
* Add ofNullable() variants for most factory methods, e.g., JavaType.listOfNullable()
* Overhaul Javadoc of Config and JavaType
Result:
* Clear separation between accessing nullable and non-null values
* Config.as() is used for the common non-null case
* Config.as() can perform non-null runtime checks in a future release (breaking change)
* More ergonomic construction of types with nullable type arguments
* More detailed and consistent documentation
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -17,6 +17,7 @@ package org.pkl.config.java;
|
|||||||
|
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
import org.pkl.config.java.mapper.ValueMapper;
|
import org.pkl.config.java.mapper.ValueMapper;
|
||||||
import org.pkl.core.Composite;
|
import org.pkl.core.Composite;
|
||||||
|
|
||||||
@@ -49,17 +50,37 @@ abstract class AbstractConfig implements Config {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T as(Class<T> type) {
|
public <T> T as(Class<T> type) {
|
||||||
return as((Type) type);
|
return trustNonNull(asNullable(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> T as(JavaType<T> type) {
|
||||||
|
return trustNonNull(asNullable(type));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T as(Type type) {
|
public <T> T as(Type type) {
|
||||||
|
return trustNonNull(asNullable(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> @Nullable T asNullable(Class<T> type) {
|
||||||
return mapper.map(getRawValue(), type);
|
return mapper.map(getRawValue(), type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T as(JavaType<T> javaType) {
|
public <T> @Nullable T asNullable(JavaType<T> type) {
|
||||||
return as(javaType.getType());
|
return mapper.map(getRawValue(), type.getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T extends @Nullable Object> T asNullable(Type type) {
|
||||||
|
return mapper.map(getRawValue(), type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({"unchecked", "DataFlowIssue", "NullAway"})
|
||||||
|
private static <T> T trustNonNull(@Nullable Object value) {
|
||||||
|
return (T) value;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract Object getRawChildValue(String property);
|
protected abstract Object getRawChildValue(String property);
|
||||||
|
|||||||
@@ -17,60 +17,130 @@ package org.pkl.config.java;
|
|||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
|
import org.jspecify.annotations.NullUnmarked;
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
import org.pkl.config.java.mapper.ConversionException;
|
import org.pkl.config.java.mapper.ConversionException;
|
||||||
import org.pkl.config.java.mapper.ValueMapper;
|
import org.pkl.config.java.mapper.ValueMapper;
|
||||||
import org.pkl.core.Evaluator;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A root, intermediate, or leaf node in a configuration tree. Child nodes can be obtained by name
|
* A root, intermediate, or leaf node in a configuration tree.
|
||||||
* using {@link #get(String)}. To consume the node's composite or scalar value, convert the value to
|
*
|
||||||
* the desired Java type, using one of the provided {@link #as} methods.
|
* <p>To navigate to a child node, use {@link #get(String)} with the child's unqualified name.
|
||||||
|
*
|
||||||
|
* <p>To retrieve this node's value, use:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link #as(Class)} for non-null types.
|
||||||
|
* <li>{@link #asNullable(Class)} for nullable types.
|
||||||
|
* <li>{@link #as(JavaType)} for parameterized types, such as {@code List<@Nullable String>}.
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>Whether a method can return null depends on the method and target type used. For example,
|
||||||
|
* {@code asNullable(String.class)} can return {@code null}, while {@code
|
||||||
|
* as(JavaType.listOfNullable(String.class))} can return a {@code List<String>} with nullable
|
||||||
|
* elements. These nullness rules are for static analysis tools such as IntelliJ IDEA and NullAway
|
||||||
|
* and are not enforced at runtime.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings({"DeprecatedIsStillUsed"})
|
@SuppressWarnings({"DeprecatedIsStillUsed"})
|
||||||
public interface Config {
|
public interface Config {
|
||||||
/**
|
/**
|
||||||
* The dot-separated name of this node. For example, the node reached using {@code
|
* Returns the qualified name of this node, or the empty string if this is the root node.
|
||||||
* rootNode.get("foo").get("bar")} has qualified name {@code foo.bar}. Returns the empty String
|
*
|
||||||
* for the root node itself.
|
* <p>The qualified name is formed by joining child names using {@code '.'}. For example, {@code
|
||||||
|
* rootNode.get("foo").get("bar").getQualifiedName()} returns {@code "foo.bar"}.
|
||||||
*/
|
*/
|
||||||
String getQualifiedName();
|
String getQualifiedName();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The raw value of this node, as provided by {@link Evaluator}. Typically, a node's value is not
|
* Returns the underlying value of this node.
|
||||||
* consumed directly, but converted to the desired Java type using {@link #as}.
|
*
|
||||||
|
* <p>This value is typically accessed indirectly via {@link #as(Class)}, {@link
|
||||||
|
* #asNullable(Class)}, or {@link #as(JavaType)}.
|
||||||
*/
|
*/
|
||||||
Object getRawValue();
|
Object getRawValue();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the child node with the given unqualified name.
|
* Returns the child node with the given unqualified name.
|
||||||
*
|
*
|
||||||
|
* <p>For example, {@code get("foo").get("bar")} returns the child named {@code "bar"} of the
|
||||||
|
* child named {@code "foo"}. Passing a qualified name to this method, as in {@code
|
||||||
|
* get("foo.bar")}, is not supported.
|
||||||
|
*
|
||||||
* @throws NoSuchChildException if a child with the given name does not exist
|
* @throws NoSuchChildException if a child with the given name does not exist
|
||||||
*/
|
*/
|
||||||
Config get(String childName);
|
Config get(String childName);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts this node's value to the given {@link Class}.
|
* Returns this node's value as a non-null value of the given {@link Class}.
|
||||||
|
*
|
||||||
|
* <p>If this node's value may be {@code null}, use {@link #asNullable(Class)} instead. In a
|
||||||
|
* future version, this method will perform a non-null check.
|
||||||
*
|
*
|
||||||
* @throws ConversionException if the value cannot be converted to the given type
|
* @throws ConversionException if the value cannot be converted to the given type
|
||||||
*/
|
*/
|
||||||
<T extends @Nullable Object> T as(Class<T> type);
|
@NullUnmarked
|
||||||
|
<T> T as(Class<T> type);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts this node's value to the given {@link Type}.
|
* Returns this node's value as a non-null value of the given {@link JavaType}.
|
||||||
*
|
*
|
||||||
* <p>Note that usages of this method are not type safe.
|
* <p>If this node's value may be {@code null}, use {@link #asNullable(JavaType)} instead. In a
|
||||||
|
* future version, this method will perform a non-null check.
|
||||||
|
*
|
||||||
|
* <p>Use this method when converting to a parameterized type, such as {@code List<@Nullable
|
||||||
|
* String>}.
|
||||||
*
|
*
|
||||||
* @throws ConversionException if the value cannot be converted to the given type
|
* @throws ConversionException if the value cannot be converted to the given type
|
||||||
*/
|
*/
|
||||||
<T extends @Nullable Object> T as(Type type);
|
@NullUnmarked
|
||||||
|
<T> T as(JavaType<T> type);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts this node's value to the given {@link JavaType}.
|
* Returns this node's value as a non-null value of the given {@link Type}.
|
||||||
|
*
|
||||||
|
* <p>If this node's value may be {@code null}, use {@link #asNullable(Type)} instead. In a future
|
||||||
|
* version, this method will perform a non-null check.
|
||||||
|
*
|
||||||
|
* <p>Use this method when the target type is already available as a {@link Type}; otherwise,
|
||||||
|
* prefer {@link #as(Class)} or {@link #as(JavaType)}.
|
||||||
*
|
*
|
||||||
* @throws ConversionException if the value cannot be converted to the given type
|
* @throws ConversionException if the value cannot be converted to the given type
|
||||||
*/
|
*/
|
||||||
<T extends @Nullable Object> T as(JavaType<T> type);
|
@NullUnmarked
|
||||||
|
<T> T as(Type type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns this node's value as a nullable value of the given {@link Class}.
|
||||||
|
*
|
||||||
|
* <p>If this node's value cannot be {@code null}, use {@link #as(Class)} instead.
|
||||||
|
*
|
||||||
|
* @throws ConversionException if the value cannot be converted to the given type
|
||||||
|
*/
|
||||||
|
<T> @Nullable T asNullable(Class<T> type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns this node's value as a nullable value of the given {@link Class}.
|
||||||
|
*
|
||||||
|
* <p>If this node's value cannot be {@code null}, use {@link #as(JavaType)} instead.
|
||||||
|
*
|
||||||
|
* <p>Use this method when converting to a parameterized type, such as {@code List<@Nullable
|
||||||
|
* String>}.
|
||||||
|
*
|
||||||
|
* @throws ConversionException if the value cannot be converted to the given type
|
||||||
|
*/
|
||||||
|
<T> @Nullable T asNullable(JavaType<T> type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns this node's value as a nullable value of the given {@link Type}.
|
||||||
|
*
|
||||||
|
* <p>If this node's value cannot be {@code null}, use {@link #as(Type)} instead.
|
||||||
|
*
|
||||||
|
* <p>Use this method when the target type is already available as a {@link Type}; otherwise,
|
||||||
|
* prefer {@link #asNullable(Class)} or {@link #as(JavaType)}.
|
||||||
|
*
|
||||||
|
* @throws ConversionException if the value cannot be converted to the given type
|
||||||
|
*/
|
||||||
|
<T extends @Nullable Object> T asNullable(Type type);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decodes a config from the supplied byte array.
|
* Decodes a config from the supplied byte array.
|
||||||
|
|||||||
@@ -23,30 +23,77 @@ import org.pkl.config.java.mapper.Types;
|
|||||||
import org.pkl.core.Pair;
|
import org.pkl.core.Pair;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runtime representation of a possibly parameterized Java type. Factory methods are provided to
|
* Represents a Java type, including fully parameterized types.
|
||||||
* ease construction of commonly used Java standard library types. For example, a {@code JavaType}
|
|
||||||
* for {@code List<String>} can be constructed using {@code JavaType.listOf(String.class)}.
|
|
||||||
*
|
*
|
||||||
* <p>Parameterizations of other types can be constructed using the <em>super type token</em> idiom:
|
* <p>This class captures complete type information that cannot be expressed with {@link Class}, for
|
||||||
|
* example {@code List<String>} or {@code Result<String, @Nullable Integer>}. It is often used with
|
||||||
|
* {@link Config#as(JavaType)}.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>A {@code JavaType} always represents a non-null top-level type, but its type arguments may be
|
||||||
|
* nullable. For example, {@code listOfNullable(String.class)} represents {@code List<@Nullable
|
||||||
|
* String>}.
|
||||||
|
*
|
||||||
|
* <p>To construct a non-parameterized type, use {@link #of(Class)}.
|
||||||
|
*
|
||||||
|
* <p>To construct common parameterized types, use one of:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link #optionalOf(Class)}
|
||||||
|
* <li>{@link #arrayOf(Class)}
|
||||||
|
* <li>{@link #collectionOf(Class)}
|
||||||
|
* <li>{@link #iterableOf(Class)}
|
||||||
|
* <li>{@link #listOf(Class)}
|
||||||
|
* <li>{@link #setOf(Class)}
|
||||||
|
* <li>{@link #mapOf(Class, Class)}
|
||||||
|
* <li>{@link #pairOf(Class, Class)}
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>Apart from {@code optionalOf()}, the above methods have nullable variants:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link #arrayOfNullable(Class)}
|
||||||
|
* <li>{@link #collectionOfNullable(Class)}
|
||||||
|
* <li>{@link #iterableOfNullable(Class)}
|
||||||
|
* <li>{@link #listOfNullable(Class)}
|
||||||
|
* <li>{@link #setOfNullable(Class)}
|
||||||
|
* <li>{@link #mapOfNullableKeys(Class, Class)}
|
||||||
|
* <li>{@link #mapOfNullableValues(Class, Class)}
|
||||||
|
* <li>{@link #mapOfNullableKeysAndValues(Class, Class)}
|
||||||
|
* <li>{@link #pairOfNullableFirst(Class, Class)}
|
||||||
|
* <li>{@link #pairOfNullableSecond(Class, Class)}
|
||||||
|
* <li>{@link #pairOfNullableFirstAndSecond(Class, Class)}
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>These methods can be nested. For example, {@code optionalOf(listOfNullable(String.class))}
|
||||||
|
* represents {@code Optional<List<@Nullable String>>}.
|
||||||
|
*
|
||||||
|
* <p>To construct arbitrary parameterized types, use the <em>super-type token</em> idiom:
|
||||||
*
|
*
|
||||||
* <pre>{@code
|
* <pre>{@code
|
||||||
* class Mapping<T> {} // some user-defined type
|
* class Result<T, U> {} // library or user-defined type
|
||||||
|
*
|
||||||
* Config config = ...
|
* Config config = ...
|
||||||
*
|
*
|
||||||
* Mapping<String> container = config.as(
|
* Result<String, @Nullable Integer> result =
|
||||||
* // construct super type token for Mapping<String>
|
* config.as(new JavaType<Result<String, @Nullable Integer>>() {});
|
||||||
* new JavaType<Mapping<String>>() {}
|
|
||||||
* );
|
|
||||||
* }</pre>
|
* }</pre>
|
||||||
*
|
*
|
||||||
* @param <T> the type reified by this {@code JavaType} instance
|
* @param <T> the non-null type represented by this {@code JavaType}
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unused")
|
|
||||||
public class JavaType<T> {
|
public class JavaType<T> {
|
||||||
private final Type type;
|
private final Type type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a {@code JavaType} using the super-type token idiom.
|
||||||
|
*
|
||||||
|
* <p>Subclasses must be parameterized with the desired type, for example:
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* new JavaType<List<@Nullable String>>() {}
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* @throws IllegalStateException if this instance is not parameterized
|
||||||
|
*/
|
||||||
protected JavaType() {
|
protected JavaType() {
|
||||||
var superclass = getClass().getGenericSuperclass();
|
var superclass = getClass().getGenericSuperclass();
|
||||||
if (!(superclass instanceof ParameterizedType parameterizedType)) {
|
if (!(superclass instanceof ParameterizedType parameterizedType)) {
|
||||||
@@ -59,100 +106,382 @@ public class JavaType<T> {
|
|||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates a {@code JavaType} for the given {@code Class}. */
|
/** Creates a {@code JavaType} for the given type. */
|
||||||
public static <T> JavaType<T> of(Class<T> type) {
|
public static <T> JavaType<T> of(Class<T> type) {
|
||||||
return new JavaType<>(type);
|
return new JavaType<>(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@code JavaType} for the given {@code Type}.
|
* Creates a {@code JavaType} for the given {@link Type}.
|
||||||
*
|
*
|
||||||
* <p>Note: This method is not type safe, and should be used with care.
|
* <p>Use this method when the target type is already available as a {@link Type}; otherwise,
|
||||||
|
* prefer {@link #of(Class)}.
|
||||||
*/
|
*/
|
||||||
public static <T> JavaType<T> of(Type type) {
|
public static <T> JavaType<T> of(Type type) {
|
||||||
return new JavaType<>(type);
|
return new JavaType<>(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates an {@link Optional} type with the given element type. */
|
/**
|
||||||
|
* Creates an {@link Optional} type with the given non-null element type.
|
||||||
|
*
|
||||||
|
* <p>For a parameterized element type, use {@link #optionalOf(JavaType)}.
|
||||||
|
*/
|
||||||
public static <E> JavaType<Optional<E>> optionalOf(Class<E> elementType) {
|
public static <E> JavaType<Optional<E>> optionalOf(Class<E> elementType) {
|
||||||
return JavaType.of(Types.optionalOf(elementType));
|
return JavaType.of(Types.optionalOf(elementType));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates an {@link Optional} type with the given element type. */
|
/** Creates an {@link Optional} type with the given non-null element type. */
|
||||||
public static <E> JavaType<Optional<E>> optionalOf(JavaType<E> elementType) {
|
public static <E> JavaType<Optional<E>> optionalOf(JavaType<E> elementType) {
|
||||||
return JavaType.of(Types.optionalOf(elementType.type));
|
return JavaType.of(Types.optionalOf(elementType.type));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates a {@link Pair} type with the given first and second element types. */
|
/**
|
||||||
|
* Creates a {@link Pair} type with the given non-null first and non-null second element types.
|
||||||
|
*
|
||||||
|
* <p>For nullable first and/or second element types, use one of the {@code pairOfNullable*}
|
||||||
|
* methods.
|
||||||
|
*
|
||||||
|
* <p>For parameterized element types, use {@link #pairOf(JavaType, JavaType)}.
|
||||||
|
*/
|
||||||
public static <F, S> JavaType<Pair<F, S>> pairOf(Class<F> firstType, Class<S> secondType) {
|
public static <F, S> JavaType<Pair<F, S>> pairOf(Class<F> firstType, Class<S> secondType) {
|
||||||
return JavaType.of(Types.pairOf(firstType, secondType));
|
return JavaType.of(Types.pairOf(firstType, secondType));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates a {@link Pair} type with the given first and second element types. */
|
/**
|
||||||
|
* Creates a {@link Pair} type with the given non-null first and non-null second element types.
|
||||||
|
*
|
||||||
|
* <p>For nullable first and/or second element types, use one of the {@code pairOfNullable*}
|
||||||
|
* methods.
|
||||||
|
*/
|
||||||
public static <F, S> JavaType<Pair<F, S>> pairOf(JavaType<F> firstType, JavaType<S> secondType) {
|
public static <F, S> JavaType<Pair<F, S>> pairOf(JavaType<F> firstType, JavaType<S> secondType) {
|
||||||
return JavaType.of(Types.pairOf(firstType.type, secondType.type));
|
return JavaType.of(Types.pairOf(firstType.type, secondType.type));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates an array type with the given element type. */
|
/**
|
||||||
|
* Creates a {@link Pair} type with the given nullable first and non-null second element types.
|
||||||
|
*
|
||||||
|
* <p>For parameterized element types, use {@link #pairOfNullableFirst(JavaType, JavaType)}.
|
||||||
|
*/
|
||||||
|
public static <F, S> JavaType<Pair<@Nullable F, S>> pairOfNullableFirst(
|
||||||
|
Class<F> firstType, Class<S> secondType) {
|
||||||
|
return JavaType.of(Types.pairOf(firstType, secondType));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link Pair} type with the given nullable first and non-null second element types.
|
||||||
|
*/
|
||||||
|
public static <F, S> JavaType<Pair<@Nullable F, S>> pairOfNullableFirst(
|
||||||
|
JavaType<F> firstType, JavaType<S> secondType) {
|
||||||
|
return JavaType.of(Types.pairOf(firstType.type, secondType.type));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link Pair} type with the given non-null first and nullable second element types.
|
||||||
|
*
|
||||||
|
* <p>For parameterized element types, use {@link #pairOfNullableSecond(JavaType, JavaType)}.
|
||||||
|
*/
|
||||||
|
public static <F, S> JavaType<Pair<F, @Nullable S>> pairOfNullableSecond(
|
||||||
|
Class<F> firstType, Class<S> secondType) {
|
||||||
|
return JavaType.of(Types.pairOf(firstType, secondType));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link Pair} type with the given non-null first and nullable second element types.
|
||||||
|
*/
|
||||||
|
public static <F, S> JavaType<Pair<F, @Nullable S>> pairOfNullableSecond(
|
||||||
|
JavaType<F> firstType, JavaType<S> secondType) {
|
||||||
|
return JavaType.of(Types.pairOf(firstType.type, secondType.type));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link Pair} type with the given nullable first and nullable second element types.
|
||||||
|
*
|
||||||
|
* <p>For parameterized element types, use {@link #pairOfNullableFirstAndSecond(JavaType,
|
||||||
|
* JavaType)}.
|
||||||
|
*/
|
||||||
|
public static <F, S> JavaType<Pair<@Nullable F, @Nullable S>> pairOfNullableFirstAndSecond(
|
||||||
|
Class<F> firstType, Class<S> secondType) {
|
||||||
|
return JavaType.of(Types.pairOf(firstType, secondType));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link Pair} type with the given nullable first and nullable second element types.
|
||||||
|
*/
|
||||||
|
public static <F, S> JavaType<Pair<@Nullable F, @Nullable S>> pairOfNullableFirstAndSecond(
|
||||||
|
JavaType<F> firstType, JavaType<S> secondType) {
|
||||||
|
return JavaType.of(Types.pairOf(firstType.type, secondType.type));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an array type with the given non-null element type.
|
||||||
|
*
|
||||||
|
* <p>For a nullable element type, use {@link #arrayOfNullable(Class)}.
|
||||||
|
*
|
||||||
|
* <p>For a parameterized element type, use {@link #arrayOf(JavaType)}.
|
||||||
|
*/
|
||||||
public static <E> JavaType<E[]> arrayOf(Class<E> elementType) {
|
public static <E> JavaType<E[]> arrayOf(Class<E> elementType) {
|
||||||
return JavaType.of(Types.arrayOf(elementType));
|
return JavaType.of(Types.arrayOf(elementType));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates an array type with the given element type. */
|
/**
|
||||||
|
* Creates an array type with the given non-null element type.
|
||||||
|
*
|
||||||
|
* <p>For a nullable element type, use {@link #arrayOfNullable(JavaType)}.
|
||||||
|
*/
|
||||||
public static <E> JavaType<E[]> arrayOf(JavaType<E> elementType) {
|
public static <E> JavaType<E[]> arrayOf(JavaType<E> elementType) {
|
||||||
return JavaType.of(Types.arrayOf(elementType.type));
|
return JavaType.of(Types.arrayOf(elementType.type));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates an {@link Iterable} type with the given element type. */
|
/**
|
||||||
|
* Creates an array type whose element type is nullable.
|
||||||
|
*
|
||||||
|
* <p>For a non-null element type, use {@link #arrayOf(Class)}.
|
||||||
|
*
|
||||||
|
* <p>For a parameterized element type, use {@link #arrayOfNullable(JavaType)}.
|
||||||
|
*/
|
||||||
|
public static <E> JavaType<@Nullable E[]> arrayOfNullable(Class<E> elementType) {
|
||||||
|
return JavaType.of(Types.arrayOf(elementType));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an array type whose element type is nullable.
|
||||||
|
*
|
||||||
|
* <p>For a non-null element type, use {@link #arrayOf(JavaType)}.
|
||||||
|
*/
|
||||||
|
public static <E> JavaType<@Nullable E[]> arrayOfNullable(JavaType<E> elementType) {
|
||||||
|
return JavaType.of(Types.arrayOf(elementType.type));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an {@link Iterable} type with the given non-null element type.
|
||||||
|
*
|
||||||
|
* <p>For a nullable element type, use {@link #iterableOfNullable(Class)}.
|
||||||
|
*
|
||||||
|
* <p>For a parameterized element type, use {@link #iterableOf(JavaType)}.
|
||||||
|
*/
|
||||||
public static <E> JavaType<Iterable<E>> iterableOf(Class<E> elementType) {
|
public static <E> JavaType<Iterable<E>> iterableOf(Class<E> elementType) {
|
||||||
return JavaType.of(Types.iterableOf(elementType));
|
return JavaType.of(Types.iterableOf(elementType));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates an {@link Iterable} type with the given element type. */
|
/**
|
||||||
|
* Creates an {@link Iterable} type with the given non-null element type.
|
||||||
|
*
|
||||||
|
* <p>For a nullable element type, use {@link #iterableOfNullable(JavaType)}.
|
||||||
|
*/
|
||||||
public static <E> JavaType<Iterable<E>> iterableOf(JavaType<E> elementType) {
|
public static <E> JavaType<Iterable<E>> iterableOf(JavaType<E> elementType) {
|
||||||
return JavaType.of(Types.iterableOf(elementType.type));
|
return JavaType.of(Types.iterableOf(elementType.type));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates a {@link Collection} type with the given element type. */
|
/**
|
||||||
|
* Creates an {@link Iterable} type whose element type is nullable.
|
||||||
|
*
|
||||||
|
* <p>For a non-null element type, use {@link #iterableOf(Class)}.
|
||||||
|
*
|
||||||
|
* <p>For a parameterized element type, use {@link #iterableOfNullable(JavaType)}.
|
||||||
|
*/
|
||||||
|
public static <E> JavaType<Iterable<@Nullable E>> iterableOfNullable(Class<E> elementType) {
|
||||||
|
return JavaType.of(Types.iterableOf(elementType));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an {@link Iterable} type whose element type is nullable.
|
||||||
|
*
|
||||||
|
* <p>For a non-null element type, use {@link #iterableOf(JavaType)}.
|
||||||
|
*/
|
||||||
|
public static <E> JavaType<Iterable<@Nullable E>> iterableOfNullable(JavaType<E> elementType) {
|
||||||
|
return JavaType.of(Types.iterableOf(elementType.type));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link Collection} type with the given non-null element type.
|
||||||
|
*
|
||||||
|
* <p>For a nullable element type, use {@link #collectionOfNullable(Class)}.
|
||||||
|
*
|
||||||
|
* <p>For a parameterized element type, use {@link #collectionOf(JavaType)}.
|
||||||
|
*/
|
||||||
public static <E> JavaType<Collection<E>> collectionOf(Class<E> elementType) {
|
public static <E> JavaType<Collection<E>> collectionOf(Class<E> elementType) {
|
||||||
return JavaType.of(Types.collectionOf(elementType));
|
return JavaType.of(Types.collectionOf(elementType));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates a {@link Collection} type with the given element type. */
|
/**
|
||||||
|
* Creates a {@link Collection} type with the given non-null element type.
|
||||||
|
*
|
||||||
|
* <p>For a nullable element type, use {@link #collectionOfNullable(JavaType)}.
|
||||||
|
*/
|
||||||
public static <E> JavaType<Collection<E>> collectionOf(JavaType<E> elementType) {
|
public static <E> JavaType<Collection<E>> collectionOf(JavaType<E> elementType) {
|
||||||
return JavaType.of(Types.collectionOf(elementType.type));
|
return JavaType.of(Types.collectionOf(elementType.type));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates a {@link List} type with the given element type. */
|
/**
|
||||||
|
* Creates a {@link Collection} type whose element type is nullable.
|
||||||
|
*
|
||||||
|
* <p>For a non-null element type, use {@link #collectionOf(Class)}.
|
||||||
|
*
|
||||||
|
* <p>For a parameterized element type, use {@link #collectionOfNullable(JavaType)}.
|
||||||
|
*/
|
||||||
|
public static <E> JavaType<Collection<@Nullable E>> collectionOfNullable(Class<E> elementType) {
|
||||||
|
return JavaType.of(Types.collectionOf(elementType));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link Collection} type whose element type is nullable.
|
||||||
|
*
|
||||||
|
* <p>For a non-null element type, use {@link #collectionOf(JavaType)}.
|
||||||
|
*/
|
||||||
|
public static <E> JavaType<Collection<@Nullable E>> collectionOfNullable(
|
||||||
|
JavaType<E> elementType) {
|
||||||
|
return JavaType.of(Types.collectionOf(elementType.type));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link List} type with the given non-null element type.
|
||||||
|
*
|
||||||
|
* <p>For a nullable element type, use {@link #listOfNullable(Class)}.
|
||||||
|
*
|
||||||
|
* <p>For a parameterized element type, use {@link #listOf(JavaType)}.
|
||||||
|
*/
|
||||||
public static <E> JavaType<List<E>> listOf(Class<E> elementType) {
|
public static <E> JavaType<List<E>> listOf(Class<E> elementType) {
|
||||||
return JavaType.of(Types.listOf(elementType));
|
return JavaType.of(Types.listOf(elementType));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates a {@link List} type with the given element type. */
|
/**
|
||||||
|
* Creates a {@link List} type with the given non-null element type.
|
||||||
|
*
|
||||||
|
* <p>For a nullable element type, use {@link #listOfNullable(JavaType)}.
|
||||||
|
*/
|
||||||
public static <E> JavaType<List<E>> listOf(JavaType<E> elementType) {
|
public static <E> JavaType<List<E>> listOf(JavaType<E> elementType) {
|
||||||
return JavaType.of(Types.listOf(elementType.type));
|
return JavaType.of(Types.listOf(elementType.type));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates a {@link Set} type with the given element type. */
|
/**
|
||||||
|
* Creates a {@link List} type whose element type is nullable.
|
||||||
|
*
|
||||||
|
* <p>For a non-null element type, use {@link #listOf(Class)}.
|
||||||
|
*
|
||||||
|
* <p>For a parameterized element type, use {@link #listOfNullable(JavaType)}.
|
||||||
|
*/
|
||||||
|
public static <E> JavaType<List<@Nullable E>> listOfNullable(Class<E> elementType) {
|
||||||
|
return JavaType.of(Types.listOf(elementType));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link List} type whose element type is nullable.
|
||||||
|
*
|
||||||
|
* <p>For a non-null element type, use {@link #listOf(JavaType)}.
|
||||||
|
*/
|
||||||
|
public static <E> JavaType<List<@Nullable E>> listOfNullable(JavaType<E> elementType) {
|
||||||
|
return JavaType.of(Types.listOf(elementType.type));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link Set} type with the given non-null element type.
|
||||||
|
*
|
||||||
|
* <p>For a nullable element type, use {@link #setOfNullable(Class)}.
|
||||||
|
*
|
||||||
|
* <p>For a parameterized element type, use {@link #setOf(JavaType)}.
|
||||||
|
*/
|
||||||
public static <E> JavaType<Set<E>> setOf(Class<E> elementType) {
|
public static <E> JavaType<Set<E>> setOf(Class<E> elementType) {
|
||||||
return JavaType.of(Types.setOf(elementType));
|
return JavaType.of(Types.setOf(elementType));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates a {@link Set} type with the given element type. */
|
/**
|
||||||
|
* Creates a {@link Set} type with the given non-null element type.
|
||||||
|
*
|
||||||
|
* <p>For a nullable element type, use {@link #setOfNullable(JavaType)}.
|
||||||
|
*/
|
||||||
public static <E> JavaType<Set<E>> setOf(JavaType<E> elementType) {
|
public static <E> JavaType<Set<E>> setOf(JavaType<E> elementType) {
|
||||||
return JavaType.of(Types.setOf(elementType.type));
|
return JavaType.of(Types.setOf(elementType.type));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates a {@link Map} type with the given key and value types. */
|
/**
|
||||||
|
* Creates a {@link Set} type whose element type is nullable.
|
||||||
|
*
|
||||||
|
* <p>For a non-null element type, use {@link #setOf(Class)}.
|
||||||
|
*
|
||||||
|
* <p>For a parameterized element type, use {@link #setOfNullable(JavaType)}.
|
||||||
|
*/
|
||||||
|
public static <E> JavaType<Set<@Nullable E>> setOfNullable(Class<E> elementType) {
|
||||||
|
return JavaType.of(Types.setOf(elementType));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link Set} type whose element type is nullable.
|
||||||
|
*
|
||||||
|
* <p>For a non-null element type, use {@link #setOf(JavaType)}.
|
||||||
|
*/
|
||||||
|
public static <E> JavaType<Set<@Nullable E>> setOfNullable(JavaType<E> elementType) {
|
||||||
|
return JavaType.of(Types.setOf(elementType.type));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link Map} type with the given non-null key and non-null value types.
|
||||||
|
*
|
||||||
|
* <p>For nullable keys and/or values, use one of the {@code mapOfNullable*} methods.
|
||||||
|
*
|
||||||
|
* <p>For parameterized key and value types, use {@link #mapOf(JavaType, JavaType)}.
|
||||||
|
*/
|
||||||
public static <K, V> JavaType<Map<K, V>> mapOf(Class<K> keyType, Class<V> valueType) {
|
public static <K, V> JavaType<Map<K, V>> mapOf(Class<K> keyType, Class<V> valueType) {
|
||||||
return JavaType.of(Types.mapOf(keyType, valueType));
|
return JavaType.of(Types.mapOf(keyType, valueType));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates a {@link Map} type with the given key and value types. */
|
/**
|
||||||
|
* Creates a {@link Map} type with the given non-null key and non-null value types.
|
||||||
|
*
|
||||||
|
* <p>For nullable keys and/or values, use one of the {@code mapOfNullable*} methods.
|
||||||
|
*/
|
||||||
public static <K, V> JavaType<Map<K, V>> mapOf(JavaType<K> keyType, JavaType<V> valueType) {
|
public static <K, V> JavaType<Map<K, V>> mapOf(JavaType<K> keyType, JavaType<V> valueType) {
|
||||||
return JavaType.of(Types.mapOf(keyType.type, valueType.type));
|
return JavaType.of(Types.mapOf(keyType.type, valueType.type));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link Map} type with the given nullable key and non-null value types.
|
||||||
|
*
|
||||||
|
* <p>For parameterized key and value types, use {@link #mapOfNullableKeys(JavaType, JavaType)}.
|
||||||
|
*/
|
||||||
|
public static <K, V> JavaType<Map<@Nullable K, V>> mapOfNullableKeys(
|
||||||
|
Class<K> keyType, Class<V> valueType) {
|
||||||
|
return JavaType.of(Types.mapOf(keyType, valueType));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates a {@link Map} type with the given nullable key and non-null value types. */
|
||||||
|
public static <K, V> JavaType<Map<@Nullable K, V>> mapOfNullableKeys(
|
||||||
|
JavaType<K> keyType, JavaType<V> valueType) {
|
||||||
|
return JavaType.of(Types.mapOf(keyType.type, valueType.type));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link Map} type with the given non-null key and nullable value types.
|
||||||
|
*
|
||||||
|
* <p>For parameterized key and value types, use {@link #mapOfNullableValues(JavaType, JavaType)}.
|
||||||
|
*/
|
||||||
|
public static <K, V> JavaType<Map<K, @Nullable V>> mapOfNullableValues(
|
||||||
|
Class<K> keyType, Class<V> valueType) {
|
||||||
|
return JavaType.of(Types.mapOf(keyType, valueType));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates a {@link Map} type with the given non-null key and nullable value types. */
|
||||||
|
public static <K, V> JavaType<Map<K, @Nullable V>> mapOfNullableValues(
|
||||||
|
JavaType<K> keyType, JavaType<V> valueType) {
|
||||||
|
return JavaType.of(Types.mapOf(keyType.type, valueType.type));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link Map} type with the given nullable key and nullable value types.
|
||||||
|
*
|
||||||
|
* <p>For parameterized key and value types, use {@link #mapOfNullableKeysAndValues(JavaType,
|
||||||
|
* JavaType)}.
|
||||||
|
*/
|
||||||
|
public static <K, V> JavaType<Map<@Nullable K, @Nullable V>> mapOfNullableKeysAndValues(
|
||||||
|
Class<K> keyType, Class<V> valueType) {
|
||||||
|
return JavaType.of(Types.mapOf(keyType, valueType));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates a {@link Map} type with the given nullable key and nullable value types. */
|
||||||
|
public static <K, V> JavaType<Map<@Nullable K, @Nullable V>> mapOfNullableKeysAndValues(
|
||||||
|
JavaType<K> keyType, JavaType<V> valueType) {
|
||||||
|
return JavaType.of(Types.mapOf(keyType.type, valueType.type));
|
||||||
|
}
|
||||||
|
|
||||||
/** Returns the underlying {@link Type}. */
|
/** Returns the underlying {@link Type}. */
|
||||||
public Type getType() {
|
public Type getType() {
|
||||||
return type;
|
return type;
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ package org.pkl.config.java;
|
|||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
import org.junit.jupiter.api.AfterAll;
|
import org.junit.jupiter.api.AfterAll;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.pkl.core.ModuleSource;
|
import org.pkl.core.ModuleSource;
|
||||||
@@ -50,4 +52,31 @@ public final class ConfigEvaluatorTest extends AbstractConfigTest {
|
|||||||
var address = addressConfig.as(Address.class);
|
var address = addressConfig.as(Address.class);
|
||||||
assertThat(address.street).isEqualTo("Fuzzy St.");
|
assertThat(address.street).isEqualTo("Fuzzy St.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void asNullableWithClass() {
|
||||||
|
var mod = evaluator.evaluate(ModuleSource.text("nullValue = null; strValue = \"Bob\""));
|
||||||
|
@Nullable String nullValue = mod.get("nullValue").asNullable(String.class);
|
||||||
|
@Nullable String strValue = mod.get("strValue").asNullable(String.class);
|
||||||
|
assertThat(nullValue).isNull();
|
||||||
|
assertThat(strValue).isNotNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void asNullableWithType() {
|
||||||
|
var mod = evaluator.evaluate(ModuleSource.text("nullValue = null; strValue = \"Bob\""));
|
||||||
|
@Nullable String nullValue = mod.get("nullValue").asNullable((Type) String.class);
|
||||||
|
@Nullable String strValue = mod.get("strValue").asNullable((Type) String.class);
|
||||||
|
assertThat(nullValue).isNull();
|
||||||
|
assertThat(strValue).isNotNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void asNullableWithJavaType() {
|
||||||
|
var mod = evaluator.evaluate(ModuleSource.text("nullValue = null; strValue = \"Bob\""));
|
||||||
|
@Nullable String nullValue = mod.get("nullValue").asNullable(JavaType.of(String.class));
|
||||||
|
@Nullable String strValue = mod.get("strValue").asNullable(JavaType.of(String.class));
|
||||||
|
assertThat(nullValue).isNull();
|
||||||
|
assertThat(strValue).isNotNull();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,15 +17,25 @@ package org.pkl.config.java;
|
|||||||
|
|
||||||
import static org.assertj.core.api.Assertions.*;
|
import static org.assertj.core.api.Assertions.*;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.pkl.config.java.mapper.Reflection;
|
import org.pkl.config.java.mapper.Reflection;
|
||||||
import org.pkl.config.java.mapper.Types;
|
import org.pkl.config.java.mapper.Types;
|
||||||
|
import org.pkl.core.Pair;
|
||||||
|
|
||||||
public final class JavaTypeTest {
|
public final class JavaTypeTest {
|
||||||
|
@Test
|
||||||
|
public void constructSimpleType() {
|
||||||
|
assertThat(JavaType.of(String.class)).isEqualTo(new JavaType<String>() {});
|
||||||
|
//noinspection AssertBetweenInconvertibleTypes
|
||||||
|
assertThat(JavaType.of((Type) String.class)).isEqualTo(new JavaType<String>() {});
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void constructOptionalType() {
|
public void constructOptionalType() {
|
||||||
var type = JavaType.optionalOf(String.class);
|
var type = JavaType.optionalOf(String.class);
|
||||||
@@ -34,6 +44,14 @@ public final class JavaTypeTest {
|
|||||||
assertThat(Reflection.toRawType(type.getType())).isEqualTo(Optional.class);
|
assertThat(Reflection.toRawType(type.getType())).isEqualTo(Optional.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void constructPairType() {
|
||||||
|
var type = JavaType.pairOf(String.class, Integer.class);
|
||||||
|
assertThat(type).isEqualTo(new JavaType<Pair<String, Integer>>() {});
|
||||||
|
assertThat(type)
|
||||||
|
.isEqualTo(JavaType.pairOf(JavaType.of(String.class), JavaType.of(Integer.class)));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void constructArrayType() {
|
public void constructArrayType() {
|
||||||
var type = JavaType.arrayOf(String.class);
|
var type = JavaType.arrayOf(String.class);
|
||||||
@@ -42,6 +60,14 @@ public final class JavaTypeTest {
|
|||||||
assertThat(Reflection.toRawType(type.getType()).isArray()).isTrue();
|
assertThat(Reflection.toRawType(type.getType()).isArray()).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void constructArrayOfNullableType() {
|
||||||
|
var type = JavaType.arrayOfNullable(String.class);
|
||||||
|
assertThat(type).isEqualTo(new JavaType<@Nullable String[]>() {});
|
||||||
|
assertThat(type).isEqualTo(JavaType.arrayOfNullable(JavaType.of(String.class)));
|
||||||
|
assertThat(Reflection.toRawType(type.getType()).isArray()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void constructIterableType() {
|
public void constructIterableType() {
|
||||||
var type = JavaType.iterableOf(String.class);
|
var type = JavaType.iterableOf(String.class);
|
||||||
@@ -50,6 +76,14 @@ public final class JavaTypeTest {
|
|||||||
assertThat(Reflection.toRawType(type.getType())).isEqualTo(Iterable.class);
|
assertThat(Reflection.toRawType(type.getType())).isEqualTo(Iterable.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void constructIterableOfNullableType() {
|
||||||
|
var type = JavaType.iterableOfNullable(String.class);
|
||||||
|
assertThat(type).isEqualTo(new JavaType<Iterable<@Nullable String>>() {});
|
||||||
|
assertThat(type).isEqualTo(JavaType.iterableOfNullable(JavaType.of(String.class)));
|
||||||
|
assertThat(Reflection.toRawType(type.getType())).isEqualTo(Iterable.class);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void constructCollectionType() {
|
public void constructCollectionType() {
|
||||||
var type = JavaType.collectionOf(String.class);
|
var type = JavaType.collectionOf(String.class);
|
||||||
@@ -58,6 +92,14 @@ public final class JavaTypeTest {
|
|||||||
assertThat(Reflection.toRawType(type.getType())).isEqualTo(Collection.class);
|
assertThat(Reflection.toRawType(type.getType())).isEqualTo(Collection.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void constructCollectionOfNullableType() {
|
||||||
|
var type = JavaType.collectionOfNullable(String.class);
|
||||||
|
assertThat(type).isEqualTo(new JavaType<Collection<@Nullable String>>() {});
|
||||||
|
assertThat(type).isEqualTo(JavaType.collectionOfNullable(JavaType.of(String.class)));
|
||||||
|
assertThat(Reflection.toRawType(type.getType())).isEqualTo(Collection.class);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void constructListType() {
|
public void constructListType() {
|
||||||
var type = JavaType.listOf(String.class);
|
var type = JavaType.listOf(String.class);
|
||||||
@@ -66,6 +108,14 @@ public final class JavaTypeTest {
|
|||||||
assertThat(Reflection.toRawType(type.getType())).isEqualTo(List.class);
|
assertThat(Reflection.toRawType(type.getType())).isEqualTo(List.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void constructListOfNullableType() {
|
||||||
|
var type = JavaType.listOfNullable(String.class);
|
||||||
|
assertThat(type).isEqualTo(new JavaType<List<@Nullable String>>() {});
|
||||||
|
assertThat(type).isEqualTo(JavaType.listOfNullable(JavaType.of(String.class)));
|
||||||
|
assertThat(Reflection.toRawType(type.getType())).isEqualTo(List.class);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void constructSetType() {
|
public void constructSetType() {
|
||||||
var type = JavaType.setOf(String.class);
|
var type = JavaType.setOf(String.class);
|
||||||
@@ -74,6 +124,14 @@ public final class JavaTypeTest {
|
|||||||
assertThat(Reflection.toRawType(type.getType())).isEqualTo(Set.class);
|
assertThat(Reflection.toRawType(type.getType())).isEqualTo(Set.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void constructSetOfNullableType() {
|
||||||
|
var type = JavaType.setOfNullable(String.class);
|
||||||
|
assertThat(type).isEqualTo(new JavaType<Set<@Nullable String>>() {});
|
||||||
|
assertThat(type).isEqualTo(JavaType.setOfNullable(JavaType.of(String.class)));
|
||||||
|
assertThat(Reflection.toRawType(type.getType())).isEqualTo(Set.class);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void constructMapType() {
|
public void constructMapType() {
|
||||||
var type = JavaType.mapOf(String.class, URI.class);
|
var type = JavaType.mapOf(String.class, URI.class);
|
||||||
@@ -83,7 +141,63 @@ public final class JavaTypeTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void usageAsTypeToken() {
|
public void constructPairOfNullableFirstType() {
|
||||||
|
var type = JavaType.pairOfNullableFirst(String.class, Integer.class);
|
||||||
|
assertThat(type).isEqualTo(new JavaType<Pair<@Nullable String, Integer>>() {});
|
||||||
|
assertThat(type)
|
||||||
|
.isEqualTo(
|
||||||
|
JavaType.pairOfNullableFirst(JavaType.of(String.class), JavaType.of(Integer.class)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void constructPairOfNullableSecondType() {
|
||||||
|
var type = JavaType.pairOfNullableSecond(String.class, Integer.class);
|
||||||
|
assertThat(type).isEqualTo(new JavaType<Pair<String, @Nullable Integer>>() {});
|
||||||
|
assertThat(type)
|
||||||
|
.isEqualTo(
|
||||||
|
JavaType.pairOfNullableSecond(JavaType.of(String.class), JavaType.of(Integer.class)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void constructPairOfNullableFirstAndSecondType() {
|
||||||
|
var type = JavaType.pairOfNullableFirstAndSecond(String.class, Integer.class);
|
||||||
|
assertThat(type).isEqualTo(new JavaType<Pair<@Nullable String, @Nullable Integer>>() {});
|
||||||
|
assertThat(type)
|
||||||
|
.isEqualTo(
|
||||||
|
JavaType.pairOfNullableFirstAndSecond(
|
||||||
|
JavaType.of(String.class), JavaType.of(Integer.class)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void constructMapOfNullableKeysType() {
|
||||||
|
var type = JavaType.mapOfNullableKeys(String.class, URI.class);
|
||||||
|
assertThat(type).isEqualTo(new JavaType<Map<@Nullable String, URI>>() {});
|
||||||
|
assertThat(type)
|
||||||
|
.isEqualTo(JavaType.mapOfNullableKeys(JavaType.of(String.class), JavaType.of(URI.class)));
|
||||||
|
assertThat(Reflection.toRawType(type.getType())).isEqualTo(Map.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void constructMapOfNullableValuesType() {
|
||||||
|
var type = JavaType.mapOfNullableValues(String.class, URI.class);
|
||||||
|
assertThat(type).isEqualTo(new JavaType<Map<String, @Nullable URI>>() {});
|
||||||
|
assertThat(type)
|
||||||
|
.isEqualTo(JavaType.mapOfNullableValues(JavaType.of(String.class), JavaType.of(URI.class)));
|
||||||
|
assertThat(Reflection.toRawType(type.getType())).isEqualTo(Map.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void constructMapOfNullableKeysAndValuesType() {
|
||||||
|
var type = JavaType.mapOfNullableKeysAndValues(String.class, URI.class);
|
||||||
|
assertThat(type).isEqualTo(new JavaType<Map<@Nullable String, @Nullable URI>>() {});
|
||||||
|
assertThat(type)
|
||||||
|
.isEqualTo(
|
||||||
|
JavaType.mapOfNullableKeysAndValues(JavaType.of(String.class), JavaType.of(URI.class)));
|
||||||
|
assertThat(Reflection.toRawType(type.getType())).isEqualTo(Map.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void constructTypeToken() {
|
||||||
var javaType = new JavaType<Map<String, List<URI>>>() {};
|
var javaType = new JavaType<Map<String, List<URI>>>() {};
|
||||||
|
|
||||||
assertThat(javaType.getType()).isEqualTo(Types.mapOf(String.class, Types.listOf(URI.class)));
|
assertThat(javaType.getType()).isEqualTo(Types.mapOf(String.class, Types.listOf(URI.class)));
|
||||||
@@ -110,6 +224,7 @@ public final class JavaTypeTest {
|
|||||||
var type2 = new JavaType<Map<String, List<URL>>>() {};
|
var type2 = new JavaType<Map<String, List<URL>>>() {};
|
||||||
var type3 = JavaType.of(Types.mapOf(String.class, Types.listOf(Path.class)));
|
var type3 = JavaType.of(Types.mapOf(String.class, Types.listOf(Path.class)));
|
||||||
|
|
||||||
|
//noinspection AssertBetweenInconvertibleTypes
|
||||||
assertThat(type2).isNotEqualTo(type1);
|
assertThat(type2).isNotEqualTo(type1);
|
||||||
assertThat(type3).isNotEqualTo(type1);
|
assertThat(type3).isNotEqualTo(type1);
|
||||||
assertThat(type2).isNotEqualTo(type3);
|
assertThat(type2).isNotEqualTo(type3);
|
||||||
|
|||||||
@@ -40,14 +40,15 @@ import org.pkl.config.kotlin.mapper.KotlinConverterFactories
|
|||||||
* `as(JavaType.listOf(String::class.java))`
|
* `as(JavaType.listOf(String::class.java))`
|
||||||
*/
|
*/
|
||||||
inline fun <reified T> Config.to(): T {
|
inline fun <reified T> Config.to(): T {
|
||||||
val result = `as`<T>(typeOf<T>().javaType)
|
if (null is T) {
|
||||||
if (result == null && null !is T) {
|
return asNullable(typeOf<T>().javaType)
|
||||||
throw ConversionException(
|
}
|
||||||
|
|
||||||
|
return `as`(typeOf<T>().javaType)
|
||||||
|
?: throw ConversionException(
|
||||||
"Expected a non-null value but got `null`. " +
|
"Expected a non-null value but got `null`. " +
|
||||||
"To allow null values, convert to a nullable Kotlin type, for example `String?`."
|
"To allow null values, convert to a nullable Kotlin type, for example `String?`."
|
||||||
)
|
)
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user