blob: 63417caff0eb35f6f8cf8249c43e58221d12c811 [file] [log] [blame]
// ***************************************************************************************************************************
// * 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. *
// ***************************************************************************************************************************
package org.apache.juneau.internal;
import static org.apache.juneau.internal.ThrowableUtils.*;
import static org.apache.juneau.internal.StringUtils.*;
import java.io.*;
import java.nio.charset.*;
import org.apache.juneau.*;
import org.apache.juneau.utils.*;
/**
* Various I/O related utility methods.
*/
public final class IOUtils {
/** UTF-8 charset */
public static final Charset UTF8 = Charset.forName("UTF-8");
/**
* Reads the contents of a file into a string.
*
* @param path The path of the file to read using default character encoding.
* @return The contents of the reader as a string, or <jk>null</jk> if file does not exist.
* @throws IOException If a problem occurred trying to read from the reader.
*/
public static String readFile(String path) throws IOException {
return read(new File(path));
}
/**
* Reads the specified object to a <c>String</c>.
*
* <p>
* Can be any of the following object types:
* <ul>
* <li>{@link CharSequence}
* <li>{@link File}
* <li>{@link Reader}
* <li>{@link InputStream}
* <li><code><jk>byte</jk>[]</code>
* </ul>
*
* @param in The object to read.
* @return The object serialized to a string, or <jk>null</jk> if it wasn't a supported type.
* @throws IOException Thrown by underlying stream.
*/
public static String read(Object in) throws IOException {
if (in == null)
return null;
if (in instanceof CharSequence)
return in.toString();
if (in instanceof File)
return read((File)in);
if (in instanceof Reader)
return read((Reader)in);
if (in instanceof InputStream)
return read((InputStream)in);
if (in instanceof byte[])
return read(new ByteArrayInputStream((byte[])in));
throw new IOException("Cannot convert object of type '"+in.getClass().getName()+"' to a String.");
}
/**
* Same as {@link #read(Object)} but appends all the input into a single String.
*
* @param in The objects to read.
* @return The objects serialized to a string, never <jk>null</jk>.
* @throws IOException Thrown by underlying stream.
*/
public static String readAll(Object...in) throws IOException {
if (in.length == 1)
return read(in[0]);
StringWriter sw = new StringWriter();
for (Object o : in)
sw.write(emptyIfNull(read(o)));
return sw.toString();
}
/**
* Reads the contents of a file into a string.
*
* @param in The file to read using default character encoding.
* @return The contents of the reader as a string, or <jk>null</jk> if file does not exist.
* @throws IOException If a problem occurred trying to read from the reader.
*/
public static String read(File in) throws IOException {
if (in == null || ! in.exists())
return null;
try (Reader r = FileReaderBuilder.create(in).build()) {
return read(r, 0, 1024);
}
}
/**
* Reads the contents of a reader into a string.
*
* @param in The input reader.
* @return The contents of the reader as a string.
* @throws IOException If a problem occurred trying to read from the reader.
*/
public static String read(Reader in) throws IOException {
return read(in, 0, 1024);
}
/**
* Reads the contents of an input stream into a string using the specified charset.
*
* @param in The input stream.
* @param cs The charset of the contents of the input stream.
* @return The contents of the reader as a string. <jk>null</jk> if input stream was null.
* @throws IOException If a problem occurred trying to read from the input stream.
*/
public static String read(InputStream in, Charset cs) throws IOException {
if (in == null)
return null;
return read(new InputStreamReader(in, cs));
}
/**
* Reads the contents of an input stream into a string using the system default charset.
*
* @param in The input stream.
* @return The contents of the reader as a string, or <jk>null</jk> if the input stream is null.
* @throws IOException If a problem occurred trying to read from the input stream.
*/
public static String read(InputStream in) throws IOException {
if (in == null)
return null;
return read(new InputStreamReader(in, Charset.defaultCharset()));
}
/**
* Reads the specified input into a {@link String} until the end of the input is reached.
*
* <p>
* The {@code Reader} is automatically closed.
*
* <p>
* If the {@code Reader} is not an instance of a {@code BufferedReader}, then it gets wrapped in a
* {@code BufferedReader}.
*
* @param in The input reader.
* @param length Specify a positive number if the length of the input is known.
* @param bufferSize Specify the buffer size to use.
* @return The contents of the reader as a string. <jk>null</jk> if reader was null.
* @throws IOException If a problem occurred trying to read from the reader.
*/
public static String read(Reader in, int length, int bufferSize) throws IOException {
if (in == null)
return null;
if (bufferSize == 0)
bufferSize = 1024;
length = (length <= 0 ? bufferSize : length);
StringBuilder sb = new StringBuilder(length); // Assume they're ASCII characters.
try {
char[] buf = new char[Math.min(bufferSize, length)];
int i = 0;
while ((i = in.read(buf)) != -1)
sb.append(buf, 0, i);
return sb.toString();
} finally {
in.close();
}
}
/**
* Read the specified object into a byte array.
*
* @param in
* The object to read into a byte array.
* <br>Can be any of the following types:
* <ul>
* <li><code><jk>byte</jk>[]</code>
* <li>{@link InputStream}
* <li>{@link Reader}
* <li>{@link CharSequence}
* <li>{@link File}
* </ul>
* @param buffSize
* The buffer size to use.
* @return The contents of the stream as a byte array.
* @throws IOException Thrown by underlying stream or if object is not a supported type.
*/
public static byte[] readBytes(Object in, int buffSize) throws IOException {
if (in == null)
return new byte[0];
if (in instanceof byte[])
return (byte[])in;
if (in instanceof CharSequence)
return in.toString().getBytes(UTF8);
if (in instanceof InputStream)
return readBytes((InputStream)in, buffSize);
if (in instanceof Reader)
return read((Reader)in, 0, buffSize).getBytes(UTF8);
if (in instanceof File)
return readBytes((File)in, buffSize);
throw new IOException("Cannot convert object of type '"+in.getClass().getName()+"' to a byte array.");
}
/**
* Read the specified input stream into a byte array.
*
* @param in
* The stream to read into a byte array.
* @return The contents of the stream as a byte array.
* @throws IOException Thrown by underlying stream.
*/
public static byte[] readBytes(InputStream in) throws IOException {
return in == null ? null : readBytes(in, 1024);
}
/**
* Read the specified input stream into a byte array.
*
* @param in
* The stream to read into a byte array.
* @param buffSize
* The buffer size to use.
* @return The contents of the stream as a byte array.
* @throws IOException Thrown by underlying stream.
*/
public static byte[] readBytes(InputStream in, int buffSize) throws IOException {
if (buffSize == 0)
buffSize = 1024;
try (final ByteArrayOutputStream buff = new ByteArrayOutputStream(buffSize)) {
int nRead;
byte[] b = new byte[buffSize];
while ((nRead = in.read(b, 0, b.length)) != -1)
buff.write(b, 0, nRead);
buff.flush();
return buff.toByteArray();
}
}
/**
* Read the specified file into a byte array.
*
* @param in
* The file to read into a byte array.
* @return The contents of the file as a byte array.
* @throws IOException Thrown by underlying stream.
*/
public static byte[] readBytes(File in) throws IOException {
return readBytes(in, 1024);
}
/**
* Read the specified file into a byte array.
*
* @param in
* The file to read into a byte array.
* @param buffSize
* The buffer size to use.
* @return The contents of the file as a byte array.
* @throws IOException Thrown by underlying stream.
*/
public static byte[] readBytes(File in, int buffSize) throws IOException {
if (buffSize == 0)
buffSize = 1024;
if (! (in.exists() && in.canRead()))
return new byte[0];
buffSize = Math.min((int)in.length(), buffSize);
try (FileInputStream fis = new FileInputStream(in)) {
return readBytes(fis, buffSize);
}
}
/**
* Shortcut for calling <c>readBytes(in, 1024);</c>
*
* @param in
* The object to read into a byte array.
* <br>Can be any of the following types:
* <ul>
* <li><code><jk>byte</jk>[]</code>
* <li>{@link InputStream}
* <li>{@link Reader}
* <li>{@link CharSequence}
* <li>{@link File}
* </ul>
* @return The contents of the stream as a byte array.
* @throws IOException Thrown by underlying stream or if object is not a supported type.
*/
public static byte[] readBytes(Object in) throws IOException {
return readBytes(in, 1024);
}
/**
* Same as {@link #readBytes(Object)} but appends all the input into a single byte array.
*
* @param in The objects to read.
* @return The objects serialized to a byte array, never <jk>null</jk>.
* @throws IOException Thrown by underlying stream.
*/
public static byte[] readBytes(Object...in) throws IOException {
if (in.length == 1)
return readBytes(in[0]);
try (final ByteArrayOutputStream buff = new ByteArrayOutputStream(1024)) {
for (Object o : in) {
byte[] bo = readBytes(o);
if (bo != null)
buff.write(bo);
}
buff.flush();
return buff.toByteArray();
}
}
/**
* Writes the contents of the specified <c>Reader</c> to the specified file.
*
* @param out The file to write the output to.
* @param in The reader to pipe from.
* @return The number of characters written to the file.
* @throws IOException Thrown by underlying stream.
*/
public static int write(File out, Reader in) throws IOException {
assertFieldNotNull(out, "out");
assertFieldNotNull(in, "in");
try (Writer w = FileWriterBuilder.create(out).build()) {
return IOPipe.create(in, w).run();
}
}
/**
* Writes the contents of the specified <c>InputStream</c> to the specified file.
*
* @param out The file to write the output to.
* @param in The input stream to pipe from.
* @return The number of characters written to the file.
* @throws IOException Thrown by underlying stream.
*/
public static int write(File out, InputStream in) throws IOException {
assertFieldNotNull(out, "out");
assertFieldNotNull(in, "in");
try (OutputStream os = new FileOutputStream(out)) {
return IOPipe.create(in, os).run();
}
}
/**
* Pipes the contents of the specified object into the writer.
*
* <p>
* The reader is closed, the writer is not.
*
* @param in
* The input to pipe from.
* Can be any of the types defined by {@link #toReader(Object)}.
* @param out
* The writer to pipe to.
* @throws IOException Thrown by underlying stream.
*/
public static void pipe(Object in, Writer out) throws IOException {
IOPipe.create(in, out).run();
}
/**
* Pipes the contents of the specified object into the output stream.
*
* <p>
* The input stream is closed, the output stream is not.
*
* @param in
* The input to pipe from.
* Can be any of the types defined by {@link #toInputStream(Object)}.
* @param out
* The writer to pipe to.
* @throws IOException Thrown by underlying stream.
*/
public static void pipe(Object in, OutputStream out) throws IOException {
IOPipe.create(in, out).run();
}
/**
* Wraps the specified reader in a buffered reader.
*
* @param r The reader being wrapped.
* @return
* The reader wrapped in a {@link BufferedReader}, or the original {@link Reader} if it's already a buffered
* reader.
*/
public static Reader getBufferedReader(Reader r) {
if (r == null || r instanceof BufferedReader || r instanceof StringReader)
return r;
return new BufferedReader(r);
}
/**
* Counts the number of bytes in the input stream and then closes the stream.
*
* @param is The input stream to read from.
* @return The number of bytes read.
* @throws IOException Thrown by underlying stream.
*/
public static long count(InputStream is) throws IOException {
assertFieldNotNull(is, "is");
long c = 0;
long i;
try {
while ((i = is.skip(1024)) != 0)
c += i;
} finally {
is.close();
}
return c;
}
/**
* Counts the number of characters in the reader and then closes the reader.
*
* @param r The reader to read from.
* @return The number of characters read.
* @throws IOException Thrown by underlying stream.
*/
public static long count(Reader r) throws IOException {
assertFieldNotNull(r, "r");
long c = 0;
long i;
try {
while ((i = r.skip(1024)) != 0)
c += i;
} finally {
r.close();
}
return c;
}
/**
* Given the specified <js>"Content-Length"</js> header value, return an appropriate buffer size.
*
* <p>
* The maximum buffer size is 1MB.
*
* @param contentLength The value of the <js>"Content-Length"</js> header.
* @return The appropriate buffer size.
*/
public static int getBufferSize(String contentLength) {
try {
if (isNotEmpty(contentLength)) {
long l = Long.decode(contentLength);
if (l > 1048576)
return 1048576;
if (l <= 0)
return 8192;
return (int)l;
}
} catch (Exception e) {
return 8192;
}
return 8192;
}
/**
* Close input stream and ignore any exceptions.
*
* <p>
* No-op if input stream is <jk>null</jk>.
*
* @param is The input stream to close.
*/
public static void closeQuietly(InputStream is) {
try {
if (is != null)
is.close();
} catch (IOException e) {}
}
/**
* Close output stream and ignore any exceptions.
*
* <p>
* No-op if output stream is <jk>null</jk>.
*
* @param os The output stream to close.
*/
public static void closeQuietly(OutputStream os) {
try {
if (os != null)
os.close();
} catch (IOException e) {}
}
/**
* Close reader and ignore any exceptions.
*
* <p>
* No-op if reader is <jk>null</jk>.
*
* @param r The reader to close.
*/
public static void closeQuietly(Reader r) {
try {
if (r != null)
r.close();
} catch (IOException e) {}
}
/**
* Close writer and ignore any exceptions.
*
* <p>
* No-op if writer is <jk>null</jk>.
*
* @param w The writer to close.
*/
public static void closeQuietly(Writer w) {
try {
if (w != null)
w.close();
} catch (IOException e) {}
}
/**
* Quietly close all specified input streams, output streams, readers, and writers.
*
* @param o The list of all objects to quietly close.
*/
public static void closeQuietly(Object...o) {
for (Object o2 : o) {
if (o2 instanceof InputStream)
closeQuietly((InputStream)o2);
if (o2 instanceof OutputStream)
closeQuietly((OutputStream)o2);
if (o2 instanceof Reader)
closeQuietly((Reader)o2);
if (o2 instanceof Writer)
closeQuietly((Writer)o2);
}
}
/**
* Flushes multiple output streams and writers in a single call.
*
* @param o
* The objects to flush.
* <jk>null</jk> entries are ignored.
* @throws IOException Thrown by underlying stream.
*/
public static void flush(Object...o) throws IOException {
IOException ex = null;
for (Object o2 : o) {
try {
if (o2 instanceof OutputStream)
((OutputStream)o2).flush();
if (o2 instanceof Writer)
((Writer)o2).flush();
} catch (IOException e) {
ex = e;
}
}
if (ex != null)
throw ex;
}
/**
* Close all specified input streams, output streams, readers, and writers.
*
* @param o
* The list of all objects to close.
* <jk>null</jk> entries are ignored.
* @throws IOException Thrown by underlying stream.
*/
public static void close(Object...o) throws IOException {
IOException ex = null;
for (Object o2 : o) {
try {
if (o2 instanceof InputStream)
((InputStream)o2).close();
if (o2 instanceof OutputStream)
((OutputStream)o2).close();
if (o2 instanceof Reader)
((Reader)o2).close();
if (o2 instanceof Writer)
((Writer)o2).close();
} catch (IOException e) {
ex = e;
}
}
if (ex != null)
throw ex;
}
/**
* Converts an object to a <c>Reader</c>.
*
* @param o
* The object to convert to a reader.
* Can be any of the following:
* <ul>
* <li>{@link InputStream}
* <li>{@link Reader}
* <li>{@link File}
* <li>{@link CharSequence}
* <li><code><jk>byte</jk>[]</code>
* <li><code><jk>null</jk></code> - Returns <jk>null</jk>.
* </ul>
* @return The object converted to a reader.
* @throws IOException If file could not be read.
* @throws IllegalArgumentException If invalid object passed in.
*/
public static Reader toReader(Object o) throws IOException {
if (o == null)
return null;
if (o instanceof CharSequence)
return new StringReader(o.toString());
if (o instanceof File)
return new FileReader((File)o);
if (o instanceof Reader)
return (Reader)o;
if (o instanceof InputStream)
return new InputStreamReader((InputStream)o, "UTF-8");
if (o instanceof byte[])
return new InputStreamReader(new ByteArrayInputStream((byte[])o), "UTF-8");
throw new FormattedIllegalArgumentException("Invalid object of type {0} passed to IOUtils.toReader(Object)", o.getClass());
}
/**
* Converts an object to an <c>InputStream</c>.
*
* @param o
* The object to convert to an input stream.
* Can be any of the following:
* <ul>
* <li>{@link InputStream}
* <li>{@link Reader}
* <li>{@link File}
* <li>{@link CharSequence} - Converted to UTF-8 stream.
* <li><code><jk>byte</jk>[]</code>
* <li><code><jk>null</jk></code> - Returns <jk>null</jk>.
* </ul>
* @return The object converted to an input stream.
* @throws IOException If file could not be read.
* @throws IllegalArgumentException If invalid object passed in.
*/
public static InputStream toInputStream(Object o) throws IOException {
if (o == null)
return null;
if (o instanceof InputStream)
return (InputStream)o;
if (o instanceof File)
return new FileInputStream((File)o);
if (o instanceof byte[])
return new ByteArrayInputStream((byte[])o);
if (o instanceof CharSequence)
return new ByteArrayInputStream(((CharSequence)o).toString().getBytes(UTF8));
if (o instanceof Reader)
return new ByteArrayInputStream(IOUtils.read((Reader)o).getBytes(UTF8));
throw new FormattedIllegalArgumentException("Invalid object of type {0} passed to IOUtils.toInputStream(Object)", o.getClass());
}
/**
* Writes the specified string to the specified file.
*
* @param path The file path.
* @param contents The new file contents.
* @throws IOException Thrown by underlying stream.
*/
public static void write(String path, String contents) throws IOException {
write(new File(path), new StringReader(contents));
}
/**
* Loads a text file from either the file system or classpath.
*
* @param name The file name.
* @param paths The paths to search.
* @return The file contents, or <jk>null</jk> if not found.
* @throws IOException Thrown by underlying stream.
*/
public static String loadSystemResourceAsString(String name, String...paths) throws IOException {
for (String path : paths) {
File p = new File(path);
if (p.exists()) {
File f = new File(p, name);
if (f.exists() && f.canRead())
return read(f);
}
}
ClassLoader cl = Thread.currentThread().getContextClassLoader();
if (cl == null)
cl = ClassLoader.getSystemClassLoader();
for (String path : paths) {
String n = ".".equals(path) ? name : path + '/' + name;
try (InputStream is = cl.getResourceAsStream(n)) {
if (is != null)
return read(is);
}
try (InputStream is = ClassLoader.getSystemResourceAsStream(n)) {
if (is != null)
return read(is);
}
}
return null;
}
}