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 53a36ee267887d253dd95f924747847a940815d6 Author: Martin Desruisseaux <[email protected]> AuthorDate: Thu Feb 1 13:22:29 2024 +0100 Reduce the amount of magic in `IOUtilities` relative to the conversion from URL to Path. This is consequence of URL constuctors being deprecated in Java 20. If URLs can only be created via URI, a consequence is that the encoding must be UTF-8 and some `IOUtilities` methods become obsolete. --- .../org/apache/sis/io/stream/ChannelFactory.java | 5 +- .../main/org/apache/sis/io/stream/IOUtilities.java | 206 ++++----------------- .../org/apache/sis/io/stream/IOUtilitiesTest.java | 76 +------- 3 files changed, 46 insertions(+), 241 deletions(-) diff --git a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/ChannelFactory.java b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/ChannelFactory.java index 38e1d64089..b0fa01ae17 100644 --- a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/ChannelFactory.java +++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/ChannelFactory.java @@ -31,6 +31,7 @@ import java.io.IOException; import java.net.URI; import java.net.URL; import java.net.MalformedURLException; +import java.net.URISyntaxException; import java.nio.file.Path; import java.nio.file.Files; import java.nio.file.InvalidPathException; @@ -174,8 +175,8 @@ public abstract class ChannelFactory { */ if (storage instanceof URL) { try { - storage = IOUtilities.toPath((URL) storage, encoding); - } catch (IOException e) { + storage = Path.of(((URL) storage).toURI()); + } catch (IllegalArgumentException | URISyntaxException | FileSystemNotFoundException e) { /* * This is normal if the URL uses HTTP or FTP protocol for instance. Log the exception at FINE * level without stack trace. We will open the channel later using the URL instead of the Path. diff --git a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/IOUtilities.java b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/IOUtilities.java index f3d786aca5..d03c9b5ad0 100644 --- a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/IOUtilities.java +++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/io/stream/IOUtilities.java @@ -27,6 +27,7 @@ import java.io.DataOutput; import java.io.InputStream; import java.io.OutputStream; import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URL; import java.net.URLDecoder; @@ -314,7 +315,7 @@ public final class IOUtilities extends Static { * @param path the path to encode, or {@code null}. * @return the encoded path, or {@code null} if and only if the given path was null. */ - public static String encodeURI(final String path) { + static String encodeURI(final String path) { if (path == null) { return null; } @@ -354,161 +355,6 @@ public final class IOUtilities extends Static { return (buffer != null) ? buffer.toString() : path; } - /** - * Converts a path specified as a character string to an URL. - * This method can be used as a replacement for the deprecated {@link URL} constructors. - * - * @param url the path to convert, or {@code null}. - * @param encoding if the URL is encoded in a {@code application/x-www-form-urlencoded} MIME format, - * the character encoding (normally {@code "UTF-8"}). - * If the URL is not encoded, then {@code null}. - * @return the path converted to an uRL, or {@code null} if the given path was null. - * @throws MalformedURLException if the path cannot be parsed as an URL. - * @throws IOException if a non-null {@code encoding} was specified and an encoding error is found. - */ - public static URL toURL(String url, final String encoding) throws IOException { - if (url == null) { - return null; - } - if (encoding != null) { - url = URLDecoder.decode(url, encoding); - } - url = encodeURI(url); - try { - return new URI(url).parseServerAuthority().toURL(); - } catch (IllegalArgumentException | URISyntaxException cause) { - throw (MalformedURLException) new MalformedURLException(malformed(url, cause)).initCause(cause); - } - } - - /** - * Converts a {@link URL} to a {@link URI}. This is equivalent to a call to the standard {@link URL#toURI()} - * method, except for the following functionalities: - * - * <ul> - * <li>Optionally decodes the {@code "%XX"} sequences, where {@code "XX"} is a number.</li> - * <li>Escape spaces and a some other reserved characters.</li> - * <li>Converts exceptions into subclasses of {@link IOException}.</li> - * </ul> - * - * @param url the URL to convert, or {@code null}. - * @param encoding if the URL is encoded in a {@code application/x-www-form-urlencoded} MIME format, - * the character encoding (normally {@code "UTF-8"}). If the URL is not encoded, - * then {@code null}. - * @return the URI for the given URL, or {@code null} if the given URL was null. - * @throws IOException if the URL cannot be converted to a URI. - * - * @see URI#URI(String) - * @see URL#toURI() - */ - public static URI toURI(final URL url, final String encoding) throws IOException { - if (url == null) { - return null; - } - /* - * Convert the URL to a URI, taking in account the encoding if any. We want to escape - * spaces with `encodeURI(…)` before to convert. Invoking `URL.toURI()` is preferable - * to `new URI(String)` because the former performs more checks, but is possible only - * if the decoding and escaping resulted in no change. - */ - final String specified = url.toExternalForm(); - String path = specified; - if (encoding != null) { - path = URLDecoder.decode(path, encoding); - } - path = encodeURI(path); - try { - return path.equals(specified) ? url.toURI() : new URI(path); - } catch (URISyntaxException cause) { - /* - * Occurs only if the URL is not compliant with RFC 2396. Otherwise every URL - * should succeed, so a failure can actually be considered as a malformed URL. - */ - throw (MalformedURLException) new MalformedURLException(malformed(url, cause)).initCause(cause); - } - } - - /** - * Converts a {@link URL} to a {@link File}. This is equivalent to a call to the standard - * {@link URL#toURI()} method followed by a call to the {@link File#File(URI)} constructor, - * except that exceptions are converted to {@link IOException}: - * - * @param url the URL to convert, or {@code null}. - * @return the file for the given URL, or {@code null} if the given URL was null. - * @throws IOException if the URL cannot be converted to a file. - * - * @see File#File(URI) - */ - public static File toFile(final URL url) throws IOException { - if (url == null) { - return null; - } else try { - return new File(url.toURI()); - } catch (IllegalArgumentException | URISyntaxException cause) { - /* - * Typically happen when the URI scheme is not "file". But may also happen if the - * URI contains fragment that cannot be represented in a File (e.g. a Query part). - * The IllegalArgumentException does not allow us to distinguish those cases. - */ - throw new IOException(malformed(url, cause), cause); - } - } - - /** - * Prepares a message for a malformed URL. - * - * @param url the malformed URL. - * @param cause the exception thrown. - */ - private static String malformed(final Object url, final Exception cause) { - return Exceptions.formatChainedMessages(null, - Errors.format(Errors.Keys.IllegalArgumentValue_2, "URL", url), cause); - } - - /** - * Converts a {@link URL} to a {@link Path}. This is equivalent to a call to the standard - * {@link URL#toURI()} method followed by a call to the {@link Path#of(URI)} static method, - * except for the following functionalities: - * - * <ul> - * <li>Optionally decodes the {@code "%XX"} sequences, where {@code "XX"} is a number.</li> - * <li>Converts various exceptions into subclasses of {@link IOException}.</li> - * </ul> - * - * @param url the URL to convert, or {@code null}. - * @param encoding if the URL is encoded in a {@code application/x-www-form-urlencoded} MIME format, - * the character encoding (normally {@code "UTF-8"}). If the URL is not encoded, - * then {@code null}. - * @return the path for the given URL, or {@code null} if the given URL was null. - * @throws IOException if the URL cannot be converted to a path. - * - * @see Path#of(URI) - */ - public static Path toPath(final URL url, final String encoding) throws IOException { - if (url == null) { - return null; - } - final URI uri = toURI(url, encoding); - try { - return Path.of(uri); - } catch (IllegalArgumentException | FileSystemNotFoundException cause) { - final String message = malformed(url, cause); - /* - * If the exception is IllegalArgumentException, then the URI scheme has been recognized - * but the URI syntax is illegal for that file system. So we can consider that the URL is - * malformed in regard to the rules of that particular file system. - */ - final IOException e; - if (cause instanceof IllegalArgumentException) { - e = new MalformedURLException(message); - e.initCause(cause); - } else { - e = new IOException(message, cause); - } - throw e; - } - } - /** * Parses the following path as a {@link File} if possible, or a {@link URL} otherwise. * In the special case where the given {@code path} is a URL using the {@code "file"} protocol, @@ -521,13 +367,15 @@ public final class IOUtilities extends Static { * * @param path the path to convert, or {@code null}. * @param encoding if the URL is encoded in a {@code application/x-www-form-urlencoded} MIME format, - * the character encoding (normally {@code "UTF-8"}). If the URL is not encoded, - * then {@code null}. This argument is ignored if the given path does not need - * to be converted from URL to {@code File}. - * @return the path as a {@link File} if possible, or a {@link URL} otherwise. - * @throws IOException if the given path is not a file and cannot be parsed as a URL. + * the character encoding (normally {@code "UTF-8"}). If the URL is not encoded, then {@code null}. + * This argument is ignored if the given path does not need to be converted from URL to {@code File}. + * @return the path as a {@link File} if possible, or as a {@link URL} otherwise. + * @throws UnsupportedEncodingException if the specified encoding is invalid. + * @throws MalformedURLException if the given path is not a file and cannot be parsed as a URL. */ - public static Object toFileOrURL(final String path, final String encoding) throws IOException { + public static Object toFileOrURL(String path, final String encoding) + throws UnsupportedEncodingException, MalformedURLException + { if (path == null) { return null; } @@ -546,17 +394,31 @@ public final class IOUtilities extends Static { return new File(path); } } - final URL url = toURL(path, encoding); - final String scheme = url.getProtocol(); - if (scheme != null && scheme.equalsIgnoreCase("file")) { - return toFile(url); + if (encoding != null) { + path = URLDecoder.decode(path, encoding); // Replace sequences of the form "%xy". } - /* - * Leave the URL in its original encoding on the assumption that this is the encoding expected by - * the server. This is different than the policy for URI, because the latter are always in UTF-8. - * If a URI is needed, callers should use toURI(url, encoding). - */ - return url; + path = encodeURI(path); // Re-encode spaces and a few other characters. + MalformedURLException ex; + IllegalArgumentException suppressed = null; + try { + final URI uri = new URI(path); + final String scheme = uri.getScheme(); + if (scheme != null && scheme.equalsIgnoreCase("file")) try { + return new File(uri); + } catch (IllegalArgumentException e) { + suppressed = e; + } + return uri.parseServerAuthority().toURL(); + } catch (MalformedURLException cause) { + ex = cause; + } catch (IllegalArgumentException | URISyntaxException cause) { + ex = (MalformedURLException) new MalformedURLException(Exceptions.formatChainedMessages(null, + Errors.format(Errors.Keys.IllegalArgumentValue_2, "path", path), cause)).initCause(cause); + } + if (suppressed != null) { + ex.addSuppressed(suppressed); + } + throw ex; } /** diff --git a/endorsed/src/org.apache.sis.storage/test/org/apache/sis/io/stream/IOUtilitiesTest.java b/endorsed/src/org.apache.sis.storage/test/org/apache/sis/io/stream/IOUtilitiesTest.java index bd9ee4a692..59146b4cc1 100644 --- a/endorsed/src/org.apache.sis.storage/test/org/apache/sis/io/stream/IOUtilitiesTest.java +++ b/endorsed/src/org.apache.sis.storage/test/org/apache/sis/io/stream/IOUtilitiesTest.java @@ -22,7 +22,6 @@ import java.net.URI; import java.net.URISyntaxException; import java.io.File; import java.io.IOException; -import org.apache.sis.util.CharSequences; // Test dependencies import org.junit.Test; @@ -154,74 +153,10 @@ public final class IOUtilitiesTest extends TestCase { assertNull(IOUtilities.encodeURI(null)); } - /** - * Tests {@link IOUtilities#toURI(URL, String)}. - * - * @throws IOException if a URL cannot be parsed. - * @throws URISyntaxException if a URI cannot be parsed. - */ - @Test - @DependsOnMethod("testEncodeURI") - @SuppressWarnings("deprecation") - public void testToURI() throws IOException, URISyntaxException { - assertEquals(new URI("file:/Users/name/Map.png"), - IOUtilities.toURI(new URL("file:/Users/name/Map.png"), null)); - assertEquals(new URI("file:/Users/name/Map%20with%20spaces.png"), - IOUtilities.toURI(new URL("file:/Users/name/Map with spaces.png"), null)); - assertEquals(new URI("file:/Users/name/Map%20with%20spaces.png"), - IOUtilities.toURI(new URL("file:/Users/name/Map%20with%20spaces.png"), "UTF-8")); - assertEquals(new URI("file:/Users/name/Map%20with%20spaces.png"), - IOUtilities.toURI(new URL("file:/Users/name/Map%20with%20spaces.png"), "ISO-8859-1")); - - // Here the URL is considered non-encoded, so the method shall encode the % sign. - assertEquals(new URI("file:/Users/name/Map%2520with%2520spaces.png"), - IOUtilities.toURI(new URL("file:/Users/name/Map%20with%20spaces.png"), null)); - } - - /** - * Tests the {@link IOUtilities#toFile(URL)} method. Do not test a Windows-specific path - * (e.g. {@code "file:///C:/some/path/Map.png"}), since the result is different on Windows or - * Unix platforms. - * - * @throws IOException if a URL cannot be parsed. - */ - @Test - @DependsOnMethod("testToURI") - public void testToFile() throws IOException { - testToFile(null, "+"); - } - - /** - * Same test as {@link #testToFile()}, but using the UTF-8 encoding. - * - * @throws IOException if a URL cannot be parsed. - */ - @Test - @DependsOnMethod("testToFile") - public void testToFileFromUTF8() throws IOException { - testToFile("UTF-8", "%2B"); - } - - /** - * Implementation of {@link #testToURL()} using the given encoding. - * If the encoding is null, then the {@code URLDecoder} will not be used. - * - * @param encoding the encoding, or {@code null} if none. - * @param plus the representation for the {@code '+'} sign. - * @throws IOException if a URL cannot be parsed. - */ - private void testToFile(final String encoding, final String plus) throws IOException { - assertEquals(new File("/Users/name/Map.png"), // Unix absolute path. - IOUtilities.toFile(IOUtilities.toURL("file:/Users/name/Map.png", encoding))); - assertEquals(new File("/Users/name/Map with spaces.png"), // Path with space. - IOUtilities.toFile(IOUtilities.toURL("file:/Users/name/Map with spaces.png", encoding))); - assertEquals(new File("/Users/name/++t--++est.shp"), // Path with + sign. - IOUtilities.toFile(IOUtilities.toURL( - CharSequences.replace("file:/Users/name/++t--++est.shp", "+", plus).toString(), encoding))); - } - /** * Tests {@link IOUtilities#toFileOrURL(String)}. + * Do not test a Windows-specific path (e.g. {@code "file:///C:/some/path/Map.png"}), + * because the result is different on Windows or Unix platforms. * * @throws IOException if a URL cannot be parsed. */ @@ -233,5 +168,12 @@ public final class IOUtilitiesTest extends TestCase { assertEquals(URI.create("http://localhost").toURL(), IOUtilities.toFileOrURL("http://localhost", null)); assertEquals(new File("/Users/name/Map with spaces.png"), IOUtilities.toFileOrURL("file:/Users/name/Map%20with%20spaces.png", "UTF-8")); + + String path = "file:/Users/name/++t--++est.shp"; + var expected = new File("/Users/name/++t--++est.shp"); + assertEquals(expected, IOUtilities.toFileOrURL(path, null)); + + path = path.replace("+", "%2B"); + assertEquals(expected, IOUtilities.toFileOrURL(path, "UTF-8")); } }
