ca.macewan.lmsreporter.api
Class AbstractAnalyzer

java.lang.Object
  extended byca.macewan.lmsreporter.api.AbstractAnalyzer
All Implemented Interfaces:
Analyzer, Runnable

public abstract class AbstractAnalyzer
extends Object
implements Analyzer

Convenience class that implements the Analyzer interface. It offers a default implementation for all interface methods except run (as required by Runnable, which Analyzer extends). Additionally, this class provides some utility methods to work with WebCT global database CSV files, to fire AnalysisEvent events, and to access the source and output files.

A typical implementation can simply extend this class and implement the run method to perform the analysis. In the run method, use the updateXXXX methods to notify the user of progress and errors. Also, check regularly for isRunning, so the run method can pre-emptively return, allowing the analysis to be canceled gracefully.

Version:
0.1
Author:
Erwin Veugelers

Field Summary
protected  AnalysisEvent ae
          The event object used (and re-used) for sending events.
 
Constructor Summary
AbstractAnalyzer()
          Constructor, which should always run.
 
Method Summary
 void addAnalysisListener(AnalysisListener al)
          Adds an AnalysisListener to the list of listeners.
 void analyze()
          The method called by the application to start the analysis process.
protected  void finishAnalysis(String info)
          Convenience method to notify the application that your analysis is done.
protected  void fireAnalysisUpdate()
          Sends the AnalysisEvent ae to all registered AnalysisListeners.
 String getCopyright()
          Returns a short copyright information string for your Analyzer.
 String getDescription()
          The application will read a description for this Analyzer object which will be used in the analyzer selection list.
protected  File getGlobalDBFile()
          Returns the WebCT comma-separated Global Database file to process.
protected  List getGlobalDBKeys()
          Utility method to read the first significant line of the global database file and return the database field names as a List.
protected  File getOutputFile()
          Returns the output file to save the analysis results in.
 Component getSettingsUI()
          Called when the implementing class is selected by the user.
protected  File getSnapshotFile()
          Returns the WebCT IMS Enterprise XML snapshot file to process.
 boolean isRunning()
          Returns true if the analysis is currently running.
 boolean isWaitingForUI()
          Returns true if the analysis is currently suspended, waiting for some user interface action to complete.
 void postProcess()
          Called immediately after the analyze method.
 void preProcess()
          Called immediately before the analyze method.
protected  HashMap readGlobalDB()
          Utility method to read the whole global database CSV file into a HashMap.
protected  HashMap readGlobalDBField(String fieldName)
          Reads the global database file, and creates a HashMap where the keys are each user's WebCT ID, and the values are the value of the requested field as strings.
protected  HashMap readGlobalDBField(String fieldName, double phaseStart, double phaseLength)
          Creates a HashMap with the values of the requested field (see readGlobalDBField(String) for more information).
 void removeAnalysisListener(AnalysisListener al)
          Removes an AnalysisListener from the list of listeners.
abstract  void run()
          Runs the actual analysis.
 void setDescription(String desc)
          The application may set a description for this Analyzer object which will be used in the analyzer selection list.
protected  void setRunning(boolean isRunning)
          Causes isRunning to return the value passed in here.
 void setSources(File snapshotFile, File globalDBFile, File outFile)
          Accepts and stores the two input files and the output file used by the analysis.
 void setWaitingForUI(boolean waitForUI)
          Called by the application when the additional user interface action has completed.
 void stopAnalysis(String info)
          Cause the analysis to stop prematurely.
 String toString()
          Overrides Object.toString() to provide a human-readable description to be displayed in the application's analysis selection list.
protected  void updateProgress(double progress)
          Utility method to aid in the sending of progress AnalysisEvents.
protected  void updateProgress(double progress, String info)
          Utility method to aid in the sending of progress AnalysisEvents.
protected  void updateWithError(Throwable t)
          Utility method to aid in the sending of error AnalysisEvents.
