This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git

commit f736d4ebd7b8543058b11712994d72a30765e067
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Mon Sep 16 17:00:23 2024 +0200

    `GDALStoreProvider.probeContent(…)` should identify the driver without 
opening the dataset.
---
 .../main/org/apache/sis/storage/gdal/GDAL.java     |  12 +-
 .../org/apache/sis/storage/gdal/GDALStore.java     |  24 ++--
 .../apache/sis/storage/gdal/GDALStoreProvider.java |  13 +-
 .../main/org/apache/sis/storage/gdal/Opener.java   | 143 +++++++++------------
 4 files changed, 89 insertions(+), 103 deletions(-)

diff --git 
a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDAL.java
 
b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDAL.java
index aff1f1b0b3..1f7e6de681 100644
--- 
a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDAL.java
+++ 
b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDAL.java
@@ -87,6 +87,12 @@ final class GDAL extends NativeFunctions {
      */
     final MethodHandle getMetadataItem;
 
+    /**
+     * <abbr>GDAL</abbr> {@code GDALDriverH GDALIdentifyDriver(const char 
*pszFilename, CSLConstList papszFileList)}.
+     * Identify the driver that can open a dataset.
+     */
+    final MethodHandle identifyDriver;
+
     /**
      * <abbr>GDAL</abbr> {@code GDALDatasetH GDALOpenEx(const char 
*pszFilename, …)}.
      * Opens a raster or vector file by invoking the open method of each 
driver in turn.
@@ -275,6 +281,7 @@ final class GDAL extends NativeFunctions {
                 ValueLayout.ADDRESS));  // const char* domain
 
         // For Opener
+        identifyDriver = lookup(linker, "GDALIdentifyDriver", 
acceptTwoPtrsReturnPointer);
         free  = lookup(linker, "VSIFree",    
FunctionDescriptor.ofVoid(ValueLayout.ADDRESS));
         close = lookup(linker, "GDALClose",  acceptPointerReturnInt);
         open  = lookup(linker, "GDALOpenEx", 
FunctionDescriptor.of(ValueLayout.ADDRESS,
@@ -528,10 +535,13 @@ final class GDAL extends NativeFunctions {
      * This way to encode arrays of strings is specific to <abbr>GDAL</abbr>.
      *
      * @param  arena  the arena to use for memory allocation.
-     * @param  items  the Java strings to copy.
+     * @param  items  the Java strings to copy, or {@code null}.
      * @return the {@code NULL}-terminated array of string.
      */
     static MemorySegment toNullTerminatedStrings(final Arena arena, final 
String... items) {
+        if (items == null) {
+            return MemorySegment.NULL;
+        }
         final var layout = ValueLayout.ADDRESS;
         final MemorySegment array = arena.allocate(layout, items.length + 1);
         for (int i=0; i<items.length; i++) {
diff --git 
a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDALStore.java
 
b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDALStore.java
index c51c689141..3e9382ba92 100644
--- 
a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDALStore.java
+++ 
b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDALStore.java
@@ -153,13 +153,17 @@ public class GDALStore extends DataStore implements 
Aggregate, ResourceOnFileSys
             namespace = factory.createNameSpace(factory.createLocalName(null, 
filename), null);
         }
         final String[] drivers;
-        final Opener opener;
-        drivers  = connector.getOption(GDALStoreProvider.DRIVERS_OPTION_KEY);
-        path     = connector.getStorageAs(Path.class);
-        location = connector.commit(URI.class, GDALStoreProvider.NAME);
-        opener   = Opener.read(provider, Opener.toURL(location, path), 
drivers);
-        closer   = Cleaners.SHARED.register(this, opener);      // Must do now 
in case of exception before completion.
-        handle   = opener.handle;
+        drivers    = connector.getOption(GDALStoreProvider.DRIVERS_OPTION_KEY);
+        location   = connector.getStorageAs(URI.class);
+        path       = connector.getStorageAs(Path.class);
+        String url = connector.commit(String.class, GDALStoreProvider.NAME);
+        if (location != null) {
+            url = Opener.toURL(location, path);
+        }
+        Opener opener;
+        opener = new Opener(provider, url, drivers);
+        closer = Cleaners.SHARED.register(this, opener);    // Must do now in 
case of exception before completion.
+        handle = opener.handle;
     }
 
     /**
@@ -178,7 +182,7 @@ public class GDALStore extends DataStore implements 
Aggregate, ResourceOnFileSys
         path     = parent.path;
         location = parent.location;
         factory  = parent.factory;
-        opener   = Opener.read(getProvider(), url, driver);
+        opener   = new Opener(getProvider(), url, driver);
         closer   = Cleaners.SHARED.register(this, opener);      // Must do now 
in case of exception before completion.
         handle   = opener.handle;
     }
@@ -312,7 +316,7 @@ public class GDALStore extends DataStore implements 
Aggregate, ResourceOnFileSys
             if (subdatasets != null && !subdatasets.isEmpty()) {
                 components = subdatasets;
             } else {
-                components = 
UnmodifiableArrayList.wrap(TiledResource.groupBySizeAndType(this, gdal, 
handle));
+                components = 
UnmodifiableArrayList.wrap(TiledResource.groupBySizeAndType(this, gdal, 
handle()));
             }
         } finally {
             ErrorHandler.throwOnFailure(this, "components");
@@ -336,7 +340,7 @@ public class GDALStore extends DataStore implements 
Aggregate, ResourceOnFileSys
         final MemorySegment result;
         try (var arena = Arena.ofConfined()) {
             final MemorySegment domain = arena.allocateFrom("SUBDATASETS");
-            result = (MemorySegment) gdal.getMetadata.invokeExact(handle, 
domain);
+            result = (MemorySegment) gdal.getMetadata.invokeExact(handle(), 
domain);
         } catch (Throwable e) {
             throw GDAL.propagate(e);
         }
diff --git 
a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDALStoreProvider.java
 
b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDALStoreProvider.java
index d1f6c053cc..ff505bb631 100644
--- 
a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDALStoreProvider.java
+++ 
b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/GDALStoreProvider.java
@@ -16,7 +16,6 @@
  */
 package org.apache.sis.storage.gdal;
 
-import java.net.URI;
 import java.util.List;
 import java.util.Optional;
 import java.util.logging.Logger;
@@ -283,17 +282,7 @@ public class GDALStoreProvider extends DataStoreProvider {
      */
     @Override
     public ProbeResult probeContent(final StorageConnector connector) throws 
DataStoreException {
-        final URI url = connector.getStorageAs(URI.class);
-        if (url != null) {
-            final GDAL gdal = tryGDAL("probeContent").orElse(null);
-            if (gdal != null) {
-                try (Opener p = Opener.read(this, Opener.toURL(url, 
connector.getStorageAs(Path.class)))) {
-                    String mimeType = p.getMetadataItem(gdal, "DMD_MIMETYPE");
-                    return new ProbeResult(true, mimeType, null);
-                }
-            }
-        }
-        return ProbeResult.UNSUPPORTED_STORAGE;
+        return Opener.probeContent(this, connector);
     }
 
     /**
diff --git 
a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/Opener.java
 
b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/Opener.java
index af3dc8e7cf..4a2539745f 100644
--- 
a/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/Opener.java
+++ 
b/incubator/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/gdal/Opener.java
@@ -21,9 +21,9 @@ import java.util.Locale;
 import java.net.URI;
 import java.nio.file.Path;
 import java.lang.foreign.Arena;
-import java.lang.foreign.ValueLayout;
 import java.lang.foreign.MemorySegment;
 import org.apache.sis.util.privy.Constants;
+import org.apache.sis.storage.ProbeResult;
 import org.apache.sis.storage.StorageConnector;
 import org.apache.sis.storage.DataStoreException;
 
@@ -40,7 +40,7 @@ import org.apache.sis.storage.DataStoreException;
  * @author  Quentin Bialota (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
  */
-final class Opener implements Runnable, AutoCloseable {
+final class Opener implements Runnable {
     /**
      * Schemes of URLs to open with <var>GDAL</var> Virtual File Systems 
backed by <abbr>CURL</abbr>.
      */
@@ -54,11 +54,23 @@ final class Opener implements Runnable, AutoCloseable {
     private final GDALStoreProvider owner;
 
     /**
-     * Pointer to the <abbr>GDAL</abbr> object in native memory.
+     * Pointer to the <abbr>GDAL</abbr> object in native memory, or {@code 
null} if the file couldn't be opened.
      * This is a {@code GDALDatasetH} in the C/C++ <abbr>API</abbr>.
      */
     final MemorySegment handle;
 
+    /**
+     * Creates a new instance for read operations on the given file or 
<abbr>URL</abbr>.
+     *
+     * @param  owner           owner of the set of handles for invoking 
<abbr>GDAL</abbr> functions.
+     * @param  url             <abbr>URL</abbr> (<var>GDAL</var> syntax) of 
the data store to open.
+     * @param  allowedDrivers  short names (identifiers) of drivers that may 
be used, or {@code null} for any driver.
+     * @throws DataStoreException if <var>GDAL</var> cannot open the data set.
+     */
+    Opener(final GDALStoreProvider owner, final String url, final String... 
allowedDrivers) throws DataStoreException {
+        this(owner, url, new OpenFlag[] {OpenFlag.RASTER, OpenFlag.SHARED}, 
allowedDrivers, null, null);
+    }
+
     /**
      * Creates a new instance for the given file or <abbr>URL</abbr>.
      * The {@code options} argument is driver-specific, except for the
@@ -72,7 +84,7 @@ final class Opener implements Runnable, AutoCloseable {
      * </table>
      *
      * @param  owner           owner of the set of handles for invoking 
<abbr>GDAL</abbr> functions.
-     * @param  url             <abbr>URL</abbr> for <var>GDAL</var> of the 
data store to open.
+     * @param  url             <abbr>URL</abbr> (<var>GDAL</var> syntax) of 
the data store to open.
      * @param  openFlags       open flags to give to <abbr>GDAL</abbr>.
      * @param  allowedDrivers  short names (identifiers) of drivers that may 
be used, or {@code null} for any driver.
      * @param  driverOptions   driver-dependent options, or {@code null} if 
none.
@@ -92,29 +104,12 @@ final class Opener implements Runnable, AutoCloseable {
             handle = (MemorySegment) gdal.open.invokeExact(
                     arena.allocateFrom(url),
                     OpenFlag.mask(openFlags),
-                    allocateStringArray(arena, allowedDrivers),
-                    allocateStringArray(arena, driverOptions),
-                    allocateStringArray(arena, siblingFiles));
+                    GDAL.toNullTerminatedStrings(arena, allowedDrivers),
+                    GDAL.toNullTerminatedStrings(arena, driverOptions),
+                    GDAL.toNullTerminatedStrings(arena, siblingFiles));
         } catch (Throwable e) {
             throw GDAL.propagate(e);
         }
-        if (GDAL.isNull(handle)) {
-            throw new DataStoreException();     // TODO: get message from GDAL.
-        }
-    }
-
-    /**
-     * Creates a new instance for read operations on the given file or 
<abbr>URL</abbr>.
-     *
-     * @param  owner           owner of the set of handles for invoking 
<abbr>GDAL</abbr> functions.
-     * @param  url             <abbr>URL</abbr> for <var>GDAL</var> of the 
data store to open.
-     * @param  allowedDrivers  short names (identifiers) of drivers that may 
be used, or {@code null} for any driver.
-     * @throws DataStoreException if <var>GDAL</var> cannot open the data set.
-     */
-    static Opener read(final GDALStoreProvider owner, final String url, final 
String... allowedDrivers)
-            throws DataStoreException
-    {
-        return new Opener(owner, url, new OpenFlag[] {OpenFlag.RASTER, 
OpenFlag.SHARED}, allowedDrivers, null, null);
     }
 
     /**
@@ -139,56 +134,42 @@ final class Opener implements Runnable, AutoCloseable {
     }
 
     /**
-     * Allocates memory for an array of strings.
-     *
-     * @param  arena  the arena to allocate memory from.
-     * @param  values the array of strings to allocate memory for.
-     * @return the array of pointer, or {@link MemorySegment#NULL} if the 
array is {@code null} or empty.
-     */
-    private static MemorySegment allocateStringArray(final Arena arena, final 
String[] values) {
-        if (values == null || values.length == 0) {
-            return MemorySegment.NULL;
-        }
-        final MemorySegment array = arena.allocate(ValueLayout.ADDRESS, 
values.length);
-        for (int i=0; i < values.length; i++) {
-            array.setAtIndex(ValueLayout.ADDRESS, i, 
arena.allocateFrom(values[i]));
-        }
-        return array;
-    }
-
-    /**
-     * Returns a metadata item from the driver.
+     * Returns the MIME type if the given storage appears to be supported by 
this data store.
      *
-     * @param  gdal  set of handles for invoking <abbr>GDAL</abbr> functions.
-     * @param  name  the key for the metadata item to fetch.
-     * @return the metadata given by the driver, or {@code null} if 
unspecified.
+     * @param  owner      the provider which is probing the file.
+     * @param  connector  information about the storage (URL, stream, 
<i>etc</i>).
+     * @return a support status with the MIME type, or {@code null} if the 
given URL is unrecognized.
      * @throws DataStoreException if an error occurred while invoking a 
<abbr>GDAL</abbr> function.
      */
-    final String getMetadataItem(final GDAL gdal, final String name) throws 
DataStoreException {
-        try (var arena = Arena.ofConfined()) {
-            final MemorySegment driver = (MemorySegment) 
gdal.getDatasetDriver.invokeExact(handle);
-            if (!GDAL.isNull(driver)) {
-                MemorySegment n = arena.allocateFrom(name);
-                MemorySegment r = (MemorySegment) 
gdal.getMetadataItem.invokeExact(driver, n, MemorySegment.NULL);
-                return GDAL.toString(r);
+    static ProbeResult probeContent(final GDALStoreProvider owner, final 
StorageConnector connector)
+            throws DataStoreException
+    {
+        String url;
+        final URI location = connector.getStorageAs(URI.class);
+        if (location != null) {
+            url = toURL(location, connector.getStorageAs(Path.class));
+        } else {
+            url = connector.getStorageAs(String.class);
+        }
+        if (url != null) {
+            final GDAL gdal = owner.tryGDAL("probeContent").orElse(null);
+            if (gdal != null) {
+                try (var arena = Arena.ofConfined()) {
+                    final var driver = (MemorySegment) 
gdal.identifyDriver.invokeExact(
+                            arena.allocateFrom(url), MemorySegment.NULL);
+                    if (!GDAL.isNull(driver)) {
+                        MemorySegment mimeType = (MemorySegment) 
gdal.getMetadataItem.invokeExact(
+                                driver, arena.allocateFrom("DMD_MIMETYPE"), 
MemorySegment.NULL);
+                        return new ProbeResult(true, GDAL.toString(mimeType), 
null);
+                    }
+                } catch (Throwable e) {
+                    throw GDAL.propagate(e);
+                } finally {
+                    ErrorHandler.throwOnFailure(null, "probeContent");
+                }
             }
-        } catch (Throwable e) {
-            throw GDAL.propagate(e);
-        } finally {
-            ErrorHandler.throwOnFailure(null, "probeContent");
         }
-        return null;
-    }
-
-    /**
-     * Closes the <abbr>GDAL</abbr> data set. This method shall be invoked
-     * by {@link GDALStoreProvider#probeContent(StorageConnector)} only.
-     * {@link GDALStore} has its own close method.
-     */
-    @Override
-    public void close() throws DataStoreException {
-        run();
-        ErrorHandler.throwOnFailure(null, "probeContent");
+        return ProbeResult.UNSUPPORTED_STORAGE;
     }
 
     /**
@@ -198,16 +179,18 @@ final class Opener implements Runnable, AutoCloseable {
      */
     @Override
     public void run() {
-        owner.tryGDAL("close").ifPresent((gdal) -> {
-            final int err;
-            try {
-                err = (int) gdal.close.invokeExact(handle);
-            } catch (Throwable e) {
-                throw GDAL.propagate(e);
-            }
-            if (err != 0) {
-                ErrorHandler.errorOccurred(err);
-            }
-        });
+        if (handle != null) {
+            owner.tryGDAL("close").ifPresent((gdal) -> {
+                final int err;
+                try {
+                    err = (int) gdal.close.invokeExact(handle);
+                } catch (Throwable e) {
+                    throw GDAL.propagate(e);
+                }
+                if (err != 0) {
+                    ErrorHandler.errorOccurred(err);
+                }
+            });
+        }
     }
 }

Reply via email to