OptionalTypes.java
package tools.jackson.datatype.fastutil.util;
/**
* Helper for registering handlers of fastutil types that may not be available at
* runtime: {@code fastutil-core}, a smaller alternative to the full {@code fastutil}
* jar, only includes some of the types (for example no {@code boolean} or {@code byte}
* collections, and no maps with {@code float} keys).
*<p>
* Class literals ({@code IntSet.class}) and method references
* ({@code IntOpenHashSet::new}) load the referenced classes when evaluated, so all
* code that references such types needs to be in the body of the action passed to
* {@link #registerIfPresent}: the action itself (a lambda that only references the
* types of this module) can always be created.
*<p>
* Internal API: not exported from the module.
*/
public final class OptionalTypes
{
private OptionalTypes() { }
private final static String FASTUTIL_PACKAGE_INTERNAL = "it/unimi/dsi/fastutil/";
// Class included in both `fastutil` and `fastutil-core`
private final static String FASTUTIL_PROBE_CLASS = "it.unimi.dsi.fastutil.Hash";
// All code that references fastutil types registers them through this class first,
// so it is enough to ensure readability when this class is initialized
static {
_ensureFastutilReadable();
}
/**
* Module descriptor can only declare a dependency to one of {@code fastutil}
* (automatic module {@code it.unimi.dsi.fastutil}) or {@code fastutil-core}
* ({@code it.unimi.dsi.fastutil.core}): if the latter is used on the module path,
* this module would not read it, and accessing any fastutil class would fail.
* So add the read edge to whichever module fastutil classes are loaded from.
*/
private static void _ensureFastutilReadable()
{
final Module self = OptionalTypes.class.getModule();
if (!self.isNamed()) { // class path: no need
return;
}
final Class<?> probe;
try {
probe = Class.forName(FASTUTIL_PROBE_CLASS, false, OptionalTypes.class.getClassLoader());
} catch (ClassNotFoundException | LinkageError e) {
return; // no fastutil at all: nothing will be registered
}
self.addReads(probe.getModule());
}
/**
* Runs given registration action, unless it fails because a fastutil class is missing.
* Other linkage failures (a missing class of another library, a failing static
* initializer) are not expected, and are rethrown instead of hiding broken setup.
*
* @return Whether action completed, that is, whether all types it uses are available
*/
public static boolean registerIfPresent(Runnable registration)
{
try {
registration.run();
return true;
} catch (NoClassDefFoundError e) {
if (_isMissingFastutilClass(e)) {
return false;
}
throw e;
} catch (BootstrapMethodError e) {
// method references to missing classes fail during linkage
if (e.getCause() instanceof NoClassDefFoundError ncdfe
&& _isMissingFastutilClass(ncdfe)) {
return false;
}
throw e;
}
}
// JVM reports missing class as "it/unimi/dsi/fastutil/..."; a class that is present
// but failed to initialize is reported as "Could not initialize class ..." instead
private static boolean _isMissingFastutilClass(NoClassDefFoundError e) {
final String msg = e.getMessage();
return (msg != null) && msg.startsWith(FASTUTIL_PACKAGE_INTERNAL);
}
}