|
| 1 | +package io.arex.agent.bootstrap.model; |
| 2 | + |
| 3 | +import java.lang.reflect.MalformedParameterizedTypeException; |
| 4 | +import java.lang.reflect.ParameterizedType; |
| 5 | +import java.lang.reflect.Type; |
| 6 | +import java.lang.reflect.TypeVariable; |
| 7 | +import java.util.Arrays; |
| 8 | +import java.util.Objects; |
| 9 | + |
| 10 | +/** |
| 11 | + * ref: <a |
| 12 | + * href="https://github.com/openjdk/jdk17/blob/master/src/java.base/share/classes/sun/reflect/generics/reflectiveObjects/ParameterizedTypeImpl.java"> |
| 13 | + * sun/reflect/generics/reflectiveObjects/ParameterizedTypeImpl.java |
| 14 | + * </a> |
| 15 | + */ |
| 16 | +public class ParameterizedTypeImpl implements ParameterizedType { |
| 17 | + private final Type[] actualTypeArguments; |
| 18 | + private final Class<?> rawType; |
| 19 | + private final Type ownerType; |
| 20 | + |
| 21 | + private ParameterizedTypeImpl(Class<?> rawType, |
| 22 | + Type[] actualTypeArguments, |
| 23 | + Type ownerType) { |
| 24 | + this.actualTypeArguments = actualTypeArguments; |
| 25 | + this.rawType = rawType; |
| 26 | + this.ownerType = (ownerType != null) ? ownerType : rawType.getDeclaringClass(); |
| 27 | + validateConstructorArguments(); |
| 28 | + } |
| 29 | + |
| 30 | + private void validateConstructorArguments() { |
| 31 | + TypeVariable<?>[] formals = rawType.getTypeParameters(); |
| 32 | + // check correct arity of actual type args |
| 33 | + if (formals.length != actualTypeArguments.length){ |
| 34 | + throw new MalformedParameterizedTypeException(); |
| 35 | + } |
| 36 | + for (int i = 0; i < actualTypeArguments.length; i++) { |
| 37 | + // check actuals against formals' bounds |
| 38 | + } |
| 39 | + } |
| 40 | + |
| 41 | + /** |
| 42 | + * Static factory. Given a (generic) class, actual type arguments |
| 43 | + * and an owner type, creates a parameterized type. |
| 44 | + * This class can be instantiated with a a raw type that does not |
| 45 | + * represent a generic type, provided the list of actual type |
| 46 | + * arguments is empty. |
| 47 | + * If the ownerType argument is null, the declaring class of the |
| 48 | + * raw type is used as the owner type. |
| 49 | + * <p> This method throws a MalformedParameterizedTypeException |
| 50 | + * under the following circumstances: |
| 51 | + * If the number of actual type arguments (i.e., the size of the |
| 52 | + * array <tt>typeArgs</tt>) does not correspond to the number of |
| 53 | + * formal type arguments. |
| 54 | + * If any of the actual type arguments is not an instance of the |
| 55 | + * bounds on the corresponding formal. |
| 56 | + * @param rawType the Class representing the generic type declaration being |
| 57 | + * instantiated |
| 58 | + * @param actualTypeArguments - a (possibly empty) array of types |
| 59 | + * representing the actual type arguments to the parameterized type |
| 60 | + * @param ownerType - the enclosing type, if known. |
| 61 | + * @return An instance of <tt>ParameterizedType</tt> |
| 62 | + * @throws MalformedParameterizedTypeException - if the instantiation |
| 63 | + * is invalid |
| 64 | + */ |
| 65 | + public static ParameterizedTypeImpl make(Class<?> rawType, |
| 66 | + Type[] actualTypeArguments, |
| 67 | + Type ownerType) { |
| 68 | + return new ParameterizedTypeImpl(rawType, actualTypeArguments, |
| 69 | + ownerType); |
| 70 | + } |
| 71 | + |
| 72 | + |
| 73 | + /** |
| 74 | + * Returns an array of <tt>Type</tt> objects representing the actual type |
| 75 | + * arguments to this type. |
| 76 | + * |
| 77 | + * <p>Note that in some cases, the returned array be empty. This can occur |
| 78 | + * if this type represents a non-parameterized type nested within |
| 79 | + * a parameterized type. |
| 80 | + * |
| 81 | + * @return an array of <tt>Type</tt> objects representing the actual type |
| 82 | + * arguments to this type |
| 83 | + * @throws <tt>TypeNotPresentException</tt> if any of the |
| 84 | + * actual type arguments refers to a non-existent type declaration |
| 85 | + * @throws <tt>MalformedParameterizedTypeException</tt> if any of the |
| 86 | + * actual type parameters refer to a parameterized type that cannot |
| 87 | + * be instantiated for any reason |
| 88 | + * @since 1.5 |
| 89 | + */ |
| 90 | + public Type[] getActualTypeArguments() { |
| 91 | + return actualTypeArguments.clone(); |
| 92 | + } |
| 93 | + |
| 94 | + /** |
| 95 | + * Returns the <tt>Type</tt> object representing the class or interface |
| 96 | + * that declared this type. |
| 97 | + * |
| 98 | + * @return the <tt>Type</tt> object representing the class or interface |
| 99 | + * that declared this type |
| 100 | + */ |
| 101 | + public Class<?> getRawType() { |
| 102 | + return rawType; |
| 103 | + } |
| 104 | + |
| 105 | + |
| 106 | + /** |
| 107 | + * Returns a <tt>Type</tt> object representing the type that this type |
| 108 | + * is a member of. For example, if this type is <tt>O<T>.I<S></tt>, |
| 109 | + * return a representation of <tt>O<T></tt>. |
| 110 | + * |
| 111 | + * <p>If this type is a top-level type, <tt>null</tt> is returned. |
| 112 | + * |
| 113 | + * @return a <tt>Type</tt> object representing the type that |
| 114 | + * this type is a member of. If this type is a top-level type, |
| 115 | + * <tt>null</tt> is returned |
| 116 | + * @throws <tt>TypeNotPresentException</tt> if the owner type |
| 117 | + * refers to a non-existent type declaration |
| 118 | + * @throws <tt>MalformedParameterizedTypeException</tt> if the owner type |
| 119 | + * refers to a parameterized type that cannot be instantiated |
| 120 | + * for any reason |
| 121 | + * |
| 122 | + */ |
| 123 | + public Type getOwnerType() { |
| 124 | + return ownerType; |
| 125 | + } |
| 126 | + |
| 127 | + /* |
| 128 | + * From the JavaDoc for java.lang.reflect.ParameterizedType |
| 129 | + * "Instances of classes that implement this interface must |
| 130 | + * implement an equals() method that equates any two instances |
| 131 | + * that share the same generic type declaration and have equal |
| 132 | + * type parameters." |
| 133 | + */ |
| 134 | + @Override |
| 135 | + public boolean equals(Object o) { |
| 136 | + if (o instanceof ParameterizedType) { |
| 137 | + // Check that information is equivalent |
| 138 | + ParameterizedType that = (ParameterizedType) o; |
| 139 | + |
| 140 | + if (this == that) |
| 141 | + return true; |
| 142 | + |
| 143 | + Type thatOwner = that.getOwnerType(); |
| 144 | + Type thatRawType = that.getRawType(); |
| 145 | + |
| 146 | + if (false) { // Debugging |
| 147 | + boolean ownerEquality = (ownerType == null ? |
| 148 | + thatOwner == null : |
| 149 | + ownerType.equals(thatOwner)); |
| 150 | + boolean rawEquality = (rawType == null ? |
| 151 | + thatRawType == null : |
| 152 | + rawType.equals(thatRawType)); |
| 153 | + |
| 154 | + boolean typeArgEquality = Arrays.equals(actualTypeArguments, // avoid clone |
| 155 | + that.getActualTypeArguments()); |
| 156 | + for (Type t : actualTypeArguments) { |
| 157 | + System.out.printf("\t\t%s%s%n", t, t.getClass()); |
| 158 | + } |
| 159 | + |
| 160 | + System.out.printf("\towner %s\traw %s\ttypeArg %s%n", |
| 161 | + ownerEquality, rawEquality, typeArgEquality); |
| 162 | + return ownerEquality && rawEquality && typeArgEquality; |
| 163 | + } |
| 164 | + |
| 165 | + return |
| 166 | + Objects.equals(ownerType, thatOwner) && |
| 167 | + Objects.equals(rawType, thatRawType) && |
| 168 | + Arrays.equals(actualTypeArguments, // avoid clone |
| 169 | + that.getActualTypeArguments()); |
| 170 | + } else |
| 171 | + return false; |
| 172 | + } |
| 173 | + |
| 174 | + @Override |
| 175 | + public int hashCode() { |
| 176 | + return |
| 177 | + Arrays.hashCode(actualTypeArguments) ^ |
| 178 | + Objects.hashCode(ownerType) ^ |
| 179 | + Objects.hashCode(rawType); |
| 180 | + } |
| 181 | + |
| 182 | + public String toString() { |
| 183 | + StringBuilder sb = new StringBuilder(); |
| 184 | + |
| 185 | + if (ownerType != null) { |
| 186 | + if (ownerType instanceof Class) |
| 187 | + sb.append(((Class)ownerType).getName()); |
| 188 | + else |
| 189 | + sb.append(ownerType.toString()); |
| 190 | + |
| 191 | + sb.append("$"); |
| 192 | + |
| 193 | + if (ownerType instanceof ParameterizedTypeImpl) { |
| 194 | + // Find simple name of nested type by removing the |
| 195 | + // shared prefix with owner. |
| 196 | + sb.append(rawType.getName().replace( ((ParameterizedTypeImpl)ownerType).rawType.getName() + "$", |
| 197 | + "")); |
| 198 | + } else |
| 199 | + sb.append(rawType.getSimpleName()); |
| 200 | + } else |
| 201 | + sb.append(rawType.getName()); |
| 202 | + |
| 203 | + if (actualTypeArguments != null && |
| 204 | + actualTypeArguments.length > 0) { |
| 205 | + sb.append("<"); |
| 206 | + boolean first = true; |
| 207 | + for(Type t: actualTypeArguments) { |
| 208 | + if (!first) |
| 209 | + sb.append(", "); |
| 210 | + sb.append(t.getTypeName()); |
| 211 | + first = false; |
| 212 | + } |
| 213 | + sb.append(">"); |
| 214 | + } |
| 215 | + |
| 216 | + return sb.toString(); |
| 217 | + } |
| 218 | +} |
0 commit comments