protected  void updateWithUIRequest(Component c, String ttl)
          If, for some reason, your analysis needs to interact with the user in the midst of the anlysis process, this method will help send the appropriate AnalysisEvent.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
 

Field Detail

ae

protected AnalysisEvent ae
The event object used (and re-used) for sending events. See fireAnalysisUpdate.

Constructor Detail

AbstractAnalyzer

public AbstractAnalyzer()
Constructor, which should always run. Subclasses should ensure it does. It sets up event listening and some other stuff.

Method Detail

setSources

public void setSources(File snapshotFile,
                       File globalDBFile,
                       File outFile)
Accepts and stores the two input files and the output file used by the analysis. Use the methods getSnapshotFile, getGlobalDBFile and getOutputFile to gain access to these files.

Specified by:
setSources in interface Analyzer
Parameters:
snapshotFile - The WebCT IMS Enterprise snapshot export file.
globalDBFile - The WebCT Global Database CVS export file.
outFile - The output file.

analyze

public final void analyze()
The method called by the application to start the analysis process. It creates a new thread to call your run method. Please note that this method is final, so it can't be overridden. This is to ensure the method can properly initialize some event stuff and the new thread. If you need to do things before run starts, use preProcess instead.

Specified by:
analyze in interface Analyzer

preProcess

public void preProcess()
                throws AnalysisException
Called immediately before the analyze method. Any last-minute prep work that didn't fit in the settings user interface produced by getSettingsUI should be done here.

The implementation of this method in this class is empty.

Specified by:
preProcess in interface Analyzer
Throws:
AnalysisException - Most checked exceptions that may occur in this method should be wrapped inside an AnalysisException for proper notification to the user.

postProcess

public void postProcess()
                 throws AnalysisException
Called immediately after the analyze method. Any cleanup work that couldn't be done in the analysis itself should be done here.

The implementation of this method in this class is empty.

Specified by:
postProcess in interface Analyzer
Throws:
AnalysisException - Most checked exceptions that may occur in this method should be wrapped inside an AnalysisException for proper notification to the user.

getSettingsUI

public Component getSettingsUI()
Called when the implementing class is selected by the user. Should return a component, probably a javax.swing.JPanel or something, that gives the user the opprtunity to set options on the Analyzer object before the analysis starts.

The implementation of this method in this class returns a single label with an arcane "e;No settings "e; message.

Specified by:
getSettingsUI in interface Analyzer
Returns:
The component to be used for user input.

isRunning

public boolean isRunning()
Returns true if the analysis is currently running. Works as advertised.

Specified by:
isRunning in interface Analyzer
Returns:
Whether or not the analysis is currently running.

setRunning

protected void setRunning(boolean isRunning)
Causes isRunning to return the value passed in here. You do not need to set running to true when your analysis starts, since this class' analyze method already does that for you. You should, however, set running to false from any method if you need to cancel the analysis. Your analysis implementation has the responsibility to return gracefully when isRunning returns false.


isWaitingForUI

public boolean isWaitingForUI()
Returns true if the analysis is currently suspended, waiting for some user interface action to complete.

If you do need to display user interface elements while the analysis is running, you need to:

Specified by:
isWaitingForUI in interface Analyzer
Returns:
Whether or not the analysis is currently waiting for the user interface.

setWaitingForUI

public void setWaitingForUI(boolean waitForUI)
Called by the application when the additional user interface action has completed. See isWaitingForUI for more information.

Specified by:
setWaitingForUI in interface Analyzer
Parameters:
waitForUI - true or false.

getSnapshotFile

protected File getSnapshotFile()
Returns the WebCT IMS Enterprise XML snapshot file to process. See setSources as well.

Returns:
The snapshot file.

getGlobalDBFile

protected File getGlobalDBFile()
Returns the WebCT comma-separated Global Database file to process. See setSources as well.

Returns:
The global database file.

getOutputFile

