package io.micronaut.core.type;
import io.micronaut.core.annotation.AnnotationMetadata;
import io.micronaut.core.annotation.Internal;
import io.micronaut.core.util.ArrayUtils;
import io.micronaut.core.util.CollectionUtils;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.*;
@Internal
public class DefaultArgument<T> implements Argument<T> {
static final Set<Class<?>> CONTAINER_TYPES = CollectionUtils.setOf(
List.class,
Set.class,
Map.class,
Collection.class,
Queue.class,
SortedSet.class,
Deque.class,
Vector.class,
ArrayList.class);
private final Class<T> type;
private final String name;
private final Map<String, Argument<?>> typeParameters;
private final Argument[] typeParameterArray;
private final AnnotationMetadata annotationMetadata;
public DefaultArgument(Class<T> type, String name, AnnotationMetadata annotationMetadata, Argument... genericTypes) {
this(type,
name,
annotationMetadata,
ArrayUtils.isNotEmpty(genericTypes) ? initializeTypeParameters(genericTypes) : Collections.emptyMap(),
genericTypes
);
}
public DefaultArgument(Class<T> type, AnnotationMetadata annotationMetadata, Argument... genericTypes) {
this(type,
null,
annotationMetadata,
ArrayUtils.isNotEmpty(genericTypes) ? initializeTypeParameters(genericTypes) : Collections.emptyMap(),
genericTypes
);
}
public DefaultArgument(Class<T> type, String name, AnnotationMetadata annotationMetadata, Map<String, Argument<?>> typeParameters, Argument[] typeParameterArray) {
this.type = type;
this.name = name;
this.annotationMetadata = annotationMetadata != null ? annotationMetadata : AnnotationMetadata.EMPTY_METADATA;
this.typeParameters = typeParameters;
this.typeParameterArray = typeParameterArray;
}
public DefaultArgument(Type type, String name, AnnotationMetadata annotationMetadata) {
this.annotationMetadata = annotationMetadata != null ? annotationMetadata : AnnotationMetadata.EMPTY_METADATA;
if (type == null) {
type = getClass().getGenericSuperclass();
if (type instanceof ParameterizedType) {
type = ((ParameterizedType) type).getActualTypeArguments()[0];
} else {
throw new IllegalArgumentException(type + " is not parameterized");
}
}
if (type instanceof Class) {
this.type = (Class<T>) type;
this.typeParameterArray = Argument.ZERO_ARGUMENTS;
} else if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
this.type = (Class<T>) parameterizedType.getRawType();
TypeVariable<Class<T>>[] params = this.type.getTypeParameters();
Type[] paramValues = parameterizedType.getActualTypeArguments();
typeParameterArray = new Argument[params.length];
for (int i = 0; i < params.length; i++) {
TypeVariable param = params[i];
Type value = paramValues[i];
typeParameterArray[i] = new DefaultArgument(value, param.getName(), AnnotationMetadata.EMPTY_METADATA);
}
} else {
throw new IllegalArgumentException(type.getClass().getSimpleName() + " types are not supported");
}
if (name == null) {
name = this.type.getSimpleName();
}
this.name = name;
this.typeParameters = initializeTypeParameters(this.typeParameterArray);
}
@Override
public AnnotationMetadata getAnnotationMetadata() {
return annotationMetadata;
}
@Override
public Optional<Argument<?>> getFirstTypeVariable() {
if (!typeParameters.isEmpty()) {
return typeParameters.values().stream().findFirst();
}
return Optional.empty();
}
@Override
public Argument[] getTypeParameters() {
return typeParameterArray;
}
@Override
public Map<String, Argument<?>> getTypeVariables() {
return this.typeParameters;
}
@Override
public Class<T> getType() {
return type;
}
@Override
public String getName() {
if (name == null) {
return getType().getSimpleName();
}
return name;
}
@Override
public String toString() {
return type.getSimpleName() + " " + getName();
}
@Override
public boolean equalsType(Argument<?> o) {
if (this == o) {
return true;
}
if (o == null) {
return false;
}
return Objects.equals(type, o.getType()) &&
Objects.equals(typeParameters, o.getTypeVariables());
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof DefaultArgument)) {
return false;
}
DefaultArgument<?> that = (DefaultArgument<?>) o;
return Objects.equals(type, that.type) &&
Objects.equals(getName(), that.getName()) &&
Objects.equals(typeParameters, that.typeParameters);
}
@Override
public int typeHashCode() {
return Objects.hash(type, typeParameters);
}
@Override
public int hashCode() {
return Objects.hash(type, getName(), typeParameters);
}
private static Map<String, Argument<?>> initializeTypeParameters(Argument[] genericTypes) {
Map<String, Argument<?>> typeParameters;
if (genericTypes != null && genericTypes.length > 0) {
typeParameters = new LinkedHashMap<>(genericTypes.length);
for (Argument genericType : genericTypes) {
typeParameters.put(genericType.getName(), genericType);
}
} else {
typeParameters = Collections.emptyMap();
}
return typeParameters;
}
}