Various utility methods that have something to do with I/O
/**
* The utillib library.
* More information is available at http://www.jinchess.com/.
* Copyright (C) 2002 Alexander Maryanovsky.
* All rights reserved.
*
* The utillib library is free software; you can redistribute
* it and/or modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* The utillib library is distributed in the hope that it will
* be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with utillib library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Hashtable;
import java.util.Properties;
/**
* Various utility methods that have something to do with I/O.
*/
public class IOUtilities{
/**
* Maps URLs to byte arrays of the data loaded from them.
*/
private final static Hashtable urlCache = new Hashtable();
/**
* Returns a DataOutputStream object based on the given OutputStream.
* If the given OutputStream is already an instance of DataOutputStream,
* the same (given) OutputStream is casted to DataOutputStream and returned,
* otherwise, a new wrapper DataOutputStream is created and returned.
*/
public static DataOutputStream maybeCreateDataOutputStream(OutputStream out){
if (out instanceof DataOutputStream)
return (DataOutputStream)out;
else
return new DataOutputStream(out);
}
/**
* Returns a DataInputStream object based on the given InputStream.
* If the given InputStream is already an instance of DataInputStream,
* the same (given) InputStream is casted to DataInputStream and returned,
* otherwise, a new wrapper DataInputStream is created and returned.
*/
public static DataInputStream maybeCreateDataInputStream(InputStream in){
if (in instanceof DataInputStream)
return (DataInputStream)in;
else
return new DataInputStream(in);
}
/**
* Copies all the files of the given source directory into the given
* destination directory, optionally recursively.
*/
public static void copyDir(File source, File destination, boolean recurse) throws IOException{
if (!source.exists())
throw new IllegalArgumentException("The source directory ("+source+") doesn't exist");
if (!source.isDirectory())
throw new IllegalArgumentException("The source ("+source+") is a file, not a directory");
if (!destination.exists())
throw new IllegalArgumentException("The destination directory ("+destination+") doesn't exist");
if (!destination.isDirectory())
throw new IllegalArgumentException("The destination ("+destination+") is a file, not a directory");
String [] filenames = source.list();
for (int i=0; i<filenames.length; i++){
String filename = filenames[i];
File file = new File(source, filename);
if (file.isDirectory()){
if (recurse){
File destSubDir = new File(destination, filename);
if (!destSubDir.exists())
if (!destSubDir.mkdirs())
throw new IOException("Unable to create directory "+destSubDir);
copyDir(file, destSubDir, true);
}
}
else{
InputStream in = null;
OutputStream out = null;
try{
in = new FileInputStream(file);
out = new FileOutputStream(new File(destination, filename));
pump(in, out);
} finally{
if (in!=null)
in.close();
if (out!=null)
out.close();
}
}
}
}
/**
* Removes the given directory and all files within it, recursively. Returns
* <code>true</code> if successful, <code>false</code> otherwise. Note that if
* it return <code>false</code>, some (or all) the files in the directory may
* already be deleted.
*/
public static boolean rmdir(File dir){
if (!dir.isDirectory())
throw new IllegalArgumentException();
String [] filenames = dir.list();
for (int i = 0; i < filenames.length; i++){
File file = new File(dir, filenames[i]);
if (file.isDirectory()){
if (!rmdir(file))
return false;
}
else if (!file.delete())
return false;
}
return dir.delete();
}
/**
* Writes the bytes read from the given input stream into the given output
* stream until the end of the input stream is reached. Returns the amount of
* bytes actually read/written.
*/
public static int pump(InputStream in, OutputStream out) throws IOException{
return pump(in, out, new byte[2048]);
}
/**
* Writes up to the given amount of bytes read from the given input stream
* into the given output stream until the end of the input stream is reached.
* Returns the amount of bytes actually read/written.
*/
public static int pump(InputStream in, OutputStream out, int amount) throws IOException{
return pump(in, out, amount, new byte[2048]);
}
/**
* Writes the bytes read from the given input stream into the given output
* stream until the end of the input stream is reached. Returns the amount of
* bytes actually read/written. Uses the given byte array as the buffer.
*/
public static int pump(InputStream in, OutputStream out, byte [] buf) throws IOException{
if (buf.length==0)
throw new IllegalArgumentException("Cannot use a 0 length buffer");
int count;
int amountRead = 0;
while ((count = in.read(buf))!=-1){
out.write(buf,0,count);
amountRead += count;
}
return amountRead;
}
/**
* Writes up to the given amount of bytes read from the given input stream
* into the given output stream until the end of the input stream is reached.
* Returns the amount of bytes actually read/written. Uses the given byte array
* as the buffer.
*/
public static int pump(InputStream in, OutputStream out, int amount, byte [] buf) throws IOException{
if (buf.length == 0)
throw new IllegalArgumentException("Cannot use a 0 length buffer");
int amountRead = 0;
while (amount > 0){
int amountToRead = amount > buf.length ? buf.length : amount;
int count = in.read(buf, 0, amountToRead);
if (count==-1)
break;
out.write(buf,0,count);
amount -= count;
amountRead += count;
}
return amountRead;
}
/**
* Reads from the given InputStream until its end and returns a byte array
* of the contents. The input stream is not <code>close</code>d by this
* method.
*/
public static byte [] readToEnd(InputStream in) throws IOException{
byte [] buf = new byte[2048];
int amountRead = 0;
int count = 0;
while ((count = in.read(buf, amountRead, buf.length-amountRead)) > 0){
amountRead += count;
if (amountRead == buf.length){
byte [] oldBuf = buf;
buf = new byte[oldBuf.length*2];
System.arraycopy(oldBuf, 0, buf, 0, amountRead);
}
}
byte [] arr = new byte[amountRead];
System.arraycopy(buf, 0, arr, 0, amountRead);
return arr;
}
/**
* Reads the specified amount of bytes from the specified input stream and
* returns the resulting array. Throws an <code>EOFException</code> if the
* stream ends before the specified amount of bytes is read.
*/
public static byte [] read(InputStream in, int amount) throws IOException{
ByteArrayOutputStream buf = new ByteArrayOutputStream(amount);
if (pump(in, buf, amount) != amount)
throw new EOFException();
return buf.toByteArray();
}
/**
* Loads and returns data from the specified URL.
*/
public static byte [] load(URL url, boolean allowCache) throws IOException{
InputStream in = inputStreamForURL(url, allowCache);
try{
return readToEnd(in);
} finally{
try{
in.close();
} catch (IOException e){}
}
}
/**
* Reads all the information from the given InputStream and returns it as
* plain text by using the default system encoding. Note that this method
* doesn't close the given InputStream, that is left to the user.
*/
public static String loadText(InputStream in) throws IOException{
return new String(readToEnd(in));
}
/**
* Loads the text from the given URL and returns it as a string.
*
* @throws IOException if the given URL does not exist or an I/O error occurs
* while accessing it.
*/
public static String loadText(URL url, boolean allowCache) throws IOException{
return new String(load(url, allowCache));
}
/**
* Loads the given text file from the local drive, converts it to a String and
* returns the String.
*
* @throws IOException if the file does not exist or loading failed.
*/
public static String loadTextFile(File file) throws IOException{
if (!file.exists())
throw new IOException("File does not exist");
InputStream in = new FileInputStream(file);
String text = loadText(in);
in.close();
return text;
}
/**
* Loads a text file with the given name from the local drive, converts it to
* a String and returns the String.
*
* @throws IOException if the file does not exist or loading failed.
*/
public static String loadTextFile(String filename) throws IOException{
return loadTextFile(new File(filename));
}
/**
* Compares the 2 given sub arrays. Returns true if they are equal, false
* otherwise.
*
* @throws ArrayIndexOutOfBounds if
* <UL>
* <LI> <code>offset1</code> or <code>offset2</code> are negative.
* <LI> length is negative.
* <LI> <code>offset1+length</code> is bigger than <code>arr1.length</code>
* <LI> <code>offset2+length</code> is bigger than <code>arr2.length</code>
* </UL>
*/
public static boolean equal(byte [] arr1, int offset1, byte [] arr2, int offset2, int length){
if ((offset1<0)||(offset2<0)||(length<0)||(offset1+length>arr1.length)||(offset2+length>arr2.length))
throw new ArrayIndexOutOfBoundsException();
for (int i=0;i<length;i++){
if (arr1[offset1+i]!=arr2[offset2+i])
return false;
}
return true;
}
/**
* Returns a <code>URL</code> corresponding to the specified <code>File</code>
* or <code>null</code> if the <code>File</code> cannot be converted into a
* <code>URL</code>.
* NOTE: This is copied from the JDK1.3 source, File.java
*/
public static URL fileToURL(File file){
try{
String path = file.getAbsolutePath();
if (File.separatorChar != '/')
path = path.replace(File.separatorChar, '/');
if (!path.startsWith("/"))
path = "/" + path;
if (!path.endsWith("/") && file.isDirectory())
path = path + "/";
return new URL("file", "", path);
} catch (MalformedURLException e){
return null;
}
}
/**
* Creates and returns a new <code>java.util.Properties</code> object loaded
* from the specified <code>InputStream</code>.
*/
public static Properties loadProperties(InputStream in) throws IOException{
return loadProperties(in, new Properties());
}
/**
* Loads properties from the specified <code>InputStream</code> into the
* specified <code>Properties</code> object. Returns the passed
* <code>Properties</code> object.
*/
public static Properties loadProperties(InputStream in, Properties props) throws IOException{
if (in == null)
return null;
props.load(in);
return props;
}
/**
* Similar to the {@link #loadProperties(InputStream)} method, but closes
* the specified <code>InputStream</code> at the end of its operation.
*/
public static Properties loadPropertiesAndClose(InputStream in) throws IOException{
return loadPropertiesAndClose(in, new Properties());
}
/**
* Similar to the {@link #loadProperties(InputStream, Properties)} method,
* but closes the specified <code>InputStream</code> at the end of its
* operation.
*/
public static Properties loadPropertiesAndClose(InputStream in, Properties props) throws IOException{
try{
return loadProperties(in, props);
} finally{
try{
in.close();
} catch (IOException e){}
}
}
/**
* Creates and returns a new <code>java.util.Properties</code> object loaded
* from the specified <code>File</code>.
*/
public static Properties loadProperties(File file) throws IOException{
return loadPropertiesAndClose(new FileInputStream(file));
}
/**
* Creates and returns a new <code>java.util.Properties</code> object loaded
* from the specified <code>URL</code>.
* <code>allowCache</code> specifies whether the data may be retrieved from
* the cache instead of being actually retrieved.
*/
public static Properties loadProperties(URL url, boolean allowCache) throws IOException{
return loadProperties(url, allowCache, new Properties());
}
/**
* Loads properties from the specified <code>URL</code> into the specified
* </code>Properties</code> object. Returns the passed
* <code>Properties</code> object.
* <code>allowCache</code> specifies whether the data may be retrieved from
* the cache instead of being actually retrieved.
*/
public static Properties loadProperties(URL url, boolean allowCache, Properties props) throws IOException{
return loadPropertiesAndClose(inputStreamForURL(url, allowCache), props);
}
/**
* Loads and caches the contents of the specified URL. Calls to any of the
* methods that load from URLs in this class will use the cached data. Calling
* this method with an already cached URL will cause it to be loaded again. If
* an <code>IOException</code> occurs while loading the data, the cache
* remains unchanged.
*/
public static void cacheURL(URL url) throws IOException{
cacheData(url, load(url, false));
}
/**
* Forces the data mapped to the specified URL to be the specified data.
* This method is useful when one part of an application wants to generate
* or specify data for another part.
*/
public static void cacheData(URL url, byte [] data){
urlCache.put(url, data);
}
/**
* Returns whether the specified URL is cached.
*/
public static boolean isURLCached(URL url){
return urlCache.containsKey(url);
}
/**
* Returns an <code>InpuStream</code> for reading the data at the specified
* URL. If <code>allowCache</code> is <code>true</code>, and the URL is cached,
* a <code>ByteArrayInpuStream</code> with the cached data is returned.
*/
public static InputStream inputStreamForURL(URL url, boolean allowCache) throws IOException{
byte [] cached = null;
if (allowCache)
cached = (byte [])urlCache.get(url);
return cached == null ? url.openStream() : new ByteArrayInputStream(cached);
}
/**
* Loads data from the specified URLs asynchronously in a background thread.
* Once all the data is loaded, it is passed to the specified
* <code>DataReceiver</code>. <code>id</code> is a convenience allowing the
* receiver to identify the data - it is merely passed back to the receiver.
*/
public static void loadAsynchronously(URL [] urls, Object id, DataReceiver receiver, boolean allowCache){
Thread asyncReader =
new Thread(new UrlDataReader((URL[])urls.clone(), id, receiver, allowCache),
"AsyncThread-" + (++UrlDataReader.threadCount));
asyncReader.setDaemon(true);
asyncReader.start();
}
/**
* Similar to <code>loadAsynchronously</code>, but returns only when all the
* data has been loaded and passed off to the receiver.
*/
public static void loadSynchronously(URL [] urls, Object id, DataReceiver receiver, boolean allowCache){
new UrlDataReader((URL[])urls.clone(), id, receiver, allowCache).run();
}
/**
* The callback interface for asynchronous reading of data.
*/
public static interface DataReceiver{
/**
* Gets called when all the data is loaded.
* The <code>IOException</code> array holds the exceptions thrown while
* loading. The indices in all the arrays correspond.
*/
void dataRead(URL [] urls, Object id, byte [][] data, IOException [] exceptions);
}
/**
* Reads data from URLs.
*/
private static class UrlDataReader implements Runnable{
/**
* The number of <code>Threads</code> running <code>UrlDataReader</code>s
* already created.
*/
public static int threadCount = 0;
/**
* The URLs to load data from.
*/
private final URL [] urls;
/**
* The identifier of this download.
*/
private final Object id;
/**
* The callback <code>DataReceiver</code>.
*/
private final DataReceiver receiver;
/**
* Whether it is allowed for the data to be retrieved from cache.
*/
private final boolean allowCache;
/**
* The data.
*/
private final byte [][] data;
/**
* The <code>IOExceptions</code> thrown while loading the data.
*/
private final IOException [] exceptions;
/**
* Creates a new <code>UrlDataReader</code> with the specified id, to load
* data from the specified URLs and report back to the specified
* <code>DataReceiver</code>.
*/
public UrlDataReader(URL [] urls, Object id, DataReceiver receiver, boolean allowCache){
this.urls = urls;
this.id = id;
this.receiver = receiver;
this.allowCache = allowCache;
this.data = new byte[urls.length][];
this.exceptions = new IOException[urls.length];
}
/**
* Reads the data and reports back to the receiver.
*/
public void run(){
for (int i = 0; i < urls.length; i++){
try{
data[i] = load(urls[i], allowCache);
} catch (IOException e){
exceptions[i] = e;
}
}
receiver.dataRead(urls, id, data, exceptions);
}
}
}
Related examples in the same category