protected File getOutputFile()
Returns the output file to save the analysis results in. See setSources as well.

Returns:
The output file.

finishAnalysis

protected void finishAnalysis(String info)
Convenience method to notify the application that your analysis is done. Should typically be the last action in your run method. It fires a AnalysisEvent.TYPE_FINISHED event and sets runnig to false.

Parameters:
info - Some informational message to display to the user along with the event.

stopAnalysis

public void stopAnalysis(String info)
Cause the analysis to stop prematurely. The application calls this method when the user cancels the analysis, for example. This implementation sets running to false and sends an event of type AnalysisEvent.TYPE_FINISHED back to the application.

Note: This implementation does not interrupt or force the thread running the analysis to stop, other than setting running to false. Your run method should check isRunning frequently and return gracefully if it returns false.

Specified by:
stopAnalysis in interface Analyzer
Parameters:
info - Some informational message regarding this stop request.

run

public abstract void run()
Runs the actual analysis. Your analysis process should go here.

Make sure to update the application regularly about progress, notify about errors, and send an AnalysisEvent.TYPE_FINISHED event at the end. (See finishAnalysis.)

A typical analysis might look something like this:
while (isRunning() && stillReadingData) {
	// Read data step.
	updateProgress(progressValue);
}

if (!isRunning()) {
	return;
}

while (isRunning() && stillProducingOutput) {
	// Write data step.
	updateProgress(progressValue);
}

if (!isRunning()) {
	return;
}

 // Some finishing-up code.

finishAnalysis("Analysis complete");
 

Specified by:
run in interface Runnable

readGlobalDB

protected HashMap readGlobalDB()
                        throws IOException
Utility method to read the whole global database CSV file into a HashMap. The map's keys are the users WebCT IDs, and each value is another HashMap with the global database field names as keys and the user record's values as strings. If a field in the database is empty, the map value will be an empty string (""), and not null.

Beware that this resulting map can be very large with large database files, and can cause OutOfMemoryErrors. If you are just interested in the value of one or two fields, it is much more resource-efficient to use readGlobalDBField(String) instead.

Returns:
The global database file parsed into a map of maps.
Throws:
IOException - If anything goes wrong reading the file, including when getGlobalDBFile returns null.

readGlobalDBField

protected HashMap readGlobalDBField(String fieldName)
                             throws IOException
Reads the global database file, and creates a HashMap where the keys are each user's WebCT ID, and the values are the value of the requested field as strings. If a field in the database is empty, the map value will be an empty string (""), and not null. If the field doesn't exist, all values will be empty strings, and no exception is thrown.

Parameters:
fieldName - The global database field to read into the values of the resulting map.
Returns:
A map object mapping WebCT IDs to the requested field's values.
Throws:
IOException - If anything goes wrong reading the file, including when getGlobalDBFile returns null.

readGlobalDBField

protected HashMap readGlobalDBField(String fieldName,
                                    double phaseStart,
                                    double phaseLength)
                             throws IOException
Creates a HashMap with the values of the requested field (see readGlobalDBField(String) for more information). The only difference with the other version of the method is that this version lets you continue progress tracking inside the method. To accomplish this, pass the current state of progress when you call the method as argument phaseStart and the amount of progress covered by this method as phaseLength, so that progressLevelAfterMethodRuns = phaseStart + phaseLength. Please note that this value is not returned or in any other way available, so keep track of progress levels yourself based on phaseStart and phaseLength.

Parameters:
fieldName - The global database field to read into the values of the resulting map.
phaseStart - The level of analysis progress when the method is called.
phaseLength - The amount of analysis progress covered by this method.
Returns:
A map object mapping WebCT IDs to the requested field's values.
Throws:
IOException - If anything goes wrong reading the file, including when getGlobalDBFile returns null.

getGlobalDBKeys

protected List getGlobalDBKeys()
                        throws IOException
Utility method to read the first significant line of the global database file and return the database field names as a List.

