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 796ddbbf0407444efed6d9f32b2882ac218e047a Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Mon Apr 4 11:22:58 2022 +0200 Search for auxiliary files using Path in addition or URI. Those two kinds of object does not always open the same streams. --- .../apache/sis/internal/storage/PRJDataStore.java | 79 ++++++++++++++-------- .../apache/sis/internal/storage/URIDataStore.java | 59 +++++++++++++--- 2 files changed, 97 insertions(+), 41 deletions(-) diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/PRJDataStore.java b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/PRJDataStore.java index 0a5a54846a..4c04ec072a 100644 --- a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/PRJDataStore.java +++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/PRJDataStore.java @@ -18,13 +18,13 @@ package org.apache.sis.internal.storage; import java.net.URL; import java.io.IOException; +import java.io.InputStream; import java.io.InputStreamReader; import java.io.FileNotFoundException; import java.nio.charset.Charset; -import java.nio.file.FileSystemNotFoundException; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; +import java.nio.file.NoSuchFileException; import java.text.ParseException; import java.util.Arrays; import java.util.Locale; @@ -137,7 +137,7 @@ public abstract class PRJDataStore extends URIDataStore { crs = (CoordinateReferenceSystem) format.parseObject(wkt); format.validate(crs); } - } catch (FileNotFoundException e) { + } catch (NoSuchFileException | FileNotFoundException e) { listeners.warning(Resources.format(Resources.Keys.CanNotReadAuxiliaryFile_1, PRJ), e); } catch (IOException | ParseException | ClassCastException e) { throw new DataStoreException(Resources.format(Resources.Keys.CanNotReadAuxiliaryFile_1, PRJ), e); @@ -153,18 +153,37 @@ public abstract class PRJDataStore extends URIDataStore { * @param extension the filename extension of the auxiliary file to open. * @param encoding the encoding to use for reading the file content, or {@code null} for default. * @return a stream opened on the specified file, or {@code null} if the file is not found. - * @throws FileNotFoundException if the auxiliary file has not been found. + * @throws NoSuchFileException if the auxiliary file has not been found (when opened from path). + * @throws FileNotFoundException if the auxiliary file has not been found (when opened from URL). * @throws IOException if another error occurred while opening the stream. */ protected final String readAuxiliaryFile(final String extension, Charset encoding) throws IOException { - final URL url = IOUtilities.toAuxiliaryURL(location, extension); - if (url == null) { - return null; - } if (encoding == null) { encoding = Charset.defaultCharset(); } - try (InputStreamReader reader = new InputStreamReader(url.openStream(), encoding)) { + /* + * Try to open the stream using the storage type (Path or URL) closest to the type + * given at construction time. We do that because those two types can not open the + * same streams. For example Path does not open HTTP or FTP connections by default, + * and URL does not open S3 files in current implementation. + */ + final InputStream stream; + Path path = getSpecifiedPath(); + if (path != null) { + final String base = getBaseFilename(path); + path = path.resolveSibling(base.concat(PRJ)); + stream = Files.newInputStream(path); + } else { + final URL url = IOUtilities.toAuxiliaryURL(location, extension); + if (url == null) { + return null; + } + stream = url.openStream(); + } + /* + * Reads the auxiliary file fully. + */ + try (InputStreamReader reader = new InputStreamReader(stream, encoding)) { char[] buffer = new char[1024]; int offset = 0, count; while ((count = reader.read(buffer, offset, buffer.length - offset)) >= 0) { @@ -202,29 +221,29 @@ public abstract class PRJDataStore extends URIDataStore { * @throws DataStoreException if the URI can not be converted to a {@link Path}. */ protected final Path[] listComponentFiles(final String... auxiliaries) throws DataStoreException { - final Path path; - if (location == null) { - return new Path[0]; - } else try { - path = Paths.get(location); - } catch (IllegalArgumentException | FileSystemNotFoundException e) { - throw new DataStoreException(e); - } - String base = path.getFileName().toString(); - final int s = base.lastIndexOf('.'); - if (s >= 0) { - base = base.substring(0, s+1); - } - final Path[] paths = new Path[auxiliaries.length + 1]; - paths[0] = path; - int count = 1; - for (final String extension : auxiliaries) { - final Path p = path.resolveSibling(base.concat(extension)); - if (Files.isRegularFile(p)) { - paths[count++] = p; + Path[] paths = super.getComponentFiles(); + int count = paths.length; + if (count != 0) { + final Path path = paths[0]; + final String base = getBaseFilename(path); + for (final String extension : auxiliaries) { + final Path p = path.resolveSibling(base.concat(extension)); + if (Files.isRegularFile(p)) { + if (count >= paths.length) { + paths = Arrays.copyOf(paths, count + auxiliaries.length); + } + paths[count++] = p; + } } + paths = ArraysExt.resize(paths, count); } - return ArraysExt.resize(paths, count); + return paths; + } + + private static String getBaseFilename(final Path path) { + final String base = path.getFileName().toString(); + final int s = base.lastIndexOf('.'); + return (s >= 0) ? base.substring(0, s+1) : base + '.'; } /** diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/URIDataStore.java b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/URIDataStore.java index 181f44b4ee..ce4faf81d0 100644 --- a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/URIDataStore.java +++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/URIDataStore.java @@ -16,6 +16,7 @@ */ package org.apache.sis.internal.storage; +import java.io.File; import java.net.URI; import java.util.Optional; import java.nio.file.Path; @@ -54,6 +55,25 @@ public abstract class URIDataStore extends DataStore implements StoreResource, R */ protected final URI location; + /** + * The {@link #location} as a path, computed when first needed. + * If the storage given at construction time was a {@link Path} or a {@link File} instance, + * then this field is initialized in the constructor in order to avoid a "path → URI → path" roundtrip + * (such roundtrip transforms relative paths into {@linkplain Path#toAbsolutePath() absolute paths}). + * + * @see #getSpecifiedPath() + * @see #getComponentFiles() + */ + private volatile Path locationAsPath; + + /** + * Whether {@link #locationAsPath} was initialized at construction time ({@code true}) + * of inferred from the {@link #location} URI at a later time ({@code false}). + * + * @see #getSpecifiedPath() + */ + private final boolean locationIsPath; + /** * Creates a new data store. * @@ -64,6 +84,22 @@ public abstract class URIDataStore extends DataStore implements StoreResource, R protected URIDataStore(final DataStoreProvider provider, final StorageConnector connector) throws DataStoreException { super(provider, connector); location = connector.getStorageAs(URI.class); + final Object storage = connector.getStorage(); + if (storage instanceof Path) { + locationAsPath = (Path) storage; + } else if (storage instanceof File) { + locationAsPath = ((File) storage).toPath(); + } + locationIsPath = (locationAsPath != null); + } + + /** + * If the location was specified as a {@link Path} or {@link File} instance, returns that path. + * Otherwise returns {@code null}. This method does not try to convert URI to {@link Path} + * because this conversion may fail for HTTP and FTP connections. + */ + final Path getSpecifiedPath() { + return locationIsPath ? locationAsPath : null; } /** @@ -78,23 +114,24 @@ public abstract class URIDataStore extends DataStore implements StoreResource, R /** * Returns the {@linkplain #location} as a {@code Path} component or an empty array if none. - * The default implementation converts the URI to a {@link Path}. Note that if the original - * {@link StorageConnector} argument given by user at construction time already contained a - * {@link Path}, then the {@code Path} → {@code URI} → {@code Path} roundtrip is equivalent - * to returning the {@link Path#toAbsolutePath()} value of user argument. + * The default implementation returns the storage specified at construction time if it was + * a {@link Path} or {@link File}, or converts the URI to a {@link Path} otherwise. * * @return the URI as a path, or an empty array if the URI is null. * @throws DataStoreException if the URI can not be converted to a {@link Path}. */ @Override public Path[] getComponentFiles() throws DataStoreException { - final Path path; - if (location == null) { - return new Path[0]; - } else try { - path = Paths.get(location); - } catch (IllegalArgumentException | FileSystemNotFoundException e) { - throw new DataStoreException(e); + Path path = locationAsPath; + if (path == null) { + if (location == null) { + return new Path[0]; + } else try { + path = Paths.get(location); + } catch (IllegalArgumentException | FileSystemNotFoundException e) { + throw new DataStoreException(e); + } + locationAsPath = path; } return new Path[] {path}; }