/*******************************************************************************
* Copyright (c) 2011 Arapiki Solutions Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* "Peter Smith <psmith@arapiki.com>" - initial API and
* implementation and/or initial documentation
*******************************************************************************/
package com.buildml.utils.files;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
* A subclass of FileInputStream that allows us to monitor progress as we read through
* a file. This is extremely useful for long-running applications that read/parse
* very large files. It's important to give the end user some feedback on how much of
* the file has been processed.
*
* This class is intended to be used as a drop-in replacement for FileInputStream.
*
* @author "Peter Smith <psmith@arapiki.com>"
*/
public class ProgressFileInputStream extends FileInputStream {
/*=====================================================================================*
* FIELDS/TYPE
*=====================================================================================*/
/** The total length of the file we're reading. */
private long fileLength;
/** Our current position within the file. */
private long filePos;
/** The thread that'll keep the caller updated on the progress of reading the file. */
private Thread progressReporter;
/** Flag set to true when the file has been completely read. */
private boolean fileDone = false;
/*=====================================================================================*
* NESTED CLASSES
*=====================================================================================*/
/**
* A Thread class that'll report the current progress of reading through
* a ProgressFileInputStream. This thread continues until it's terminated, which is usually
* when the file is closed.
*/
private class ProgressReporterThread extends Thread {
/** The number of seconds between each successive progress report. */
private int intervalInSeconds;
/** The callback object to invoke. */
private ProgressFileInputStreamListener listener;
/**
* Create a new ProgressReporterThread object.
* @param listener The listener object that'll receive our progress updates.
* @param intervalInSeconds The number of seconds between consecutive progress reports.
*/
public ProgressReporterThread(ProgressFileInputStreamListener listener,
int intervalInSeconds) {
this.intervalInSeconds = intervalInSeconds;
this.listener = listener;
}
/*-------------------------------------------------------------------------------------*/
/**
* The main loop of the listener thread. This method invokes the listener's progress()
* method on a periodic basis.
* @see java.lang.Thread#run()
*/
@Override
public void run() {
/* continuously report progress, until the file is closed */
do {
/* sleep for the desired interval */
try {
sleep(intervalInSeconds * 1000);
} catch (InterruptedException e) {
/* do nothing */
}
/* report */
listener.progress(filePos, fileLength, (int)(100 * (float)filePos / (float)fileLength));
} while (!fileDone);
/* just to make sure we report 100% */
listener.progress(fileLength, fileLength, 100);
/* this will help the progress meter know when to stop showing results */
listener.done();
}
}
/*=====================================================================================*
* CONSTRUCTORS
*=====================================================================================*/
/**
* Create a new ProgressFileInputStream object. This is identical to a FileInputStream object,
* except that a listener object can be attached to the stream in order to monitor progress.
*
* @param name The name of the file to be read.
* @param listener The ProgressFileInputStreamListener object that will be notified of progress.
* @param intervalInSeconds The interval (in seconds) between consecutive progress reports.
* @throws FileNotFoundException If the file wasn't found.
*/
public ProgressFileInputStream(String name, ProgressFileInputStreamListener listener,
int intervalInSeconds) throws FileNotFoundException {
/* most of the work is delegated to the parent class */
super(name);
/* determine the total length of the file */
File file = new File(name);
fileLength = file.length();
/* the position counter starts at 0 */
filePos = 0;
/* start our progress reporter thread */
progressReporter = new ProgressReporterThread(listener, intervalInSeconds);
progressReporter.start();
}
/*=====================================================================================*
* PUBLIC METHODS (all are overrides of standard FileInputStream methods).
*=====================================================================================*/
/**
* A wrapper around the standard read() method.
* @see java.io.FileInputStream#read()
*/
@Override
public int read() throws IOException {
int byteRead = super.read();
if (byteRead != -1) {
filePos++;
}
return byteRead;
}
/*-------------------------------------------------------------------------------------*/
/**
* A wrapper around the standard read() method.
* @see java.io.FileInputStream#read(byte[], int, int)
*/
@Override
public int read(byte[] b, int off, int len) throws IOException {
int lengthRead = super.read(b, off, len);
if (lengthRead > 0) {
filePos += lengthRead;
}
return lengthRead;
}
/*-------------------------------------------------------------------------------------*/
/**
* A wrapper around the standard read() method.
* @see java.io.FileInputStream#read(byte[])
*/
@Override
public int read(byte[] b) throws IOException {
int lengthRead = super.read(b);
if (lengthRead > 0) {
filePos += lengthRead;
}
return lengthRead;
}
/*-------------------------------------------------------------------------------------*/
/**
* A wrapper around the standard skip() method.
* @see java.io.FileInputStream#skip(long)
*/
@Override
public long skip(long n) throws IOException {
long bytesSkipped = super.skip(n);
if (bytesSkipped > 0) {
filePos += bytesSkipped;
}
return bytesSkipped;
}
/*-------------------------------------------------------------------------------------*/
/**
* A wrapper around the standard close() method.
* @see java.io.FileInputStream#close()
*/
@Override
public void close() throws IOException {
fileDone = true;
super.close();
}
/*-------------------------------------------------------------------------------------*/
}