graphql.kickstart.tools.util.ParameterizedTypeImpl Maven / Gradle / Ivy
/*
* Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package graphql.kickstart.tools.util;
import java.lang.reflect.MalformedParameterizedTypeException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Arrays;
import java.util.Objects;
import java.util.StringJoiner;
/**
* Implementing class for ParameterizedType interface.
*/
public class ParameterizedTypeImpl implements ParameterizedType {
private final Type[] actualTypeArguments;
private final Class rawType;
private final Type ownerType;
private ParameterizedTypeImpl(
Class rawType, Type[] actualTypeArguments, Type ownerType
) {
this.actualTypeArguments = actualTypeArguments;
this.rawType = rawType;
this.ownerType = (ownerType != null) ? ownerType : rawType.getDeclaringClass();
validateConstructorArguments();
}
private void validateConstructorArguments() {
TypeVariable[] formals = rawType.getTypeParameters();
// check correct arity of actual type args
if (formals.length != actualTypeArguments.length) {
throw new MalformedParameterizedTypeException();
}
for (int i = 0; i < actualTypeArguments.length; i++) {
// TODO check actual against formal bounds
}
}
/**
* Static factory. Given a (generic) class, actual type arguments and an owner type, creates a parameterized type. This
* class can be instantiated with a raw type that does not represent a generic type, provided the list of actual type
* arguments is empty. If the ownerType argument is null, the declaring class of the raw type is used as the owner type.
* This method throws a MalformedParameterizedTypeException
* under the following circumstances: If the number of actual type arguments (i.e., the size of the array {@code
* typeArgs}) does not correspond to the number of formal type arguments. If any of the actual type arguments is not an
* instance of the bounds on the corresponding formal.
*
* @param rawType the Class representing the generic type declaration being instantiated
* @param actualTypeArguments a (possibly empty) array of types representing the actual type arguments to the
* parameterized type
* @param ownerType the enclosing type, if known.
* @return An instance of {@code ParameterizedType}
* @throws MalformedParameterizedTypeException if the instantiation is invalid
*/
public static ParameterizedTypeImpl make(Class rawType, Type[] actualTypeArguments, Type ownerType) {
return new ParameterizedTypeImpl(rawType, actualTypeArguments, ownerType);
}
/**
* Returns an array of {@code Type} objects representing the actual type arguments to this type.
*
Note that in some cases, the returned array be empty. This can occur
* if this type represents a non-parameterized type nested within a parameterized type.
*
* @return an array of {@code Type} objects representing the actual type arguments to this type
* @throws TypeNotPresentException if any of the actual type arguments refers to a non-existent type
* declaration
* @throws MalformedParameterizedTypeException if any of the actual type parameters refer to a parameterized type that
* cannot be instantiated for any reason
* @since 1.5
*/
public Type[] getActualTypeArguments() {
return actualTypeArguments.clone();
}
/**
* Returns the {@code Type} object representing the class or interface that declared this type.
*
* @return the {@code Type} object representing the class or interface that declared this type
*/
public Class getRawType() {
return rawType;
}
/**
* Returns a {@code Type} object representing the type that this type is a member of. For example, if this type is
* {@code O.I}, return a representation of {@code O}.
* If this type is a top-level type, {@code null} is returned.
*
* @return a {@code Type} object representing the type that this type is a member of. If this type is a top-level type,
* {@code null} is returned
* @throws TypeNotPresentException if the owner type refers to a non-existent type declaration
* @throws MalformedParameterizedTypeException if the owner type refers to a parameterized type that cannot be
* instantiated for any reason
*/
public Type getOwnerType() {
return ownerType;
}
/*
* From the JavaDoc for java.lang.reflect.ParameterizedType
* "Instances of classes that implement this interface must
* implement an equals() method that equates any two instances
* that share the same generic type declaration and have equal
* type parameters."
*/
@Override
public boolean equals(Object o) {
if (o instanceof ParameterizedType) {
// Check that information is equivalent
ParameterizedType that = (ParameterizedType) o;
if (this == that) {
return true;
}
Type thatOwner = that.getOwnerType();
Type thatRawType = that.getRawType();
if (false) { // Debugging
boolean ownerEquality = (ownerType == null ? thatOwner == null : ownerType.equals(thatOwner));
boolean rawEquality = (rawType == null ? thatRawType == null : rawType.equals(thatRawType));
boolean typeArgEquality = Arrays.equals(actualTypeArguments, // avoid clone
that.getActualTypeArguments());
for (Type t : actualTypeArguments) {
System.out.printf("\t\t%s%s%n", t, t.getClass());
}
System.out.printf("\towner %s\traw %s\ttypeArg %s%n", ownerEquality, rawEquality, typeArgEquality);
return ownerEquality && rawEquality && typeArgEquality;
}
return Objects.equals(ownerType, thatOwner) && Objects.equals(rawType, thatRawType) && Arrays.equals(actualTypeArguments, // avoid clone
that.getActualTypeArguments());
} else {
return false;
}
}
@Override
public int hashCode() {
return Arrays.hashCode(actualTypeArguments) ^ Objects.hashCode(ownerType) ^ Objects.hashCode(rawType);
}
public String toString() {
StringBuilder sb = new StringBuilder();
if (ownerType != null) {
sb.append(ownerType.getTypeName());
sb.append("$");
if (ownerType instanceof ParameterizedTypeImpl) {
// Find simple name of nested type by removing the
// shared prefix with owner.
sb.append(rawType.getName().replace(((ParameterizedTypeImpl) ownerType).rawType.getName() + "$", ""));
} else {
sb.append(rawType.getSimpleName());
}
} else {
sb.append(rawType.getName());
}
if (actualTypeArguments != null) {
StringJoiner sj = new StringJoiner(", ", "<", ">");
sj.setEmptyValue("");
for (Type t : actualTypeArguments) {
sj.add(t.getTypeName());
}
sb.append(sj.toString());
}
return sb.toString();
}
}