diff --git a/src/main/java/ch/systemsx/cisd/hdf5/CharacterEncoding.java b/src/main/java/ch/systemsx/cisd/hdf5/CharacterEncoding.java index 4430ee4..4405bdb 100644 --- a/src/main/java/ch/systemsx/cisd/hdf5/CharacterEncoding.java +++ b/src/main/java/ch/systemsx/cisd/hdf5/CharacterEncoding.java @@ -16,6 +16,7 @@ package ch.systemsx.cisd.hdf5; +import static ch.systemsx.cisd.hdf5.hdf5lib.HDF5NativeLibrary.intConstant; import static hdf.hdf5lib.HDF5Constants.H5T_CSET_ASCII; import static hdf.hdf5lib.HDF5Constants.H5T_CSET_UTF8; @@ -26,8 +27,8 @@ */ public enum CharacterEncoding { - ASCII("ASCII", H5T_CSET_ASCII, 1), - UTF8("UTF8", H5T_CSET_UTF8, 4); + ASCII("ASCII", intConstant(() -> H5T_CSET_ASCII), 1), + UTF8("UTF8", intConstant(() -> H5T_CSET_UTF8), 4); private final String charSetName; diff --git a/src/main/java/ch/systemsx/cisd/hdf5/HDF5DataClass.java b/src/main/java/ch/systemsx/cisd/hdf5/HDF5DataClass.java index f4ed798..58117ff 100644 --- a/src/main/java/ch/systemsx/cisd/hdf5/HDF5DataClass.java +++ b/src/main/java/ch/systemsx/cisd/hdf5/HDF5DataClass.java @@ -16,6 +16,7 @@ package ch.systemsx.cisd.hdf5; +import static ch.systemsx.cisd.hdf5.hdf5lib.HDF5NativeLibrary.intConstant; import static hdf.hdf5lib.HDF5Constants.H5T_BITFIELD; import static hdf.hdf5lib.HDF5Constants.H5T_COMPOUND; import static hdf.hdf5lib.HDF5Constants.H5T_ENUM; @@ -47,18 +48,23 @@ public enum HDF5DataClass { // Implementation note: The order matters! ENUM needs to be before INTEGER, as H5Tdetect_class // will return TRUE for ENUM arrays when trying to detect an INTEGER class. - BITFIELD(H5T_BITFIELD, new BasicJavaTypeProvider(BitSet.class, null, null, null)), ENUM( - H5T_ENUM, new BasicJavaTypeProvider(HDF5EnumerationValue.class, - HDF5EnumerationValueArray.class, null, null)), INTEGER(H5T_INTEGER, - new IntJavaTypeProvider()), FLOAT(H5T_FLOAT, new FloatJavaTypeProvider()), STRING( - H5T_STRING, new BasicJavaTypeProvider(String.class, String[].class, String[][].class, - MDArray.class)), OPAQUE(H5T_OPAQUE, new BasicJavaTypeProvider(byte.class, - byte[].class, byte[][].class, MDByteArray.class)), BOOLEAN(-1, - new BasicJavaTypeProvider(boolean.class, BitSet.class, null, null)), COMPOUND( - H5T_COMPOUND, new BasicJavaTypeProvider(Map.class, Map[].class, Map[][].class, - MDArray.class)), REFERENCE(H5T_REFERENCE, new BasicJavaTypeProvider( - String.class, String[].class, String[][].class, MDArray.class)), OTHER(-1, - new BasicJavaTypeProvider(null, null, null, null)); + BITFIELD(intConstant(() -> H5T_BITFIELD), + new BasicJavaTypeProvider(BitSet.class, null, null, null)), + ENUM(intConstant(() -> H5T_ENUM), + new BasicJavaTypeProvider(HDF5EnumerationValue.class, HDF5EnumerationValueArray.class, + null, null)), + INTEGER(intConstant(() -> H5T_INTEGER), new IntJavaTypeProvider()), + FLOAT(intConstant(() -> H5T_FLOAT), new FloatJavaTypeProvider()), + STRING(intConstant(() -> H5T_STRING), + new BasicJavaTypeProvider(String.class, String[].class, String[][].class, MDArray.class)), + OPAQUE(intConstant(() -> H5T_OPAQUE), + new BasicJavaTypeProvider(byte.class, byte[].class, byte[][].class, MDByteArray.class)), + BOOLEAN(-1, new BasicJavaTypeProvider(boolean.class, BitSet.class, null, null)), + COMPOUND(intConstant(() -> H5T_COMPOUND), + new BasicJavaTypeProvider(Map.class, Map[].class, Map[][].class, MDArray.class)), + REFERENCE(intConstant(() -> H5T_REFERENCE), + new BasicJavaTypeProvider(String.class, String[].class, String[][].class, MDArray.class)), + OTHER(-1, new BasicJavaTypeProvider(null, null, null, null)); /** * A role that can provide a java type for a data class, rank and element size. diff --git a/src/main/java/ch/systemsx/cisd/hdf5/HDF5EnumerationType.java b/src/main/java/ch/systemsx/cisd/hdf5/HDF5EnumerationType.java index 8549396..17d595e 100644 --- a/src/main/java/ch/systemsx/cisd/hdf5/HDF5EnumerationType.java +++ b/src/main/java/ch/systemsx/cisd/hdf5/HDF5EnumerationType.java @@ -16,6 +16,7 @@ package ch.systemsx.cisd.hdf5; +import static ch.systemsx.cisd.hdf5.hdf5lib.HDF5NativeLibrary.longConstant; import static hdf.hdf5lib.HDF5Constants.H5T_NATIVE_INT16; import static hdf.hdf5lib.HDF5Constants.H5T_NATIVE_INT32; import static hdf.hdf5lib.HDF5Constants.H5T_NATIVE_INT8; @@ -43,15 +44,15 @@ public enum EnumStorageForm /** * One byte, for up to 255 alternatives. */ - BYTE(1, H5T_NATIVE_INT8, H5T_STD_U8LE), + BYTE(1, longConstant(() -> H5T_NATIVE_INT8), longConstant(() -> H5T_STD_U8LE)), /** * Two bytes, for up to 65535 alternatives. */ - SHORT(2, H5T_NATIVE_INT16, H5T_STD_U16LE), + SHORT(2, longConstant(() -> H5T_NATIVE_INT16), longConstant(() -> H5T_STD_U16LE)), /** * Four bytes, for more than 65535 alternatives. */ - INT(4, H5T_NATIVE_INT32, H5T_STD_U32LE); + INT(4, longConstant(() -> H5T_NATIVE_INT32), longConstant(() -> H5T_STD_U32LE)); private final byte storageSize; diff --git a/src/main/java/ch/systemsx/cisd/hdf5/HDF5StorageLayout.java b/src/main/java/ch/systemsx/cisd/hdf5/HDF5StorageLayout.java index 80d385d..dabc121 100644 --- a/src/main/java/ch/systemsx/cisd/hdf5/HDF5StorageLayout.java +++ b/src/main/java/ch/systemsx/cisd/hdf5/HDF5StorageLayout.java @@ -1,5 +1,6 @@ package ch.systemsx.cisd.hdf5; +import static ch.systemsx.cisd.hdf5.hdf5lib.HDF5NativeLibrary.intConstant; import hdf.hdf5lib.HDF5Constants; /** @@ -9,8 +10,8 @@ */ public enum HDF5StorageLayout { - COMPACT(HDF5Constants.H5D_COMPACT), CONTIGUOUS(HDF5Constants.H5D_CONTIGUOUS), CHUNKED( - HDF5Constants.H5D_CHUNKED), NOT_APPLICABLE(-1); + COMPACT(intConstant(() -> HDF5Constants.H5D_COMPACT)), CONTIGUOUS(intConstant(() -> HDF5Constants.H5D_CONTIGUOUS)), CHUNKED( + intConstant(() -> HDF5Constants.H5D_CHUNKED)), NOT_APPLICABLE(-1); private int id; diff --git a/src/main/java/ch/systemsx/cisd/hdf5/IHDF5WriterConfigurator.java b/src/main/java/ch/systemsx/cisd/hdf5/IHDF5WriterConfigurator.java index d7452bc..69eb31c 100644 --- a/src/main/java/ch/systemsx/cisd/hdf5/IHDF5WriterConfigurator.java +++ b/src/main/java/ch/systemsx/cisd/hdf5/IHDF5WriterConfigurator.java @@ -16,6 +16,7 @@ package ch.systemsx.cisd.hdf5; +import static ch.systemsx.cisd.hdf5.hdf5lib.HDF5NativeLibrary.intConstant; import hdf.hdf5lib.HDF5Constants; /** @@ -71,22 +72,22 @@ public enum FileFormatVersion /** * The library will create objects with the earliest possible format versions. */ - EARLIEST(HDF5Constants.H5F_LIBVER_EARLIEST, false), + EARLIEST(intConstant(() -> HDF5Constants.H5F_LIBVER_EARLIEST), false), /** * The library will allow objects to be created with the latest format versions available to the current library release version. */ - LATEST(HDF5Constants.H5F_LIBVER_LATEST, true), + LATEST(intConstant(() -> HDF5Constants.H5F_LIBVER_LATEST), true), /** * The library will allow objects to be created with the latest format versions available to HDF5 v1.8. */ - V1_8(HDF5Constants.H5F_LIBVER_V18, false), + V1_8(intConstant(() -> HDF5Constants.H5F_LIBVER_V18), false), /** * The library will allow objects to be created with the latest format versions available to HDF5 v1.10. */ - V1_10(HDF5Constants.H5F_LIBVER_V110, true); + V1_10(intConstant(() -> HDF5Constants.H5F_LIBVER_V110), true); private final int hdf5Constant; diff --git a/src/main/java/ch/systemsx/cisd/hdf5/hdf5lib/HDF5NativeLibrary.java b/src/main/java/ch/systemsx/cisd/hdf5/hdf5lib/HDF5NativeLibrary.java index b07d1a9..14e6e49 100644 --- a/src/main/java/ch/systemsx/cisd/hdf5/hdf5lib/HDF5NativeLibrary.java +++ b/src/main/java/ch/systemsx/cisd/hdf5/hdf5lib/HDF5NativeLibrary.java @@ -1,5 +1,8 @@ package ch.systemsx.cisd.hdf5.hdf5lib; +import java.util.function.IntSupplier; +import java.util.function.LongSupplier; + import org.bytedeco.hdf5.hdf5_java; import org.bytedeco.javacpp.Loader; @@ -18,6 +21,11 @@ * public entry points ({@code HDF5Factory}, {@code HDF5FactoryProvider}, {@link HDFHelper}) call it * from their static initializers. Code that touches {@code hdf.hdf5lib.H5} directly before any of * those should call {@link #load()} itself first. + *

+ * Enum constants that are initialized from {@code HDF5Constants} need the same guarantee, but they + * are created before any static initializer of their enum runs, and their constructor arguments + * are evaluated before their constructor. They read the constant through {@link #intConstant} or + * {@link #longConstant} instead, which load the library first. */ public final class HDF5NativeLibrary { @@ -43,12 +51,38 @@ public static synchronized void load() { return; } - loaded = true; - if (isSet(H5_PATH_PROPERTY) || isSet(H5_NAME_PROPERTY)) + if (isSet(H5_PATH_PROPERTY) == false && isSet(H5_NAME_PROPERTY) == false) { - return; + // Loader.load() returns null when JavaCPP's library loading is disabled. + final String path = Loader.load(hdf5_java.class); + if (path != null) + { + System.setProperty(H5_PATH_PROPERTY, path); + } } - System.setProperty(H5_PATH_PROPERTY, Loader.load(hdf5_java.class)); + // Only now, so that a failed attempt (e.g. no natives for this platform) is retried and + // reported again by the next caller, instead of surfacing later as an obscure + // UnsatisfiedLinkError from H5's static initializer. + loaded = true; + } + + /** + * Returns {@code constant.getAsInt()}, after {@link #load()}. For initializing enum constants + * from {@code HDF5Constants}, e.g. {@code COMPACT(intConstant(() -> H5D_COMPACT))}. + */ + public static int intConstant(IntSupplier constant) + { + load(); + return constant.getAsInt(); + } + + /** + * Returns {@code constant.getAsLong()}, after {@link #load()}. See {@link #intConstant}. + */ + public static long longConstant(LongSupplier constant) + { + load(); + return constant.getAsLong(); } private static boolean isSet(String key)