blob: cfef13854c9581bcaf834290b4df3903a29e2c82 [file] [log] [blame]
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);
}
}
}