constructors = getConstructorsForClass(clazz, params);
return () -> constructors.instantiate();
}
/**
* @return true if clazz represents an abstract class, false otherwise
*/
public static boolean isAbstractClass(final Class> clazz) {
return Modifier.isAbstract(clazz.getModifiers());
}
/**
* Checks if the given class implements a given interface.
*
* The check also checks whether a superclass implements the interface.
*
* @param clazzIn
* the class object to check
* @return true if and only if clazzIn
implements interfaceClazz
. Otherwise false.
*/
public static boolean isClassImplementingInterface(final Class> clazzIn, final Class> interfaceClazz) {
if (interfaceClazz == null || !interfaceClazz.isInterface()) {
throw new IllegalArgumentException("interfaceClazz does not represent an interface");
}
Class> clazz = clazzIn;
while (clazz != null) {
final Class>[] implementedInterfaces = clazz.getInterfaces();
for (final Class> interFace : implementedInterfaces) {
if (interFace.equals(interfaceClazz)) {
// class implements IOperation in a transitive chain
return true;
}
}
clazz = clazz.getSuperclass();
}
return false;
}
public static boolean isSubclassOfClass(final Class> clazzIn, final Class> superClazz) {
if (superClazz == null) {
throw new IllegalArgumentException("superClazz is null");
}
if (Modifier.isFinal(superClazz.getModifiers())) {
return false;
}
if (clazzIn == null) {
return true;
}
return superClazz.isAssignableFrom(clazzIn);
}
public static File getClassFolder(final Class> clazz, final UrlConverter resourceConverter) {
final String name = clazz.getPackage().getName();
final ClassLoader loader = getClassLoader(clazz);
final URL url = loader.getResource(name);
return tryConvertUrlToFile(loader, url, resourceConverter);
}
public static List instanceFields(Class extends Object> clazz) {
final List fields = new ArrayList<>();
while (clazz.getSuperclass() != null) {
// we don't want to process Object
Arrays.stream(clazz.getDeclaredFields()).filter(ReflectionUtil::isIncluded).forEach(fields::add);
clazz = clazz.getSuperclass();
}
return fields;
}
public static List instanceFields(final Object obj) {
if (obj == null) {
return Collections.emptyList();
}
final Class extends Object> clazz = obj.getClass();
return instanceFields(clazz);
}
public static Map instanceName2Fields(final Class extends Object> clazz) {
return instanceFields(clazz).stream().collect(Collectors.toMap(Field::getName, f -> f));
}
public static Map instanceName2Fields(final Object obj) {
if (obj == null) {
return Collections.emptyMap();
}
final Class extends Object> clazz = obj.getClass();
return instanceName2Fields(clazz);
}
/**
* Return a string of the form "name=value, " for all variables and their values from the instance represented by
* obj, excluding fields of the {@link Object} type itself, fields whose name starts with $, and fields that are
* marked with a {@link Reflected#excluded()} annotation.
*/
public static String instanceFieldsToString(final Object obj) {
if (obj == null) {
return "NULL";
}
final List fields = instanceFields(obj);
return fields.stream().filter(a -> !a.getName().startsWith("$")).filter(a -> !isExcluded(a))
.map(a -> fieldToString(obj, a)).collect(Collectors.joining(", "));
}
private static boolean isExcluded(final Field f) {
final Reflected annot = f.getAnnotation(Reflected.class);
return annot != null && annot.excluded();
}
private static boolean isIncluded(final Field f) {
return !isExcluded(f);
}
public static String fieldPrettyName(final Field f) {
final Reflected annot = f.getAnnotation(Reflected.class);
if (annot != null && !"".equals(annot.prettyName())) {
return annot.prettyName();
}
return f.getName();
}
public static String fieldToString(final Object obj, final Field f) {
String val;
try {
f.setAccessible(true);
val = String.valueOf(f.get(obj));
} catch (final IllegalArgumentException e) {
val = "IArE";
} catch (final IllegalAccessException e) {
val = "IAcE";
}
return String.format("%s=%s", fieldPrettyName(f), val);
}
public static Object access(final Object obj, final Field f) {
try {
f.setAccessible(true);
return f.get(obj);
} catch (final IllegalArgumentException e) {
throw new UnsupportedOperationException(e);
} catch (final IllegalAccessException e) {
throw new UnsupportedOperationException(e);
}
}
public static String printableStackTrace() {
return Arrays.toString(Thread.currentThread().getStackTrace());
}
/**
* Create a UrlConverter taken from the core to handle bundle resources.
*
* If the core is not present, we do not need it as no OSGi loading can take place.
*/
private static UrlConverter createBundleResourceConverter() {
final String qualifiedName = "de.uni_freiburg.informatik.ultimate.core.util.RcpUtils";
try {
final List loaders = getClassLoaders(ReflectionUtil.class);
for (final ClassLoader loader : loaders) {
final Class> clazz = getClassFromQualifiedName(qualifiedName, loader);
if (clazz == null) {
continue;
}
final Method method = clazz.getMethod("getBundleProtocolResolver");
return (UrlConverter) method.invoke(null);
}
throw new ReflectionUtilException("Could not extract Class> from qualified name " + qualifiedName);
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException
| InvocationTargetException | ReflectionUtilException e) {
return null;
}
}
private static ConstructorAndRemainingParameters getConstructorsForClass(final Class clazz,
final Object... params) {
if (clazz == null) {
throw new IllegalArgumentException("clazz is null");
}
try {
@SuppressWarnings("unchecked")
final Constructor[] constructors = (Constructor[]) clazz.getDeclaredConstructors();
if (constructors.length == 0) {
throw new ReflectionUtilException(
"Cannot instantiate class " + clazz.toString() + " because it has no constructors");
}
final Object[] newParams;
if (clazz.isMemberClass() && !Modifier.isStatic(clazz.getModifiers())) {
// this class is a non-static inner class of another one
final Class> enclosingClazz = clazz.getEnclosingClass();
final ConstructorAndRemainingParameters> enclosingClazzConstructor =
getConstructorsForClass(enclosingClazz, params);
final Object enclosingClazzInstance = enclosingClazzConstructor.instantiate();
final Object[] remainingParams = enclosingClazzConstructor.mUnusedParameters;
if (remainingParams == null || remainingParams.length == 0) {
newParams = new Object[] { enclosingClazzInstance };
} else {
newParams = new Object[remainingParams.length + 1];
newParams[0] = enclosingClazzInstance;
System.arraycopy(remainingParams, 0, newParams, 1, remainingParams.length);
}
} else {
newParams = params;
}
// filter all constructors which are valid and require less or equal the amount of parameters we have
final List> constructorCandidates = Arrays.stream(constructors)
.filter(a -> a.getParameterCount() <= newParams.length).collect(Collectors.toList());
if (constructorCandidates.isEmpty()) {
throw new ReflectionUtilException("Cannot instantiate class " + clazz.toString()
+ " because there is no constructor that takes " + newParams.length + " arguments");
}
// we will try and select the constructor which matches the most parameters that we have
int maxMatch = -1;
Constructor candidateConstructor = null;
for (final Constructor current : constructorCandidates) {
final Parameter[] parameters = current.getParameters();
int matched = 0;
for (final Parameter parameter : parameters) {
final Class> availableType = parameter.getType();
if (newParams[matched] == null && !availableType.isPrimitive()) {
// this should match
} else {
final Class> wrappedAvailableType = toWrapperClazz(availableType);
final Class> suppliedType = newParams[matched].getClass();
if (!wrappedAvailableType.isAssignableFrom(suppliedType)) {
matched = -1;
break;
}
}
matched++;
}
if (matched > maxMatch) {
maxMatch = matched;
candidateConstructor = current;
}
}
if (candidateConstructor == null) {
throw new ReflectionUtilException(
"Cannot instantiate class " + clazz.toString() + " because I did not find a valid constructor");
}
final Object[] usedParams = new Object[maxMatch];
final Object[] remainingParams = new Object[newParams.length - maxMatch];
System.arraycopy(newParams, 0, usedParams, 0, maxMatch);
System.arraycopy(newParams, maxMatch, remainingParams, 0, newParams.length - maxMatch);
return new ConstructorAndRemainingParameters<>(candidateConstructor, clazz, usedParams, remainingParams);
} catch (final SecurityException e) {
throw new ReflectionUtilException(
"Cannot instantiate class " + clazz.toString() + " because I am not allowed to access it", e);
} catch (final IllegalArgumentException e) {
throw new AssertionError(
"Cannot instantiate class " + clazz.toString() + " because the parameters do not match", e);
}
}
/**
* Return the filenames of the files specified by the given resource URL. We use the classloader to get the URL of
* this folder. We support only URLs with protocol file or bundleresource.
*/
private static Collection getFilesFromDirectoryResource(final ClassLoader loader, final URL url,
final UrlConverter resourceConverter) {
final File dirFile = tryConvertUrlToFile(loader, url, resourceConverter);
if (dirFile == null) {
return Collections.emptyList();
}
return CoreUtil.flattenDirectories(Collections.singleton(dirFile));
}
private static File tryConvertUrlToFile(final ClassLoader loader, final URL url,
final UrlConverter resourceConverter) {
final String protocol = url.getProtocol();
if ("file".equals(protocol)) {
try {
return new File(url.toURI());
} catch (final URISyntaxException e) {
return null;
}
}
if (!"bundleresource".equals(protocol)) {
throw new UnsupportedOperationException("unknown protocol " + protocol);
}
if (resourceConverter == null) {
throw new AssertionError("Someone supplied a bundleresource resource but we do not have a converter -- "
+ "check if this deployable is built correctly "
+ "(maybe de.uni_freiburg.informatik.ultimate.core is missing?)");
}
try {
final URL fileUrl = resourceConverter.convert(url);
return new File(fileUrl.getFile());
} catch (final IOException e) {
return null;
}
}
private static Class> getClassFromFile(final String packageName, final File file,
final Collection loaders) {
final String qualifiedName = getQualifiedNameFromFile(packageName, file);
if (loaders.isEmpty()) {
return getClassFromQualifiedName(qualifiedName);
}
for (final ClassLoader loader : loaders) {
final Class> clazz = getClassFromQualifiedName(qualifiedName, loader);
if (clazz != null) {
return clazz;
}
}
throw new ReflectionUtilException("Could not extract Class> from qualified name " + qualifiedName);
}
/**
* Create a {@link Class} instance from a fully qualified name
*/
private static Class> getClassFromQualifiedName(final String qualifiedName) {
try {
return Class.forName(qualifiedName);
} catch (final ClassNotFoundException e) {
throw new ReflectionUtilException("Could not extract Class> from qualified name " + qualifiedName, e);
}
}
/**
* Create a {@link Class} instance from a fully qualified name and a given classloader. Do not throw an exception,
* but let the caller do that.
*
* @param loader
*/
private static Class> getClassFromQualifiedName(final String qualifiedName, final ClassLoader loader) {
try {
return Class.forName(qualifiedName, true, loader);
} catch (final ClassNotFoundException e) {
return null;
}
}
/**
* Tries to resolve the fully qualified name from the package name and the found file. If the package is a.b.c.d and
* we found a class with the path /foo/bar/a/b/c/d/huh/OurClass.class, then the fully qualified name is
* a.b.c.d.huh.OurClass
*/
private static String getQualifiedNameFromFile(final String packageName, final File file) {
assert file != null;
assert file.getName().endsWith(".class");
final String fullname = file.getAbsolutePath();
final String filenameWithoutSuffix = fullname.substring(0, fullname.length() - 6);
final String knownPath = getPathFromPackageName(packageName);
final int validAfter = filenameWithoutSuffix.indexOf(knownPath);
assert validAfter != -1;
return filenameWithoutSuffix.substring(validAfter).replace(File.separatorChar, '.');
}
private static String getPathFromPackageName(final String packageName) {
return packageName.replace(".", File.separator);
}
private static ClassLoader getClassLoader(final Class> clazz) {
ClassLoader loader = clazz.getClassLoader();
if (loader == null) {
// Try the bootstrap classloader - obtained from the ultimate parent of the System Class Loader.
loader = ClassLoader.getSystemClassLoader();
while (loader != null && loader.getParent() != null) {
loader = loader.getParent();
}
}
return loader;
}
private static List getClassLoaders(final Class> clazz) {
final List rtr = new ArrayList<>();
ClassLoader loader = clazz.getClassLoader();
while (loader != null) {
rtr.add(loader);
loader = loader.getParent();
}
// Try the bootstrap classloader - obtained from the ultimate parent of the System Class Loader.
loader = ClassLoader.getSystemClassLoader();
while (loader != null) {
rtr.add(loader);
loader = loader.getParent();
}
return rtr;
}
/**
* Return the contents of the folder from which this class was loaded (i.e., the package). You can supply your own
* URL converter for different kinds of protocols or null; if you supply null, {@link ReflectionUtil} will try and
* find one for you.
*/
private static Collection getFolderContentsFromClass(final Class> clazz,
final UrlConverter resourceConverter) {
if (clazz == null) {
return null;
}
final ClassLoader loader = getClassLoader(clazz);
if (loader == null) {
return Collections.emptyList();
}
final String packageName = clazz.getPackage().getName();
final String packagePath = getPathFromPackageName(packageName);
final Enumeration resourceUrlsIter;
try {
resourceUrlsIter = loader.getResources(packagePath);
} catch (final IOException e) {
throw new ReflectionUtilException(
"Classloader " + loader.toString() + " could not load resource " + packagePath, e);
}
final List rtr = new ArrayList<>();
while (resourceUrlsIter.hasMoreElements()) {
final URL resourceUrl = resourceUrlsIter.nextElement();
rtr.addAll(getFilesFromDirectoryResource(loader, resourceUrl, resourceConverter));
}
return rtr;
}
private static Class> toWrapperClazz(final Class> clazz) {
if (!clazz.isPrimitive()) {
return clazz;
}
if (clazz == Integer.TYPE) {
return Integer.class;
}
if (clazz == Long.TYPE) {
return Long.class;
}
if (clazz == Boolean.TYPE) {
return Boolean.class;
}
if (clazz == Byte.TYPE) {
return Byte.class;
}
if (clazz == Character.TYPE) {
return Character.class;
}
if (clazz == Float.TYPE) {
return Float.class;
}
if (clazz == Double.TYPE) {
return Double.class;
}
if (clazz == Short.TYPE) {
return Short.class;
}
if (clazz == Void.TYPE) {
return Void.class;
}
throw new UnsupportedOperationException("Not yet implemented: wrapper for " + clazz);
}
private static final class ConstructorAndRemainingParameters {
private final Constructor mConstructor;
private final Class mClazz;
private final Object[] mMatchedParameters;
private final Object[] mUnusedParameters;
private ConstructorAndRemainingParameters(final Constructor constructor, final Class clazz,
final Object[] matchedParameters, final Object[] remainingParameters) {
mConstructor = constructor;
mClazz = clazz;
mMatchedParameters = matchedParameters;
mUnusedParameters = remainingParameters;
}
public T instantiate() {
assert mConstructor.getParameterCount() == mMatchedParameters.length : "Wrong length";
try {
return mConstructor.newInstance(mMatchedParameters);
} catch (final SecurityException | InstantiationException | IllegalAccessException e) {
throw new ReflectionUtilException(
"Cannot instantiate class " + mClazz.toString() + " because I am not allowed to access it", e);
} catch (final IllegalArgumentException e) {
throw new AssertionError(
"Cannot instantiate class " + mClazz.toString() + " because the parameters do not match", e);
} catch (final InvocationTargetException e) {
throw new ReflectionUtilException(
"Cannot instantiate class " + mClazz.toString() + " because the constructor threw an exception",
e);
}
}
}
/**
* A custom security manager that exposes the getClassContext() information
*/
private static final class ExposedSecurityManager extends SecurityManager {
public Class> getCallerClass(final int callStackDepth) {
return getClassContext()[callStackDepth];
}
}
/**
* A {@link RuntimeException} that will be thrown for unsuccessful operations of {@link ReflectionUtil}.
*
* @author Daniel Dietsch (dietsch@informatik.uni-freiburg.de)
*
*/
private static final class ReflectionUtilException extends RuntimeException {
private static final long serialVersionUID = -5821955867584671607L;
public ReflectionUtilException(final String message) {
super(message);
}
public ReflectionUtilException(final String message, final Throwable cause) {
super(message, cause);
}
}
/**
* Interface that allows me to indirectly instantiate a converter without adding the dependency explicitly.
*
* @author Daniel Dietsch (dietsch@informatik.uni-freiburg.de)
*
*/
@FunctionalInterface
public interface UrlConverter {
URL convert(URL url) throws IOException;
}
/**
* Annotation that prevents a field from being listed in {@link ReflectionUtil#instanceFields(Object)},
* {@link ReflectionUtil#instanceName2Fields(Object)}, and {@link ReflectionUtil#instanceFieldsToString(Object)}.
*
* @author Daniel Dietsch (dietsch@informatik.uni-freiburg.de)
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Reflected {
boolean excluded() default false;
String prettyName() default "";
}
}