View Javadoc

    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
    19  import java.io.BufferedReader;
    20  import java.io.BufferedWriter;
    21  import java.io.Closeable;
    22  import java.io.IOException;
    23  import java.io.InputStream;
    24  import java.io.InputStreamReader;
    25  import java.io.OutputStreamWriter;
    26  import java.io.UncheckedIOException;
    27  import java.net.URL;
    28  import java.nio.charset.Charset;
    29  import java.nio.file.Path;
    30  import java.util.stream.Stream;
    31
    32  import org.apache.commons.geometry.io.core.input.GeometryInput;
    33  import org.apache.commons.geometry.io.core.output.GeometryOutput;
    34
    35  /** Internal class containing utility methods for IO operations.
    36   */
    37  public final class GeometryIOUtils {
    38
    39      /** Path separator character used on Unix-like systems. */
    40      private static final char UNIX_PATH_SEP = '/';
    41
    42      /** Path separator character used on Windows. */
    43      private static final char WINDOWS_PATH_SEP = '\\';
    44
    45      /** Utility class; no instantiation. */
    46      private GeometryIOUtils() {}
    47
    48      /** Get the file name of the given path or null if one does not exist
    49       * or is the empty string.
    50       * @param path path to get the file name of
    51       * @return file name of the given path
    52       */
    53      public static String getFileName(final Path path) {
    54          if (path != null) {
    55              return getFileName(path.toString());
    56          }
    57
    58          return null;
    59      }
    60
    61      /** Get the file name of the given url or null if one does not exist or is
    62       * the empty string.
    63       * @param url url to get the file name of
    64       * @return file name of the given url
    65       */
    66      public static String getFileName(final URL url) {
    67          if (url != null) {
    68              return getFileName(url.getPath());
    69          }
    70
    71          return null;
    72      }
    73
    74      /** Get the file name from the given path string, defined as
    75       * the substring following the last path separator character.
    76       * Null is returned if the argument is null or the file name is
    77       * the empty string.
    78       * @param path path to get the file name from
    79       * @return file name of the given path string or null if a
    80       *      non-empty file name does not exist
    81       */
    82      public static String getFileName(final String path) {
    83          if (path != null) {
    84              final int lastSep = Math.max(
    85                      path.lastIndexOf(UNIX_PATH_SEP),
    86                      path.lastIndexOf(WINDOWS_PATH_SEP));
    87
    88              if (lastSep < path.length() - 1) {
    89                  return path.substring(lastSep + 1);
    90              }
    91          }
    92
    93          return null;
    94      }
    95
    96      /** Get the part of the file name after the last dot.
    97       * @param fileName file name to get the extension for
    98       * @return the extension of the file name, the empty string if no extension is found, or
    99       *      null if the argument is null
    100      */
    101     public static String getFileExtension(final String fileName) {
    102         if (fileName != null) {
    103             final int idx = fileName.lastIndexOf('.');
    104             if (idx > -1) {
    105                 return fileName.substring(idx + 1);
    106             }
    107
    108             return "";
    109         }
    110
    111         return null;
    112     }
    113
    114     /** Create a {@link BufferedReader} for reading from the given input. The charset used is the charset
    115      * defined in {@code input} or {@code defaultCharset} if null.
    116      * @param input input to read from
    117      * @param defaultCharset charset to use if no charset is defined in the input
    118      * @return new reader instance
    119      * @throws UncheckedIOException if an I/O error occurs
    120      */
    121     public static BufferedReader createBufferedReader(final GeometryInput input, final Charset defaultCharset) {
    122         final Charset charset = input.getCharset() != null ?
    123                 input.getCharset() :
    124                 defaultCharset;
    125
    126         return new BufferedReader(new InputStreamReader(input.getInputStream(), charset));
    127     }
    128
    129     /** Create a {@link BufferedWriter} for writing to the given output. The charset used is the charset
    130      * defined in {@code output} or {@code defaultCharset} if null.
    131      * @param output output to write to
    132      * @param defaultCharset charset to use if no charset is defined in the output
    133      * @return new writer instance
    134      * @throws UncheckedIOException if an I/O error occurs
    135      */
    136     public static BufferedWriter createBufferedWriter(final GeometryOutput output, final Charset defaultCharset) {
    137         final Charset charset = output.getCharset() != null ?
    138                 output.getCharset() :
    139                 defaultCharset;
    140
    141         return new BufferedWriter(new OutputStreamWriter(output.getOutputStream(), charset));
    142     }
    143
    144     /** Get a value from {@code supplier}, wrapping any {@link IOException} with
    145      * {@link UncheckedIOException}.
    146      * @param <T> returned type
    147      * @param supplier object supplying the return value
    148      * @return supplied value
    149      * @throws UncheckedIOException if an I/O error occurs
    150      */
    151     public static <T> T getUnchecked(final IOSupplier<T> supplier) {
    152         try {
    153             return supplier.get();
    154         } catch (IOException exc) {
    155             throw createUnchecked(exc);
    156         }
    157     }
    158
    159     /** Pass the given argument to the consumer, wrapping any {@link IOException} with
    160      * {@link UncheckedIOException}.
    161      * @param <T> argument type
    162      * @param consumer function to call
    163      * @param arg function argument
    164      * @throws UncheckedIOException if an I/O error occurs
    165      */
    166     public static <T> void acceptUnchecked(final IOConsumer<T> consumer, final T arg) {
    167         try {
    168             consumer.accept(arg);
    169         } catch (IOException exc) {
    170             throw createUnchecked(exc);
    171         }
    172     }
    173
    174     /** Call the given function with the argument and return the {@code int} result, wrapping any
    175      * {@link IOException} with {@link UncheckedIOException}.
    176      * @param <T> argument type
    177      * @param fn function to call
    178      * @param arg function argument
    179      * @return int value
    180      * @throws UncheckedIOException if an I/O error occurs
    181      */
    182     public static <T> int applyAsIntUnchecked(final IOToIntFunction<T> fn, final T arg) {
    183         try {
    184             return fn.applyAsInt(arg);
    185         } catch (IOException exc) {
    186             throw createUnchecked(exc);
    187         }
    188     }
    189
    190     /** Close the argument, wrapping any IO exceptions with {@link UncheckedIOException}.
    191      * @param closeable argument to close
    192      * @throws UncheckedIOException if an I/O error occurs
    193      */
    194     public static void closeUnchecked(final Closeable closeable) {
    195         try {
    196             closeable.close();
    197         } catch (IOException exc) {
    198             throw createUnchecked(exc);
    199         }
    200     }
    201
    202     /** Create an unchecked exception from the given checked exception. The message of the
    203      * returned exception contains the original exception's type and message.
    204      * @param exc exception to wrap in an unchecked exception
    205      * @return the unchecked exception
    206      */
    207     public static UncheckedIOException createUnchecked(final IOException exc) {
    208         final String msg = exc.getClass().getSimpleName() + ": " + exc.getMessage();
    209         return new UncheckedIOException(msg, exc);
    210     }
    211
    212     /** Create an exception indicating a parsing or syntax error.
    213      * @param msg exception message
    214      * @return an exception indicating a parsing or syntax error
    215      */
    216     public static IllegalStateException parseError(final String msg) {
    217         return parseError(msg, null);
    218     }
    219
    220     /** Create an exception indicating a parsing or syntax error.
    221      * @param msg exception message
    222      * @param cause exception cause
    223      * @return an exception indicating a parsing or syntax error
    224      */
    225     public static IllegalStateException parseError(final String msg, final Throwable cause) {
    226         return new IllegalStateException(msg, cause);
    227     }
    228
    229     /** Pass a supplied {@link Closeable} instance to {@code function} and return the result.
    230      * The {@code Closeable} instance returned by the supplier is closed if function execution
    231      * fails, otherwise the instance is <em>not</em> closed.
    232      * @param <T> Return type
    233      * @param <C> Closeable type
    234      * @param function function called with the supplied Closeable instance
    235      * @param closeableSupplier supplier used to obtain a Closeable instance
    236      * @return result of calling {@code function} with a supplied Closeable instance
    237      * @throws java.io.UncheckedIOException if an I/O error occurs
    238      */
    239     public static <T, C extends Closeable> T tryApplyCloseable(final IOFunction<C, T> function,
    240             final IOSupplier<? extends C> closeableSupplier) {
    241         C closeable = null;
    242         RuntimeException exc;
    243         try {
    244             closeable = closeableSupplier.get();
    245             return function.apply(closeable);
    246         } catch (RuntimeException e) {
    247             exc = e;
    248         } catch (IOException e) {
    249             exc = createUnchecked(e);
    250         }
    251
    252         if (closeable != null) {
    253             try {
    254                 closeable.close();
    255             } catch (IOException suppressed) {
    256                 exc.addSuppressed(suppressed);
    257             }
    258         }
    259
    260         throw exc;
    261     }
    262
    263     /** Create a stream associated with an input stream. The input stream is closed when the
    264      * stream is closed and also closed if stream creation fails. Any {@link IOException} thrown
    265      * when the input stream is closed after the return of this method are wrapped with {@link UncheckedIOException}.
    266      * @param <T> Stream element type
    267      * @param <I> Input stream type
    268      * @param streamFunction function accepting an input stream and returning a stream
    269      * @param inputStreamSupplier supplier used to obtain the input stream
    270      * @return stream associated with the input stream return by the supplier
    271      * @throws java.io.UncheckedIOException if an I/O error occurs during input stream and stream creation
    272      */
    273     public static <T, I extends InputStream> Stream<T> createCloseableStream(
    274             final IOFunction<I, Stream<T>> streamFunction, final IOSupplier<? extends I> inputStreamSupplier) {
    275         return tryApplyCloseable(
    276                 in -> streamFunction.apply(in).onClose(closeAsUncheckedRunnable(in)),
    277                 inputStreamSupplier);
    278     }
    279
    280     /** Return a {@link Runnable} that calls {@link Closeable#getClass() close()} on the argument,
    281      * wrapping any {@link IOException} with {@link UncheckedIOException}.
    282      * @param closeable instance to be closed
    283      * @return runnable that calls {@code close()) on the argument
    284      */
    285     private static Runnable closeAsUncheckedRunnable(final Closeable closeable) {
    286         return () -> closeUnchecked(closeable);
    287     }
    288 }