/*
* DataLoader.java 1.3 04/06/98
*
* Copyright (c) 1997, 1998 by Kevin Swan. All rights reserved.
*
* I reserve all rights to this software. You may use it and
* distribute it freely, provided you do not remove this header
* and attribute partial credit to the author, Kevin Swan.
*
* 14 Apr 1998 Created.
* Released as version 1.0.
* 20 Apr 1998 Added the refreshing ability.
* Increased its responsibilities to include text
* wrapping. Now, all data returned from the
* DataLoader will be already line wrapped.
* Released as version 1.1.
* 21 Apr 1998 Corrected a bug in the Thread management.
* Expanded variable documentation, added DELAY constant
* Released as version 1.2.
* 04 Jun 1998 Added ability to specify not to apply line wrapping.
* Added constructor
* public DataLoader (URL, Font, int width, boolean)
* The default is that it will wrap text. If the
* new constructor is used with a boolean argument of
* false, then no line wrapping is applied.
* Released as version 1.3.
*/
/*
* Commented out for unbundled distribution.
*
* package kevin.applets.textscroll;
*/
import java.net.URL;
import java.awt.Font;
import java.net.URLConnection;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.Vector;
/**
* This class is used to load the text data for TextScroll in an
* asynchronous way. It will also apply the automatic line wrapping
* to the data. It provides a refresh()
method which
* can be used to force this DataLoader
to reload
* the data from the URL it was given when it was constructed.
*
* @version 1.3, 04 Jun 1998
* @author Kevin Swan, 013639s@dragon.acadiau.ca
*/
public class DataLoader implements Runnable {
/**
* The current version of this class.
*/
public static final String VERSION = "1.3";
/**
* This is how long the thread should sleep when waiting
* for a refresh()
request or a stop()
* request. Default is half a second.
*/
private static final int DELAY = 500;
/** The thread of execution. This allows it to run asynchronously. */
private Thread thread = null;
/** The URL to load the data from. */
private URL url = null;
/** The array to store the data in. */
private String[] textData = null;
/** Variable used to flag if the data is ready or not. */
private boolean dataReady = false;
/** Variable used to flag if an error occurred. */
private boolean errorOccurred = false;
/**
* Variable used to indicate that this DataLoader
* should reload the data from the URL it was given in the
* constructor.
*/
private boolean shouldRefresh = true;
/**
* Variable used to indicate that this DataLoader
* should apply line wrapping to the text after it is loaded
* before delivering it to the caller.
*/
private boolean shouldWrap;
/**
* Variable used to flag when the DataLoader should terminate
* its thread.
*/
private boolean isAlive = false;
/** The starting font to use when applying line wrapping. */
private Font startingFont;
/** The width to assume when applying line wrapping. */
private int width;
/**
* Constructor. It expects the URL of the data file to load from.
* The constructed DataLoader
will apply line
* wrapping to the retrieved data.
*
* @param url The URL to load the data from.
* @param startingFont The starting Font, used for line wrapping.
* @param width The width of the starting font, use for line
* wrapping.
*/
public DataLoader (URL url, Font startingFont, int width) {
this (url, startingFont, width, true);
}
/**
* Constructor. It expects the URL of the data file to load from.
* It returns immediately.
*
* @param url The URL to load the data from.
* @param startingFont The starting Font, used for line wrapping.
* @param width The width of the starting font, use for line
* wrapping.
* @param shouldWrap A boolean indicating whether or not this
* DataLoader
should apply line
* wrapping or not.
*/
public DataLoader (URL url, Font startingFont, int width, boolean shouldWrap) {
this.url = url;
this.dataReady = false;
this.shouldRefresh = true;
this.shouldWrap = shouldWrap;
this.errorOccurred = false;
this.startingFont = startingFont;
this.width = width;
this.isAlive = true;
this.thread = new Thread (this);
this.thread.start ();
}
/**
* This method is called to reload the data from the URL. It returns
* immediately. It performs automatic line wrapping based on the
* width and Font
given in the constructor.
*/
public void refresh () {
this.shouldRefresh = true;
}
/**
* This method actually loads the data. It sets the internal flags
* appropriately, such that calls to dataReady()
and
* errorOccurred
will always return correct information.
*/
public void run () {
outer:
while (isAlive) {
while (!shouldRefresh) {
try {
this.thread.sleep (DataLoader.DELAY);
if (!isAlive) {
break outer;
}
} catch (InterruptedException ie) {
isAlive = false;
break outer;
}
}
URLConnection dataConnection = null;
DataInputStream dis = null;
try {
dataConnection = this.url.openConnection ();
} catch (IOException ioe) {
this.errorOccurred = true;
return;
}
try {
dis = new DataInputStream (dataConnection.getInputStream ());
} catch (IOException ioe) {
this.errorOccurred = true;
return;
}
String line = null;
Vector data = new Vector ();
int index;
while (true) {
try {
line = dis.readLine ();
} catch (IOException ioe) {
this.errorOccurred = true;
return;
}
if (line == null)
break;
data.addElement (line + "\n");
} /* while */
this.textData = new String[data.size ()];
for (int i = 0 ; i < data.size () ; i++)
this.textData[i] = new String ((String) data.elementAt (i));
try {
dis.close ();
} catch (IOException ioe) {
System.err.println ("IOException closing DataInputStream on text data.");
/* At this point, we can keep going. Consider this non-fatal. */
}
/* Apply line wrapping, if we should. */
if (this.shouldWrap) {
LineWrapManager wrapManager = new LineWrapManager (this.textData,
this.startingFont);
String[] tmpArr = wrapManager.wrapForWidth (this.width);
this.textData = new String [tmpArr.length];
System.arraycopy (tmpArr, 0, this.textData, 0, tmpArr.length);
}
/* Finally, get rid of any newlines that made it past the line wrapper. */
for (int i = 0 ; i < this.textData.length ; i++)
this.textData [i] = this.textData [i].trim ();
this.dataReady = true;
this.shouldRefresh = false;
}
} /* run () */
/**
* Method called to terminate the Thread.
*/
public void stop () {
this.isAlive = false;
}
/**
* Method called when the thread is started.
*/
public void start () {
if (this.isAlive)
return;
this.isAlive = true;
this.thread = new Thread (this);
this.thread.start ();
}
/**
* Method for determining when the data has arrived.
*
* @return true
if the data is ready, false
* otherwise.
*/
public boolean dataReady () {
return this.dataReady;
}
/**
* Method for determining if an error has occurred.
*
* @return true
if an error occurred, and the data is never
* going to arrive, false
if no error has occurred.
*/
public boolean errorOccurred () {
return this.errorOccurred;
}
/**
* Method for getting the actual data. If it hasn't arrived yet, this
* method returns null
.
*
* @return The array of String
s which are the data, if it
* is here, or null
if it is still loading or if an
* error occurred.
*/
public String[] getData () {
if (!this.dataReady () || this.errorOccurred ())
return null;
else
return textData;
}
}