GeometryIOUtils.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.geometry.io.core.internal;

  18. import java.io.BufferedReader;
  19. import java.io.BufferedWriter;
  20. import java.io.Closeable;
  21. import java.io.IOException;
  22. import java.io.InputStream;
  23. import java.io.InputStreamReader;
  24. import java.io.OutputStreamWriter;
  25. import java.io.UncheckedIOException;
  26. import java.net.URL;
  27. import java.nio.charset.Charset;
  28. import java.nio.file.Path;
  29. import java.util.stream.Stream;

  30. import org.apache.commons.geometry.io.core.input.GeometryInput;
  31. import org.apache.commons.geometry.io.core.output.GeometryOutput;

  32. /** Internal class containing utility methods for IO operations.
  33.  */
  34. public final class GeometryIOUtils {

  35.     /** Path separator character used on Unix-like systems. */
  36.     private static final char UNIX_PATH_SEP = '/';

  37.     /** Path separator character used on Windows. */
  38.     private static final char WINDOWS_PATH_SEP = '\\';

  39.     /** Utility class; no instantiation. */
  40.     private GeometryIOUtils() {}

  41.     /** Get the file name of the given path or null if one does not exist
  42.      * or is the empty string.
  43.      * @param path path to get the file name of
  44.      * @return file name of the given path
  45.      */
  46.     public static String getFileName(final Path path) {
  47.         if (path != null) {
  48.             return getFileName(path.toString());
  49.         }

  50.         return null;
  51.     }

  52.     /** Get the file name of the given url or null if one does not exist or is
  53.      * the empty string.
  54.      * @param url url to get the file name of
  55.      * @return file name of the given url
  56.      */
  57.     public static String getFileName(final URL url) {
  58.         if (url != null) {
  59.             return getFileName(url.getPath());
  60.         }

  61.         return null;
  62.     }

  63.     /** Get the file name from the given path string, defined as
  64.      * the substring following the last path separator character.
  65.      * Null is returned if the argument is null or the file name is
  66.      * the empty string.
  67.      * @param path path to get the file name from
  68.      * @return file name of the given path string or null if a
  69.      *      non-empty file name does not exist
  70.      */
  71.     public static String getFileName(final String path) {
  72.         if (path != null) {
  73.             final int lastSep = Math.max(
  74.                     path.lastIndexOf(UNIX_PATH_SEP),
  75.                     path.lastIndexOf(WINDOWS_PATH_SEP));

  76.             if (lastSep < path.length() - 1) {
  77.                 return path.substring(lastSep + 1);
  78.             }
  79.         }

  80.         return null;
  81.     }

  82.     /** Get the part of the file name after the last dot.
  83.      * @param fileName file name to get the extension for
  84.      * @return the extension of the file name, the empty string if no extension is found, or
  85.      *      null if the argument is null
  86.      */
  87.     public static String getFileExtension(final String fileName) {
  88.         if (fileName != null) {
  89.             final int idx = fileName.lastIndexOf('.');
  90.             if (idx > -1) {
  91.                 return fileName.substring(idx + 1);
  92.             }

  93.             return "";
  94.         }

  95.         return null;
  96.     }

  97.     /** Create a {@link BufferedReader} for reading from the given input. The charset used is the charset
  98.      * defined in {@code input} or {@code defaultCharset} if null.
  99.      * @param input input to read from
  100.      * @param defaultCharset charset to use if no charset is defined in the input
  101.      * @return new reader instance
  102.      * @throws UncheckedIOException if an I/O error occurs
  103.      */
  104.     public static BufferedReader createBufferedReader(final GeometryInput input, final Charset defaultCharset) {
  105.         final Charset charset = input.getCharset() != null ?
  106.                 input.getCharset() :
  107.                 defaultCharset;

  108.         return new BufferedReader(new InputStreamReader(input.getInputStream(), charset));
  109.     }

  110.     /** Create a {@link BufferedWriter} for writing to the given output. The charset used is the charset
  111.      * defined in {@code output} or {@code defaultCharset} if null.
  112.      * @param output output to write to
  113.      * @param defaultCharset charset to use if no charset is defined in the output
  114.      * @return new writer instance
  115.      * @throws UncheckedIOException if an I/O error occurs
  116.      */
  117.     public static BufferedWriter createBufferedWriter(final GeometryOutput output, final Charset defaultCharset) {
  118.         final Charset charset = output.getCharset() != null ?
  119.                 output.getCharset() :
  120.                 defaultCharset;

  121.         return new BufferedWriter(new OutputStreamWriter(output.getOutputStream(), charset));
  122.     }

  123.     /** Get a value from {@code supplier}, wrapping any {@link IOException} with
  124.      * {@link UncheckedIOException}.
  125.      * @param <T> returned type
  126.      * @param supplier object supplying the return value
  127.      * @return supplied value
  128.      * @throws UncheckedIOException if an I/O error occurs
  129.      */
  130.     public static <T> T getUnchecked(final IOSupplier<T> supplier) {
  131.         try {
  132.             return supplier.get();
  133.         } catch (IOException exc) {
  134.             throw createUnchecked(exc);
  135.         }
  136.     }

  137.     /** Pass the given argument to the consumer, wrapping any {@link IOException} with
  138.      * {@link UncheckedIOException}.
  139.      * @param <T> argument type
  140.      * @param consumer function to call
  141.      * @param arg function argument
  142.      * @throws UncheckedIOException if an I/O error occurs
  143.      */
  144.     public static <T> void acceptUnchecked(final IOConsumer<T> consumer, final T arg) {
  145.         try {
  146.             consumer.accept(arg);
  147.         } catch (IOException exc) {
  148.             throw createUnchecked(exc);
  149.         }
  150.     }

  151.     /** Call the given function with the argument and return the {@code int} result, wrapping any
  152.      * {@link IOException} with {@link UncheckedIOException}.
  153.      * @param <T> argument type
  154.      * @param fn function to call
  155.      * @param arg function argument
  156.      * @return int value
  157.      * @throws UncheckedIOException if an I/O error occurs
  158.      */
  159.     public static <T> int applyAsIntUnchecked(final IOToIntFunction<T> fn, final T arg) {
  160.         try {
  161.             return fn.applyAsInt(arg);
  162.         } catch (IOException exc) {
  163.             throw createUnchecked(exc);
  164.         }
  165.     }

  166.     /** Close the argument, wrapping any IO exceptions with {@link UncheckedIOException}.
  167.      * @param closeable argument to close
  168.      * @throws UncheckedIOException if an I/O error occurs
  169.      */
  170.     public static void closeUnchecked(final Closeable closeable) {
  171.         try {
  172.             closeable.close();
  173.         } catch (IOException exc) {
  174.             throw createUnchecked(exc);
  175.         }
  176.     }

  177.     /** Create an unchecked exception from the given checked exception. The message of the
  178.      * returned exception contains the original exception's type and message.
  179.      * @param exc exception to wrap in an unchecked exception
  180.      * @return the unchecked exception
  181.      */
  182.     public static UncheckedIOException createUnchecked(final IOException exc) {
  183.         final String msg = exc.getClass().getSimpleName() + ": " + exc.getMessage();
  184.         return new UncheckedIOException(msg, exc);
  185.     }

  186.     /** Create an exception indicating a parsing or syntax error.
  187.      * @param msg exception message
  188.      * @return an exception indicating a parsing or syntax error
  189.      */
  190.     public static IllegalStateException parseError(final String msg) {
  191.         return parseError(msg, null);
  192.     }

  193.     /** Create an exception indicating a parsing or syntax error.
  194.      * @param msg exception message
  195.      * @param cause exception cause
  196.      * @return an exception indicating a parsing or syntax error
  197.      */
  198.     public static IllegalStateException parseError(final String msg, final Throwable cause) {
  199.         return new IllegalStateException(msg, cause);
  200.     }

  201.     /** Pass a supplied {@link Closeable} instance to {@code function} and return the result.
  202.      * The {@code Closeable} instance returned by the supplier is closed if function execution
  203.      * fails, otherwise the instance is <em>not</em> closed.
  204.      * @param <T> Return type
  205.      * @param <C> Closeable type
  206.      * @param function function called with the supplied Closeable instance
  207.      * @param closeableSupplier supplier used to obtain a Closeable instance
  208.      * @return result of calling {@code function} with a supplied Closeable instance
  209.      * @throws java.io.UncheckedIOException if an I/O error occurs
  210.      */
  211.     public static <T, C extends Closeable> T tryApplyCloseable(final IOFunction<C, T> function,
  212.             final IOSupplier<? extends C> closeableSupplier) {
  213.         C closeable = null;
  214.         RuntimeException exc;
  215.         try {
  216.             closeable = closeableSupplier.get();
  217.             return function.apply(closeable);
  218.         } catch (RuntimeException e) {
  219.             exc = e;
  220.         } catch (IOException e) {
  221.             exc = createUnchecked(e);
  222.         }

  223.         if (closeable != null) {
  224.             try {
  225.                 closeable.close();
  226.             } catch (IOException suppressed) {
  227.                 exc.addSuppressed(suppressed);
  228.             }
  229.         }

  230.         throw exc;
  231.     }

  232.     /** Create a stream associated with an input stream. The input stream is closed when the
  233.      * stream is closed and also closed if stream creation fails. Any {@link IOException} thrown
  234.      * when the input stream is closed after the return of this method are wrapped with {@link UncheckedIOException}.
  235.      * @param <T> Stream element type
  236.      * @param <I> Input stream type
  237.      * @param streamFunction function accepting an input stream and returning a stream
  238.      * @param inputStreamSupplier supplier used to obtain the input stream
  239.      * @return stream associated with the input stream return by the supplier
  240.      * @throws java.io.UncheckedIOException if an I/O error occurs during input stream and stream creation
  241.      */
  242.     public static <T, I extends InputStream> Stream<T> createCloseableStream(
  243.             final IOFunction<I, Stream<T>> streamFunction, final IOSupplier<? extends I> inputStreamSupplier) {
  244.         return tryApplyCloseable(
  245.                 in -> streamFunction.apply(in).onClose(closeAsUncheckedRunnable(in)),
  246.                 inputStreamSupplier);
  247.     }

  248.     /** Return a {@link Runnable} that calls {@link Closeable#getClass() close()} on the argument,
  249.      * wrapping any {@link IOException} with {@link UncheckedIOException}.
  250.      * @param closeable instance to be closed
  251.      * @return runnable that calls {@code close()) on the argument
  252.      */
  253.     private static Runnable closeAsUncheckedRunnable(final Closeable closeable) {
  254.         return () -> closeUnchecked(closeable);
  255.     }
  256. }