TypeUtils.java
/*
* Copyright © 2025 Indiana University
* All rights reserved.
*
* BSD 3-Clause License
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* - Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package iu.type;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Executable;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import edu.iu.UnsafeRunnable;
import edu.iu.UnsafeSupplier;
import edu.iu.type.IuType;
/**
* Miscellaneous type introspection utilities.
*/
final class TypeUtils {
/**
* Gets the context class loader appropriate for a given annotated element.
*
* @param element annotated element
* @return class loader that loaded the element
*/
static ClassLoader getContext(AnnotatedElement element) {
if (element instanceof Class)
return ((Class<?>) element).getClassLoader();
else if (element instanceof Executable)
return ((Executable) element).getDeclaringClass().getClassLoader();
else if (element instanceof Field)
return ((Field) element).getDeclaringClass().getClassLoader();
else if (element instanceof Parameter)
return ((Parameter) element).getDeclaringExecutable().getDeclaringClass().getClassLoader();
else
throw new UnsupportedOperationException("Cannot determine context for " + element);
}
/**
* Invokes an {@link UnsafeSupplier} using a specific context class loader.
*
* @param <T> return type
* @param contextLoader {@link ClassLoader}
* @param supplier {@link UnsafeSupplier}
* @return result of {@link UnsafeSupplier#get()}
*
* @throws Throwable from {@link UnsafeSupplier#get()}
*/
static <T> T callWithContext(ClassLoader contextLoader, UnsafeSupplier<T> supplier) throws Throwable {
if (contextLoader == null)
contextLoader = ClassLoader.getPlatformClassLoader();
var current = Thread.currentThread();
var loader = current.getContextClassLoader();
try {
current.setContextClassLoader(contextLoader);
return supplier.get();
} finally {
current.setContextClassLoader(loader);
}
}
/**
* Invokes an {@link UnsafeRunnable} using a specific context class loader.
*
* @param contextLoader {@link ClassLoader}
* @param runnable {@link UnsafeRunnable}
*
* @throws Throwable from {@link UnsafeRunnable#run()}
*/
static void callWithContext(ClassLoader contextLoader, UnsafeRunnable runnable) throws Throwable {
if (contextLoader == null)
contextLoader = ClassLoader.getPlatformClassLoader();
var current = Thread.currentThread();
var loader = current.getContextClassLoader();
try {
current.setContextClassLoader(contextLoader);
runnable.run();
} finally {
current.setContextClassLoader(loader);
}
}
/**
* Invokes an {@link UnsafeSupplier} using a context appropriate for an
* annotated element.
*
* @param <T> return type
* @param element {@link AnnotatedElement}
* @param supplier {@link UnsafeSupplier}
* @return result of {@link UnsafeSupplier#get()}
*
* @throws Throwable from {@link UnsafeSupplier#get()}
*/
static <T> T callWithContext(AnnotatedElement element, UnsafeSupplier<T> supplier) throws Throwable {
return callWithContext(getContext(element), supplier);
}
/**
* Prints a generic type.
*
* @param type generic type
* @return human-readable form
*/
static String printType(Type type) {
if (type instanceof Class) {
var c = (Class<?>) type;
if (c.isArray())
return printType(c.componentType()) + "[]";
else
return c.getSimpleName();
} else if (type instanceof GenericArrayType) {
var genericArrayType = (GenericArrayType) type;
var genericComponentType = genericArrayType.getGenericComponentType();
return printType(genericComponentType) + "[]";
} else if (type instanceof ParameterizedType) {
var parameterizedType = (ParameterizedType) type;
StringBuilder sb = new StringBuilder(printType(parameterizedType.getRawType()));
sb.append('<');
var l = sb.length();
for (var actualTypeArgument : parameterizedType.getActualTypeArguments()) {
if (sb.length() > l)
sb.append(',');
sb.append(printType(actualTypeArgument));
}
sb.append('>');
return sb.toString();
} else
return type.toString();
}
/**
* Refers to a type in a hierarchy, for internal use by {@link TypeTemplate} and
* {@link TypeFacade}.
*
* @param <T> referrer type
* @param referrerType referrer type facade
* @param hierarchy referrer's type hierarchy
* @param referentType generic referent type to match
*
* @return inherited type facade with same erasure as the {@code referentType}
*/
static <T> IuType<?, ? super T> referTo(IuType<?, T> referrerType, Iterable<TypeFacade<?, ? super T>> hierarchy,
Type referentType) {
var erasedClass = TypeFactory.getErasedClass(referentType);
if (erasedClass == referrerType.erasedClass())
return referrerType;
for (var superType : hierarchy)
if (superType.erasedClass() == erasedClass)
return superType;
throw new IllegalArgumentException(
printType(referentType) + " not present in type hierarchy for " + referrerType + "; " + hierarchy);
}
private TypeUtils() {
}
}