Returns:
The list containing all global database keys.
Throws:
IOException - If anything goes wrong reading the file, including when getGlobalDBFile returns null.

addAnalysisListener

public void addAnalysisListener(AnalysisListener al)
Adds an AnalysisListener to the list of listeners. You don't need to override this unless you need some really special event handling.

Specified by:
addAnalysisListener in interface Analyzer
Parameters:
al - The listener to be added to the list.

removeAnalysisListener

public void removeAnalysisListener(AnalysisListener al)
Removes an AnalysisListener from the list of listeners. You don't need to override this unless you need some really special event handling.

Specified by:
removeAnalysisListener in interface Analyzer
Parameters:
al - The listener to be removed from the list.

updateProgress

protected void updateProgress(double progress)
Utility method to aid in the sending of progress AnalysisEvents. The method sets all the appropriate event fields, including a generic information string based on an estimate of the remaining time for the analysis to complete. It then fires the event as well, so no need to call fireAnalysisUpdate yourself.

Parameters:
progress - The current progress level to be reported.

updateProgress

protected void updateProgress(double progress,
                              String info)
Utility method to aid in the sending of progress AnalysisEvents. The method sets all the appropriate event fields, including the information string you pass. It then fires the event as well, so no need to call fireAnalysisUpdate yourself.

Parameters:
progress - The current progress level to be reported.
info - The information string to include in the event.

updateWithError

protected void updateWithError(Throwable t)
Utility method to aid in the sending of error AnalysisEvents. The method sets all the appropriate event fields, including the Throwable you pass. This should typically be a checked exception, but the method doesn't make any restrictions. It fires the event as well, so no need to call fireAnalysisUpdate yourself.

Note: Sending an error-type event will cause the application to assume the analysis finished prematurely. Be sure to drop out of your analysis after firing this event.

Parameters:
t - The error to be reported.

updateWithUIRequest

protected void updateWithUIRequest(Component c,
                                   String ttl)
If, for some reason, your analysis needs to interact with the user in the midst of the anlysis process, this method will help send the appropriate AnalysisEvent. It also puts the object in a waiting state so that isWaitingForUI returns true. You are responsible for creating the user interface elements, to be passed as a single component to this method. You also provide a title string to caption the resulting dialog. You are also responsible for adding any listeners to your user interface elements so you can respond to the user input. Finally you need to make sure your run method is suspended while the user interface is being displayed. See isWaitingForUI for more information.

Parameters:
c - The component containing your user interface elements.
ttl - The title to caption the resulting application dialog.

fireAnalysisUpdate

protected void fireAnalysisUpdate()
Sends the AnalysisEvent ae to all registered AnalysisListeners. If you are using the updateXXXX methods, you don't need to call this method. Only if you set the fields of the ae field yourself do you need to call this method to send the event.


setDescription

public void setDescription(String desc)
The application may set a description for this Analyzer object which will be used in the analyzer selection list.

Specified by:
setDescription in interface Analyzer
Parameters:
desc - The description of this Analyzer.

getDescription

public String getDescription()
The application will read a description for this Analyzer object which will be used in the analyzer selection list.

Actually, this is not accurate. The application's list is populated using toString, but the implementation of toString returns getDescription(), so overriding either method will allow you to control the result.

To control the output of this method, call setDescription in your initialization code rather than overriding this method.

Specified by:
getDescription in interface Analyzer
Returns:
The description of this Analyzer.

getCopyright

public String getCopyright()
Returns a short copyright information string for your Analyzer. The default implementation returns null, so override at will. The information is displayed near your Analyzer's settings interface in a small label, with callous disregard for the length of the text. Keep it short.

Specified by:
getCopyright in interface Analyzer
Returns:
The copyright information for this Analyzer.

toString

public String toString()
Overrides Object.toString() to provide a human-readable description to be displayed in the application's analysis selection list. This implementation simply returns getDescription().