/**
* Copyright 2015 Santhosh Kumar Tekuri
*
* The JLibs authors license 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 jlibs.core.io;
import java.io.*;
import java.nio.charset.Charset;
/**
* This class contains various Input/Output related utility methods.
* </p>
* <br>
* <b>Stardard Charsets:</b><br>
* This class contains constants to stardard charsets that are supported by JVM Spec.
* <pre class="prettyprint">
* byte buff[] = ...
* String str = new String(buff, IOUtil.{@link IOUtil#UTF_8 UTF_8});
* </pre>
*
* <b>Pumping:</b><br>
* To read a file content as String:
* <pre class="prettyprint">
* CharArrayWriter cout = new CharArrayWriter();
* IOUtil.{@link IOUtil#pump(java.io.Reader, java.io.Writer, boolean, boolean) pump}(new FileReader(file), cout, true, true);
* String content = cout.toString();
* </pre>
* To simplify code, <code>pump(...)</code> method returns output; So the above code could be written in single line as follows:
* <pre class="prettyprint">
* String content = IOUtil.pump(new FileReader(file), new CharArrayWriter(), true, true).toString();
* </pre>
* if output is not specified, it defaults to {@link CharArrayWriter2}. so the same code can be written as:
* <pre class="prettyprint">
* String content = IOUtil.{@link IOUtil#pump(java.io.Reader, boolean) pump}(new FileReader(file), true).toString();
* </pre>
* Similar versions of pump(...) methods are available for byte-streams also;<br>
* Let us see how these methods simplify some code;
* <br><br>
* To copy file:
* <pre class="prettyprint">
* IOUtil.{@link IOUtil#pump(java.io.InputStream, java.io.OutputStream, boolean, boolean) pump}(new FileInputStream(fromFile), new FileOutputStream(toFile), true, true);
* </pre>
* To create zip file:
* <pre class="prettyprint">
* ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(zipFile));
* for(File file: files){
* zipOut.putNextEntry(new ZipEntry(file.getName());
* IOUtil.{@link IOUtil#pump(java.io.InputStream, java.io.OutputStream, boolean, boolean) pump}(new FileInputStream(file), zipOut, true, false); // note that last arg is false
* zipOut.closeEntry();
* }
* zipOut.close();
* </pre>
* To create file with given string:
* <pre class="prettyprint">
* String content = ...
* IOUtil.{@link IOUtil#pump(java.io.Reader, java.io.Writer, boolean, boolean) pump}(new StringReader(content), new FileWriter(file), true, true);
* </pre>
* to read a file content into byte array:
* <pre class="prettyprint">
* byte bytes[] = IOUtil.{@link IOUtil#pump(java.io.InputStream, boolean) pump}(new FileInputStream(file), true).toByteArray(); // output defaults to {@link ByteArrayOutputStream2}
* </pre>
*
* @author Santhosh Kumar T
*/
public class IOUtil{
/*-------------------------------------------------[ Standard Charsets ]---------------------------------------------------*/
public static final Charset US_ASCII = Charset.forName("US-ASCII");
public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
public static final Charset UTF_8 = Charset.forName("UTF-8");
public static final Charset UTF_16BE = Charset.forName("UTF-16BE");
public static final Charset UTF_16LE = Charset.forName("UTF-16LE");
public static final Charset UTF_16 = Charset.forName("UTF-16");
public static final Charset UTF_32BE = Charset.forName("UTF-32BE");
public static final Charset UTF_32LE = Charset.forName("UTF-32LE");
public static final Charset UTF_32 = Charset.forName("UTF-32");
/*-------------------------------------------------[ Pumping ]---------------------------------------------------*/
/**
* Reads data from <code>is</code> and writes it into an instanceof {@link ByteArrayOutputStream2}.<br>
*
* @param is inputstream from which data is read
* @param closeIn close inputstream or not
* @return the instance of {@link ByteArrayOutputStream2} into which data is written
* @throws IOException if an I/O error occurs.
*/
public static ByteArrayOutputStream2 pump(InputStream is, boolean closeIn) throws IOException{
return pump(is, new ByteArrayOutputStream2(), closeIn, true);
}
/**
* Reads data from <code>is</code> and writes it into <code>os</code>.<br>
* <code>is</code> and <code>os</code> are closed if <code>closeIn</code> and <code>closeOut</code>
* are true respectively.
*
* @param is inputstream from which data is read
* @param os outputstream into which data is written
* @param closeIn close inputstream or not
* @param closeOut close outputstream or not
* @return the argument <code>os</os>
* @throws IOException if an I/O error occurs.
*/
public static <T extends OutputStream> T pump(InputStream is, T os, boolean closeIn, boolean closeOut) throws IOException{
byte buff[] = new byte[1024];
int len;
Exception exception = null;
try{
while((len=is.read(buff))!=-1)
os.write(buff, 0, len);
}catch(Exception ex){
exception = ex;
}finally{
try{
try{
if(closeIn)
is.close();
}finally{
if(closeOut)
os.close();
}
}catch(IOException ex){
if(exception!=null)
ex.printStackTrace();
else
exception = ex;
}
}
if(exception instanceof IOException)
throw (IOException)exception;
else if(exception instanceof RuntimeException)
throw (RuntimeException)exception;
return os;
}
/**
* Reads data from <code>reader</code> and writes it into an instanceof {@link CharArrayWriter2}.<br>
*
* @param reader reader from which data is read
* @param closeReader close reader or not
* @return the instance of {@link CharArrayWriter2} into which data is written
* @throws IOException if an I/O error occurs.
*/
public static CharArrayWriter2 pump(Reader reader, boolean closeReader) throws IOException{
return pump(reader, new CharArrayWriter2(), closeReader, true);
}
/**
* Reads data from <code>reader</code> and writes it into <code>writer</code>.<br>
* <code>reader</code> and <code>writer</code> are closed if <code>closeReader</code> and <code>closeWriter</code>
* are true respectively.
*
* @param reader reader from which data is read
* @param writer writer into which data is written
* @param closeReader close reader or not
* @param closeWriter close writer or not
* @return the argument <code>writer</os>
* @throws IOException if an I/O error occurs.
*/
public static <T extends Writer> T pump(Reader reader, T writer, boolean closeReader, boolean closeWriter) throws IOException{
char buff[] = new char[1024];
int len;
Exception exception = null;
try{
while((len=reader.read(buff))!=-1)
writer.write(buff, 0, len);
}catch(Exception ex){
exception = ex;
}finally{
try{
try{
if(closeReader)
reader.close();
}finally{
if(closeWriter)
writer.close();
}
}catch(IOException ex){
if(exception!=null)
ex.printStackTrace();
else
exception = ex;
}
}
if(exception instanceof IOException)
throw (IOException)exception;
else if(exception instanceof RuntimeException)
throw (RuntimeException)exception;
return writer;
}
/*-------------------------------------------------[ Read-Fully ]---------------------------------------------------*/
/**
* Reads data from given inputstream into specified buffer.<br>
* If the given inputstream doesn't have number of bytes equal to the length
* of the buffer available, it simply reads only the available number of bytes.
*
* @param in input stream from which data is read
* @param b the buffer into which the data is read.
* @return the number of bytes read. if the inputstream doen't have enough bytes available
* to fill the buffer, it returns the the number of bytes read
* @throws IOException if an I/O error occurs.
*/
public static int readFully(InputStream in, byte b[]) throws IOException {
return readFully(in, b, 0, b.length);
}
/**
* Reads <code>len</code> bytes from given input stream into specified buffer.<br>
* If the given inputstream doesn't have <code>len</code> bytes available,
* it simply reads only the availabel number of bytes.
*
* @param in input stream from which data is read
* @param b the buffer into which the data is read.
* @param off an int specifying the offset into the data.
* @param len an int specifying the number of bytes to read.
* @return the number of bytes read. if the inputstream doen't have <code>len</code> bytes available
* it returns the the number of bytes read
* @throws IOException if an I/O error occurs.
*/
public static int readFully(InputStream in, byte b[], int off, int len) throws IOException{
if(len<0)
throw new IndexOutOfBoundsException();
int n = 0;
while(n<len){
int count = in.read(b, off+n, len-n);
if(count<0)
return n;
n += count;
}
return n;
}
/**
* Reads data from given reader into specified buffer.<br>
* If the given reader doesn't have number of chars equal to the length
* of the buffer available, it simply reads only the available number of chars.
*
* @param reader reader from which data is read
* @param ch the buffer into which the data is read.
* @return the number of chars read. if the reader doen't have enough chars available
* to fill the buffer, it returns the the number of chars read
* @throws IOException if an I/O error occurs.
*/
public static int readFully(Reader reader, char ch[]) throws IOException {
return readFully(reader, ch, 0, ch.length);
}
/**
* Reads <code>len</code> chars from given reader into specified buffer.<br>
* If the given reader doesn't have <code>len</code> chars available,
* it simply reads only the availabel number of chars.
*
* @param reader input stream from which data is read
* @param ch the buffer into which the data is read.
* @param off an int specifying the offset into the data.
* @param len an int specifying the number of chars to read.
* @return the number of chars read. if the reader doen't have <code>len</code> chars available
* it returns the the number of chars read
* @throws IOException if an I/O error occurs.
*/
public static int readFully(Reader reader, char ch[], int off, int len) throws IOException{
if(len<0)
throw new IndexOutOfBoundsException();
int n = 0;
while(n<len){
int count = reader.read(ch, off+n, len-n);
if(count<0)
return n;
n += count;
}
return n;
}
}