| package org.apache.lucene.util; |
| |
| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| import java.io.BufferedReader; |
| import java.io.Closeable; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.io.Reader; |
| import java.nio.charset.Charset; |
| import java.nio.charset.CharsetDecoder; |
| import java.nio.charset.CodingErrorAction; |
| |
| import org.apache.lucene.store.Directory; |
| |
| /** This class emulates the new Java 7 "Try-With-Resources" statement. |
| * Remove once Lucene is on Java 7. |
| * @lucene.internal */ |
| public final class IOUtils { |
| |
| /** |
| * UTF-8 charset string |
| * @see Charset#forName(String) |
| */ |
| public static final String UTF_8 = "UTF-8"; |
| |
| /** |
| * UTF-8 {@link Charset} instance to prevent repeated |
| * {@link Charset#forName(String)} lookups |
| */ |
| public static final Charset CHARSET_UTF_8 = Charset.forName("UTF-8"); |
| private IOUtils() {} // no instance |
| |
| /** |
| * <p>Closes all given <tt>Closeable</tt>s, suppressing all thrown exceptions. Some of the <tt>Closeable</tt>s |
| * may be null, they are ignored. After everything is closed, method either throws <tt>priorException</tt>, |
| * if one is supplied, or the first of suppressed exceptions, or completes normally.</p> |
| * <p>Sample usage:<br/> |
| * <pre class="prettyprint"> |
| * Closeable resource1 = null, resource2 = null, resource3 = null; |
| * ExpectedException priorE = null; |
| * try { |
| * resource1 = ...; resource2 = ...; resource3 = ...; // Acquisition may throw ExpectedException |
| * ..do..stuff.. // May throw ExpectedException |
| * } catch (ExpectedException e) { |
| * priorE = e; |
| * } finally { |
| * closeWhileHandlingException(priorE, resource1, resource2, resource3); |
| * } |
| * </pre> |
| * </p> |
| * @param priorException <tt>null</tt> or an exception that will be rethrown after method completion |
| * @param objects objects to call <tt>close()</tt> on |
| */ |
| public static <E extends Exception> void closeWhileHandlingException(E priorException, Closeable... objects) throws E, IOException { |
| Throwable th = null; |
| |
| for (Closeable object : objects) { |
| try { |
| if (object != null) { |
| object.close(); |
| } |
| } catch (Throwable t) { |
| addSuppressed((priorException == null) ? th : priorException, t); |
| if (th == null) { |
| th = t; |
| } |
| } |
| } |
| |
| if (priorException != null) { |
| throw priorException; |
| } else { |
| reThrow(th); |
| } |
| } |
| |
| /** |
| * Closes all given <tt>Closeable</tt>s, suppressing all thrown exceptions. |
| * @see #closeWhileHandlingException(Exception, Closeable...) */ |
| public static <E extends Exception> void closeWhileHandlingException(E priorException, Iterable<? extends Closeable> objects) throws E, IOException { |
| Throwable th = null; |
| |
| for (Closeable object : objects) { |
| try { |
| if (object != null) { |
| object.close(); |
| } |
| } catch (Throwable t) { |
| addSuppressed((priorException == null) ? th : priorException, t); |
| if (th == null) { |
| th = t; |
| } |
| } |
| } |
| |
| if (priorException != null) { |
| throw priorException; |
| } else { |
| reThrow(th); |
| } |
| } |
| |
| /** |
| * Closes all given <tt>Closeable</tt>s. Some of the |
| * <tt>Closeable</tt>s may be null; they are |
| * ignored. After everything is closed, the method either |
| * throws the first exception it hit while closing, or |
| * completes normally if there were no exceptions. |
| * |
| * @param objects |
| * objects to call <tt>close()</tt> on |
| */ |
| public static void close(Closeable... objects) throws IOException { |
| Throwable th = null; |
| |
| for (Closeable object : objects) { |
| try { |
| if (object != null) { |
| object.close(); |
| } |
| } catch (Throwable t) { |
| addSuppressed(th, t); |
| if (th == null) { |
| th = t; |
| } |
| } |
| } |
| |
| reThrow(th); |
| } |
| |
| /** |
| * Closes all given <tt>Closeable</tt>s. |
| * @see #close(Closeable...) |
| */ |
| public static void close(Iterable<? extends Closeable> objects) throws IOException { |
| Throwable th = null; |
| |
| for (Closeable object : objects) { |
| try { |
| if (object != null) { |
| object.close(); |
| } |
| } catch (Throwable t) { |
| addSuppressed(th, t); |
| if (th == null) { |
| th = t; |
| } |
| } |
| } |
| |
| reThrow(th); |
| } |
| |
| /** |
| * Closes all given <tt>Closeable</tt>s, suppressing all thrown exceptions. |
| * Some of the <tt>Closeable</tt>s may be null, they are ignored. |
| * |
| * @param objects |
| * objects to call <tt>close()</tt> on |
| */ |
| public static void closeWhileHandlingException(Closeable... objects) { |
| for (Closeable object : objects) { |
| try { |
| if (object != null) { |
| object.close(); |
| } |
| } catch (Throwable t) { |
| } |
| } |
| } |
| |
| /** |
| * Closes all given <tt>Closeable</tt>s, suppressing all thrown exceptions. |
| * @see #closeWhileHandlingException(Closeable...) |
| */ |
| public static void closeWhileHandlingException(Iterable<? extends Closeable> objects) { |
| for (Closeable object : objects) { |
| try { |
| if (object != null) { |
| object.close(); |
| } |
| } catch (Throwable t) { |
| } |
| } |
| } |
| |
| /** adds a Throwable to the list of suppressed Exceptions of the first Throwable |
| * @param exception this exception should get the suppressed one added |
| * @param suppressed the suppressed exception |
| */ |
| private static void addSuppressed(Throwable exception, Throwable suppressed) { |
| if (exception != null && suppressed != null) { |
| exception.addSuppressed(suppressed); |
| } |
| } |
| |
| /** |
| * Wrapping the given {@link InputStream} in a reader using a {@link CharsetDecoder}. |
| * Unlike Java's defaults this reader will throw an exception if your it detects |
| * the read charset doesn't match the expected {@link Charset}. |
| * <p> |
| * Decoding readers are useful to load configuration files, stopword lists or synonym files |
| * to detect character set problems. However, its not recommended to use as a common purpose |
| * reader. |
| * |
| * @param stream the stream to wrap in a reader |
| * @param charSet the expected charset |
| * @return a wrapping reader |
| */ |
| public static Reader getDecodingReader(InputStream stream, Charset charSet) { |
| final CharsetDecoder charSetDecoder = charSet.newDecoder() |
| .onMalformedInput(CodingErrorAction.REPORT) |
| .onUnmappableCharacter(CodingErrorAction.REPORT); |
| return new BufferedReader(new InputStreamReader(stream, charSetDecoder)); |
| } |
| |
| /** |
| * Opens a Reader for the given {@link File} using a {@link CharsetDecoder}. |
| * Unlike Java's defaults this reader will throw an exception if your it detects |
| * the read charset doesn't match the expected {@link Charset}. |
| * <p> |
| * Decoding readers are useful to load configuration files, stopword lists or synonym files |
| * to detect character set problems. However, its not recommended to use as a common purpose |
| * reader. |
| * @param file the file to open a reader on |
| * @param charSet the expected charset |
| * @return a reader to read the given file |
| */ |
| public static Reader getDecodingReader(File file, Charset charSet) throws IOException { |
| FileInputStream stream = null; |
| boolean success = false; |
| try { |
| stream = new FileInputStream(file); |
| final Reader reader = getDecodingReader(stream, charSet); |
| success = true; |
| return reader; |
| |
| } finally { |
| if (!success) { |
| IOUtils.close(stream); |
| } |
| } |
| } |
| |
| /** |
| * Opens a Reader for the given resource using a {@link CharsetDecoder}. |
| * Unlike Java's defaults this reader will throw an exception if your it detects |
| * the read charset doesn't match the expected {@link Charset}. |
| * <p> |
| * Decoding readers are useful to load configuration files, stopword lists or synonym files |
| * to detect character set problems. However, its not recommended to use as a common purpose |
| * reader. |
| * @param clazz the class used to locate the resource |
| * @param resource the resource name to load |
| * @param charSet the expected charset |
| * @return a reader to read the given file |
| * |
| */ |
| public static Reader getDecodingReader(Class<?> clazz, String resource, Charset charSet) throws IOException { |
| InputStream stream = null; |
| boolean success = false; |
| try { |
| stream = clazz |
| .getResourceAsStream(resource); |
| final Reader reader = getDecodingReader(stream, charSet); |
| success = true; |
| return reader; |
| } finally { |
| if (!success) { |
| IOUtils.close(stream); |
| } |
| } |
| } |
| |
| /** |
| * Deletes all given files, suppressing all thrown IOExceptions. |
| * <p> |
| * Note that the files should not be null. |
| */ |
| public static void deleteFilesIgnoringExceptions(Directory dir, String... files) { |
| for (String name : files) { |
| try { |
| dir.deleteFile(name); |
| } catch (Throwable ignored) { |
| // ignore |
| } |
| } |
| } |
| |
| /** |
| * Copy one file's contents to another file. The target will be overwritten |
| * if it exists. The source must exist. |
| */ |
| public static void copy(File source, File target) throws IOException { |
| FileInputStream fis = null; |
| FileOutputStream fos = null; |
| try { |
| fis = new FileInputStream(source); |
| fos = new FileOutputStream(target); |
| |
| final byte [] buffer = new byte [1024 * 8]; |
| int len; |
| while ((len = fis.read(buffer)) > 0) { |
| fos.write(buffer, 0, len); |
| } |
| } finally { |
| close(fis, fos); |
| } |
| } |
| |
| /** |
| * Simple utilty method that takes a previously caught |
| * {@code Throwable} and rethrows either {@code |
| * IOException} or an unchecked exception. If the |
| * argument is null then this method does nothing. |
| */ |
| public static void reThrow(Throwable th) throws IOException { |
| if (th != null) { |
| if (th instanceof IOException) { |
| throw (IOException) th; |
| } |
| if (th instanceof RuntimeException) { |
| throw (RuntimeException) th; |
| } |
| if (th instanceof Error) { |
| throw (Error) th; |
| } |
| throw new RuntimeException(th); |
| } |
| } |
| } |