/*
 * Copyright 2017-2020 original authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.micronaut.core.type;

import io.micronaut.core.annotation.AnnotatedElement;
import io.micronaut.core.annotation.AnnotationMetadata;
import io.micronaut.core.annotation.UsedByGeneratedCode;
import io.micronaut.core.async.publisher.Publishers;
import io.micronaut.core.naming.NameUtils;
import io.micronaut.core.reflect.ReflectionUtils;
import io.micronaut.core.util.ArrayUtils;

import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.*;
import java.util.concurrent.CompletionStage;
import java.util.stream.Collectors;

Represents an argument to a method or constructor or type.
Author:Graeme Rocher
Type parameters:
  • <T> – The argument type
Since:1.0
/** * Represents an argument to a method or constructor or type. * * @param <T> The argument type * @author Graeme Rocher * @since 1.0 */
public interface Argument<T> extends TypeVariableResolver, AnnotatedElement, Type {
Constant for string argument.
/** * Constant for string argument. */
Argument<String> STRING = Argument.of(String.class);
Constant for int argument. Used by generated code, do not remove.
/** * Constant for int argument. Used by generated code, do not remove. */
@SuppressWarnings("unused") Argument<Integer> INT = Argument.of(int.class);
Constant for long argument. Used by generated code, do not remove.
/** * Constant for long argument. Used by generated code, do not remove. */
@SuppressWarnings("unused") Argument<Long> LONG = Argument.of(long.class);
Constant for float argument. Used by generated code, do not remove.
/** * Constant for float argument. Used by generated code, do not remove. */
@SuppressWarnings("unused") Argument<Float> FLOAT = Argument.of(float.class);
Constant for double argument. Used by generated code, do not remove.
/** * Constant for double argument. Used by generated code, do not remove. */
@SuppressWarnings("unused") Argument<Double> DOUBLE = Argument.of(double.class);
Constant for void argument. Used by generated code, do not remove.
/** * Constant for void argument. Used by generated code, do not remove. */
@SuppressWarnings("unused") Argument<Void> VOID = Argument.of(void.class);
Constant for byte argument. Used by generated code, do not remove.
/** * Constant for byte argument. Used by generated code, do not remove. */
@SuppressWarnings("unused") Argument<Byte> BYTE = Argument.of(byte.class);
Constant for boolean argument. Used by generated code, do not remove.
/** * Constant for boolean argument. Used by generated code, do not remove. */
@SuppressWarnings("unused") Argument<Boolean> BOOLEAN = Argument.of(boolean.class);
Constant char argument. Used by generated code, do not remove.
/** * Constant char argument. Used by generated code, do not remove. */
@SuppressWarnings("unused") Argument<Character> CHAR = Argument.of(char.class);
Constant short argument. Used by generated code, do not remove.
/** * Constant short argument. Used by generated code, do not remove. */
@SuppressWarnings("unused") Argument<Short> SHORT = Argument.of(short.class);
Constant representing zero arguments. Used by generated code, do not remove.
/** * Constant representing zero arguments. Used by generated code, do not remove. */
@SuppressWarnings("unused") @UsedByGeneratedCode Argument[] ZERO_ARGUMENTS = new Argument[0];
Default Object argument. Used by generated code, do not remove.
/** * Default Object argument. Used by generated code, do not remove. */
@SuppressWarnings("unused") Argument<Object> OBJECT_ARGUMENT = of(Object.class);
Constant for List argument.
/** * Constant for List<String> argument. */
Argument<List<String>> LIST_OF_STRING = Argument.listOf(String.class);
Constant for Void object argument.
/** * Constant for Void object argument. */
Argument<Void> VOID_OBJECT = Argument.of(Void.class);
Returns:The name of the argument
/** * @return The name of the argument */
@Override @NonNull String getName(); @Override @NonNull default String getTypeName() { Argument<?>[] typeParameters = getTypeParameters(); if (ArrayUtils.isNotEmpty(typeParameters)) { String typeName = getType().getTypeName(); return typeName + "<" + Arrays.stream(typeParameters).map(Argument::getTypeName).collect(Collectors.joining(",")) + ">"; } else { return getType().getTypeName(); } }
Returns:The type of the argument
/** * @return The type of the argument */
@NonNull Class<T> getType();
Whether the types are equivalent. The regular Object.equals(Object) implementation includes the argument name within the comparison so this method offers a variation that just compares types.
Params:
  • other – The type type
Returns:True if they are equal
/** * Whether the types are equivalent. The regular {@link Object#equals(Object)} implementation includes the argument * name within the comparison so this method offers a variation that just compares types. * * @param other The type type * @return True if they are equal */
boolean equalsType(Argument<?> other);
The hash code including only the types. The regular Object.hashCode() implementation includes the argument name within the comparison so this method offers a variation that just compares types.
Returns:The type hash code
/** * The hash code including only the types. The regular {@link Object#hashCode()} implementation includes the * argument name within the comparison so this method offers a variation that just compares types. * * @return The type hash code */
int typeHashCode();
Represent this argument as a ParameterizedType.
Returns:The ParameterizedType
Since:2.0.0
/** * Represent this argument as a {@link ParameterizedType}. * @return The {@link ParameterizedType} * @since 2.0.0 */
default @NonNull ParameterizedType asParameterizedType() { return new ParameterizedType() { @Override public Type[] getActualTypeArguments() { return getTypeParameters(); } @Override public Type getRawType() { return Argument.this.getType(); } @Override public Type getOwnerType() { return Argument.this; } @Override public String getTypeName() { return Argument.this.getTypeName(); } @Override public String toString() { return getTypeName(); } }; }
Whether the given argument is an instance.
Params:
  • o – The object
Returns:True if it is an instance of this type
/** * Whether the given argument is an instance. * @param o The object * @return True if it is an instance of this type */
default boolean isInstance(@Nullable Object o) { if (o == null) { return false; } return getType().isInstance(o); }
Returns the string representation of the argument type, including generics.
Params:
  • simple – If true, output the simple name of types
Returns:The type string representation
/** * Returns the string representation of the argument type, including generics. * * @param simple If true, output the simple name of types * @return The type string representation */
default String getTypeString(boolean simple) { Class<T> type = getType(); StringBuilder returnType = new StringBuilder(simple ? type.getSimpleName() : type.getName()); Map<String, Argument<?>> generics = getTypeVariables(); if (!generics.isEmpty()) { returnType .append("<") .append(generics.values() .stream() .map(arg -> arg.getTypeString(simple)) .collect(Collectors.joining(", "))) .append(">"); } return returnType.toString(); }
Returns:Whether the argument has any type variables
/** * @return Whether the argument has any type variables */
default boolean hasTypeVariables() { return !getTypeVariables().isEmpty(); }
Returns:Whether this is a container type.
/** * @return Whether this is a container type. */
default boolean isContainerType() { return DefaultArgument.CONTAINER_TYPES.contains(getType()); }
Returns:Is the return type reactive.
Since:2.0.0
/** * @return Is the return type reactive. * @since 2.0.0 */
default boolean isReactive() { return Publishers.isConvertibleToPublisher(getType()); }
Returns:Is the return the return type a reactive completable type.
Since:2.0.0
/** * @return Is the return the return type a reactive completable type. * @since 2.0.0 */
default boolean isCompletable() { return Publishers.isCompletable(getType()); }
Returns:Is the return type asynchronous.
Since:2.0.0
/** * @return Is the return type asynchronous. * @since 2.0.0 */
default boolean isAsync() { Class<T> type = getType(); return CompletionStage.class.isAssignableFrom(type); }
Returns:Is this type an optional
/** * @return Is this type an optional */
default boolean isOptional() { return getType() == Optional.class; }
Returns:Is the return type either async or reactive.
Since:2.0.0
/** * @return Is the return type either async or reactive. * @since 2.0.0 */
default boolean isAsyncOrReactive() { return isAsync() || isReactive(); }
Returns whether the return type is logically void. This includes reactive times that emit nothing (such as Completable) and asynchronous types that emit Void.
Returns:Is the return type logically void.
Since:2.0.0
/** * Returns whether the return type is logically void. This includes * reactive times that emit nothing (such as {@link io.micronaut.core.async.subscriber.Completable}) * and asynchronous types that emit {@link Void}. * * @return Is the return type logically void. * @since 2.0.0 */
default boolean isVoid() { Class<T> javaReturnType = getType(); if (javaReturnType == void.class) { return true; } else { if (isReactive() || isAsync()) { return isCompletable() || getFirstTypeVariable() .filter(arg -> arg.getType() == Void.class).isPresent(); } } return false; }
Convert an argument array to a class array.
Params:
  • arguments – The arguments
Returns:The class array
/** * Convert an argument array to a class array. * * @param arguments The arguments * @return The class array */
static @NonNull Class[] toClassArray(Argument... arguments) { if (ArrayUtils.isEmpty(arguments)) { return ReflectionUtils.EMPTY_CLASS_ARRAY; } Class[] types = new Class[arguments.length]; for (int i = 0; i < arguments.length; i++) { Argument argument = arguments[i]; types[i] = argument.getType(); } return types; }
Convert the arguments to a string representation.
Params:
  • arguments – The arguments
Returns:The String representation
/** * Convert the arguments to a string representation. * * @param arguments The arguments * @return The String representation */
static @NonNull String toString(Argument... arguments) { StringBuilder baseString = new StringBuilder(); if (ArrayUtils.isNotEmpty(arguments)) { for (int i = 0; i < arguments.length; i++) { Argument argument = arguments[i]; baseString.append(argument.toString()); if (i != arguments.length - 1) { baseString.append(','); } } } return baseString.toString(); }
Creates a new argument for the given type and name.
Params:
  • type – The type
  • name – The name
  • typeParameters – the type parameters
Type parameters:
  • <T> – The generic type
Returns:The argument instance
/** * Creates a new argument for the given type and name. * * @param type The type * @param name The name * @param typeParameters the type parameters * @param <T> The generic type * @return The argument instance */
@UsedByGeneratedCode @NonNull static <T> Argument<T> of( Class<T> type, String name, @Nullable Argument... typeParameters) { return new DefaultArgument<>(type, name, AnnotationMetadata.EMPTY_METADATA, typeParameters); }
Creates a new argument for the given type and name.
Params:
  • type – The type
  • name – The name
  • annotationMetadata – the annotation metadata
  • typeParameters – the type parameters
Type parameters:
  • <T> – The generic type
Returns:The argument instance
/** * Creates a new argument for the given type and name. * * @param type The type * @param name The name * @param annotationMetadata the annotation metadata * @param typeParameters the type parameters * @param <T> The generic type * @return The argument instance */
@UsedByGeneratedCode @NonNull static <T> Argument<T> of( Class<T> type, String name, AnnotationMetadata annotationMetadata, @Nullable Argument... typeParameters) { return new DefaultArgument<>(type, name, annotationMetadata, typeParameters); }
Creates a new argument for the given type and name.
Params:
  • type – The type
  • annotationMetadata – the annotation metadata
  • typeParameters – the type parameters
Type parameters:
  • <T> – The generic type
Returns:The argument instance
/** * Creates a new argument for the given type and name. * * @param type The type * @param annotationMetadata the annotation metadata * @param typeParameters the type parameters * @param <T> The generic type * @return The argument instance */
@UsedByGeneratedCode @NonNull static <T> Argument<T> of( Class<T> type, AnnotationMetadata annotationMetadata, @Nullable Argument... typeParameters) { return new DefaultArgument<>(type, annotationMetadata, typeParameters); }
Creates a new argument for the given type and name.
Params:
  • type – The type
  • name – The name
Type parameters:
  • <T> – The generic type
Returns:The argument instance
/** * Creates a new argument for the given type and name. * * @param type The type * @param name The name * @param <T> The generic type * @return The argument instance */
@UsedByGeneratedCode @NonNull static <T> Argument<T> of( Class<T> type, String name) { return new DefaultArgument<>(type, name, AnnotationMetadata.EMPTY_METADATA, Argument.ZERO_ARGUMENTS); }
Creates a new argument for the given type and name.
Params:
  • type – The type
  • typeParameters – The parameters type
Type parameters:
  • <T> – The generic type
Returns:The argument instance
/** * Creates a new argument for the given type and name. * * @param type The type * @param typeParameters The parameters type * @param <T> The generic type * @return The argument instance */
@UsedByGeneratedCode @NonNull static <T> Argument<T> of( Class<T> type, @Nullable Argument... typeParameters) { if (ArrayUtils.isEmpty(typeParameters)) { return of(type); } return new DefaultArgument<>(type, NameUtils.decapitalize(type.getSimpleName()), AnnotationMetadata.EMPTY_METADATA, typeParameters); }
Creates a new argument for the given type and name.
Params:
  • type – The type
Type parameters:
  • <T> – The generic type
Returns:The argument instance
/** * Creates a new argument for the given type and name. * * @param type The type * @param <T> The generic type * @return The argument instance */
@UsedByGeneratedCode @NonNull static <T> Argument<T> of( Class<T> type) { return new DefaultArgument<>(type, AnnotationMetadata.EMPTY_METADATA, Argument.ZERO_ARGUMENTS); }
Creates a new argument for the given type and name.
Params:
  • type – The type
  • typeParameters – the parameters type
Type parameters:
  • <T> – The generic type
Returns:The argument instance
/** * Creates a new argument for the given type and name. * * @param type The type * @param typeParameters the parameters type * @param <T> The generic type * @return The argument instance */
@UsedByGeneratedCode @NonNull static <T> Argument<T> of(Class<T> type, @Nullable Class<?>... typeParameters) { if (ArrayUtils.isEmpty(typeParameters)) { return of(type); } TypeVariable<Class<T>>[] parameters = type.getTypeParameters(); int len = typeParameters.length; if (parameters.length != len) { throw new IllegalArgumentException("Type parameter length does not match. Required: " + parameters.length + ", Specified: " + len); } Argument[] typeArguments = new Argument[len]; for (int i = 0; i < parameters.length; i++) { TypeVariable<Class<T>> parameter = parameters[i]; typeArguments[i] = Argument.of(typeParameters[i], parameter.getName()); } return new DefaultArgument<>(type, AnnotationMetadata.EMPTY_METADATA, typeArguments); }
Creates a new argument representing a generic list.
Params:
  • type – list element type
Type parameters:
  • <T> – list element type
Returns:The argument instance
/** * Creates a new argument representing a generic list. * * @param type list element type * @param <T> list element type * @return The argument instance */
@NonNull static <T> Argument<List<T>> listOf(Class<T> type) { //noinspection unchecked return of((Class<List<T>>) ((Class) List.class), type); }
Creates a new argument representing a generic list.
Params:
  • type – list element type
Type parameters:
  • <T> – list element type
Since:2.0.1
Returns:The argument instance
/** * Creates a new argument representing a generic list. * * @param type list element type * @param <T> list element type * @since 2.0.1 * @return The argument instance */
@NonNull static <T> Argument<List<T>> listOf(Argument<T> type) { //noinspection unchecked return of((Class<List<T>>) ((Class) List.class), type); }
Creates a new argument representing a generic set.
Params:
  • type – set element type
Type parameters:
  • <T> – set element type
Returns:The argument instance
/** * Creates a new argument representing a generic set. * * @param type set element type * @param <T> set element type * @return The argument instance */
@NonNull static <T> Argument<Set<T>> setOf(Class<T> type) { //noinspection unchecked return of((Class<Set<T>>) ((Class) Set.class), type); }
Creates a new argument representing a generic set.
Params:
  • type – set element type
Type parameters:
  • <T> – set element type
Since:2.0.1
Returns:The argument instance
/** * Creates a new argument representing a generic set. * * @param type set element type * @param <T> set element type * @since 2.0.1 * @return The argument instance */
@NonNull static <T> Argument<Set<T>> setOf(Argument<T> type) { //noinspection unchecked return of((Class<Set<T>>) ((Class) Set.class), type); }
Creates a new argument representing a generic map.
Params:
  • keyType – The key type
  • valueType – The value type
Type parameters:
  • <K> – The map key type
  • <V> – The map value type
Returns:The argument instance
/** * Creates a new argument representing a generic map. * * @param keyType The key type * @param valueType The value type * @param <K> The map key type * @param <V> The map value type * @return The argument instance */
@NonNull static <K, V> Argument<Map<K, V>> mapOf(Class<K> keyType, Class<V> valueType) { //noinspection unchecked return of((Class<Map<K, V>>) ((Class) Map.class), keyType, valueType); }
Creates a new argument representing a generic map.
Params:
  • keyType – The key type
  • valueType – The value type
Type parameters:
  • <K> – The map key type
  • <V> – The map value type
Since:2.0.1
Returns:The argument instance
/** * Creates a new argument representing a generic map. * * @param keyType The key type * @param valueType The value type * @param <K> The map key type * @param <V> The map value type * @since 2.0.1 * @return The argument instance */
@NonNull static <K, V> Argument<Map<K, V>> mapOf(Argument<K> keyType, Argument<V> valueType) { //noinspection unchecked return of((Class<Map<K, V>>) ((Class) Map.class), keyType, valueType); } }