/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* Loader.java
* Copyright (C) 2002 University of Waikato, Hamilton, New Zealand
*
*/
package weka.gui.beans;
import weka.core.Instance;
import weka.core.Instances;
import weka.core.converters.ArffLoader;
import weka.core.converters.DatabaseLoader;
import weka.core.converters.FileSourcedConverter;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.beancontext.BeanContext;
import java.io.IOException;
import java.io.ObjectStreamException;
import java.util.Enumeration;
import java.util.Vector;
import javax.swing.JButton;
/**
* Loads data sets using weka.core.converter classes
*
* @author <a href="mailto:mhall@cs.waikato.ac.nz">Mark Hall</a>
* @version $Revision: 1.23 $
* @since 1.0
* @see AbstractDataSource
* @see UserRequestAcceptor
*/
public class Loader
extends AbstractDataSource
implements Startable, UserRequestAcceptor, WekaWrapper,
EventConstraints {
/** for serialization */
private static final long serialVersionUID = 1993738191961163027L;
/**
* Holds the instances loaded
*/
private transient Instances m_dataSet;
/**
* Holds the format of the last loaded data set
*/
private transient Instances m_dataFormat;
/**
* Global info for the wrapped loader (if it exists).
*/
protected String m_globalInfo;
/**
* Thread for doing IO in
*/
private LoadThread m_ioThread;
private static int IDLE = 0;
private static int BATCH_LOADING = 1;
private static int INCREMENTAL_LOADING = 2;
private int m_state = IDLE;
/**
* Loader
*/
private weka.core.converters.Loader m_Loader = new ArffLoader();
private InstanceEvent m_ie = new InstanceEvent(this);
/**
* Keep track of how many listeners for different types of events there are.
*/
private int m_instanceEventTargets = 0;
private int m_dataSetEventTargets = 0;
/** Flag indicating that a database has already been configured*/
private boolean m_dbSet = false;
private class LoadThread extends Thread {
private DataSource m_DP;
public LoadThread(DataSource dp) {
m_DP = dp;
}
public void run() {
try {
m_visual.setAnimated();
m_visual.setText("Loading...");
boolean instanceGeneration = true;
// determine if we are going to produce data set or instance events
/* for (int i = 0; i < m_listeners.size(); i++) {
if (m_listeners.elementAt(i) instanceof DataSourceListener) {
instanceGeneration = false;
break;
}
} */
if (m_dataSetEventTargets > 0) {
instanceGeneration = false;
m_state = BATCH_LOADING;
}
if (instanceGeneration) {
m_state = INCREMENTAL_LOADING;
// boolean start = true;
Instance nextInstance = null;
// load and pass on the structure first
Instances structure = null;
try {
m_Loader.reset();
// System.err.println("NOTIFYING STRUCTURE AVAIL");
structure = m_Loader.getStructure();
notifyStructureAvailable(structure);
} catch (IOException e) {
e.printStackTrace();
}
try {
nextInstance = m_Loader.getNextInstance(structure);
} catch (IOException e) {
e.printStackTrace();
}
int z = 0;
while (nextInstance != null) {
nextInstance.setDataset(structure);
// format.add(nextInstance);
/* InstanceEvent ie = (start)
? new InstanceEvent(m_DP, nextInstance,
InstanceEvent.FORMAT_AVAILABLE)
: new InstanceEvent(m_DP, nextInstance,
InstanceEvent.INSTANCE_AVAILABLE); */
// if (start) {
// m_ie.setStatus(InstanceEvent.FORMAT_AVAILABLE);
// } else {
m_ie.setStatus(InstanceEvent.INSTANCE_AVAILABLE);
// }
m_ie.setInstance(nextInstance);
// start = false;
// System.err.println(z);
nextInstance = m_Loader.getNextInstance(structure);
if (nextInstance == null) {
m_ie.setStatus(InstanceEvent.BATCH_FINISHED);
}
notifyInstanceLoaded(m_ie);
z++;
if (z % 10000 == 0) {
m_visual.setText("" + z + " instances...");
}
}
m_visual.setStatic();
m_visual.setText(structure.relationName());
} else {
m_Loader.reset();
m_dataSet = m_Loader.getDataSet();
m_visual.setStatic();
m_visual.setText(m_dataSet.relationName());
notifyDataSetLoaded(new DataSetEvent(m_DP, m_dataSet));
}
} catch (Exception ex) {
ex.printStackTrace();
} finally {
m_ioThread = null;
// m_visual.setText("Finished");
// m_visual.setIcon(m_inactive.getVisual());
m_visual.setStatic();
m_state = IDLE;
block(false);
}
}
}
/**
* Global info (if it exists) for the wrapped loader
*
* @return the global info
*/
public String globalInfo() {
return m_globalInfo;
}
public Loader() {
super();
setLoader(m_Loader);
appearanceFinal();
}
public void setDB(boolean flag){
m_dbSet = flag;
}
protected void appearanceFinal() {
removeAll();
setLayout(new BorderLayout());
JButton goButton = new JButton("Start...");
add(goButton, BorderLayout.CENTER);
goButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
startLoading();
}
});
}
protected void appearanceDesign() {
removeAll();
setLayout(new BorderLayout());
add(m_visual, BorderLayout.CENTER);
}
/**
* Set a bean context for this bean
*
* @param bc a <code>BeanContext</code> value
*/
public void setBeanContext(BeanContext bc) {
super.setBeanContext(bc);
if (m_design) {
appearanceDesign();
} else {
appearanceFinal();
}
}
/**
* Set the loader to use
*
* @param loader a <code>weka.core.converters.Loader</code> value
*/
public void setLoader(weka.core.converters.Loader loader) {
boolean loadImages = true;
if (loader.getClass().getName().
compareTo(m_Loader.getClass().getName()) == 0) {
loadImages = false;
}
m_Loader = loader;
String loaderName = loader.getClass().toString();
loaderName = loaderName.substring(loaderName.
lastIndexOf('.')+1,
loaderName.length());
if (loadImages) {
if (m_Loader instanceof Visible) {
m_visual = ((Visible) m_Loader).getVisual();
} else {
if (!m_visual.loadIcons(BeanVisual.ICON_PATH+loaderName+".gif",
BeanVisual.ICON_PATH+loaderName+"_animated.gif")) {
useDefaultVisual();
}
}
}
m_visual.setText(loaderName);
if(! (loader instanceof DatabaseLoader)) {
// try to load structure (if possible) and notify any listeners
try {
m_dataFormat = m_Loader.getStructure();
// System.err.println(m_dataFormat);
System.out.println("[Loader] Notifying listeners of instance structure avail.");
notifyStructureAvailable(m_dataFormat);
}catch (Exception ex) {
}
}
// get global info
m_globalInfo = KnowledgeFlowApp.getGlobalInfo(m_Loader);
}
/**
* Get the loader
*
* @return a <code>weka.core.converters.Loader</code> value
*/
public weka.core.converters.Loader getLoader() {
return m_Loader;
}
/**
* Set the loader
*
* @param algorithm a Loader
* @exception IllegalArgumentException if an error occurs
*/
public void setWrappedAlgorithm(Object algorithm)
{
if (!(algorithm instanceof weka.core.converters.Loader)) {
throw new IllegalArgumentException(algorithm.getClass()+" : incorrect "
+"type of algorithm (Loader)");
}
setLoader((weka.core.converters.Loader)algorithm);
}
/**
* Get the loader
*
* @return a Loader
*/
public Object getWrappedAlgorithm() {
return getLoader();
}
/**
* Notify all listeners that the structure of a data set
* is available.
*
* @param structure an <code>Instances</code> value
*/
protected void notifyStructureAvailable(Instances structure) {
if (m_dataSetEventTargets > 0 && structure != null) {
DataSetEvent dse = new DataSetEvent(this, structure);
notifyDataSetLoaded(dse);
} else if (m_instanceEventTargets > 0 && structure != null) {
m_ie.setStructure(structure);
notifyInstanceLoaded(m_ie);
}
}
/**
* Notify all Data source listeners that a data set has been loaded
*
* @param e a <code>DataSetEvent</code> value
*/
protected void notifyDataSetLoaded(DataSetEvent e) {
Vector l;
synchronized (this) {
l = (Vector)m_listeners.clone();
}
if (l.size() > 0) {
for(int i = 0; i < l.size(); i++) {
((DataSourceListener)l.elementAt(i)).acceptDataSet(e);
}
m_dataSet = null;
}
}
/**
* Notify all instance listeners that a new instance is available
*
* @param e an <code>InstanceEvent</code> value
*/
protected void notifyInstanceLoaded(InstanceEvent e) {
Vector l;
synchronized (this) {
l = (Vector)m_listeners.clone();
}
if (l.size() > 0) {
for(int i = 0; i < l.size(); i++) {
((InstanceListener)l.elementAt(i)).acceptInstance(e);
}
m_dataSet = null;
}
}
/**
* Start loading data
*/
public void startLoading() {
if (m_ioThread == null) {
// m_visual.setText(m_dataSetFile.getName());
m_state = BATCH_LOADING;
m_ioThread = new LoadThread(Loader.this);
m_ioThread.setPriority(Thread.MIN_PRIORITY);
m_ioThread.start();
} else {
m_ioThread = null;
m_state = IDLE;
}
}
/**
* Get a list of user requests
*
* @return an <code>Enumeration</code> value
*/
public Enumeration enumerateRequests() {
Vector newVector = new Vector(0);
boolean ok = true;
if (m_ioThread == null) {
if (m_Loader instanceof FileSourcedConverter) {
if (! ((FileSourcedConverter) m_Loader).retrieveFile().isFile()) {
ok = false;
}
}
String entry = "Start loading";
if (!ok) {
entry = "$"+entry;
}
newVector.addElement(entry);
}
return newVector.elements();
}
/**
* Perform the named request
*
* @param request a <code>String</code> value
* @exception IllegalArgumentException if an error occurs
*/
public void performRequest(String request) {
if (request.compareTo("Start loading") == 0) {
startLoading();
} else {
throw new IllegalArgumentException(request
+ " not supported (Loader)");
}
}
/**
* Start loading
*
* @exception Exception if something goes wrong
*/
public void start() throws Exception {
startLoading();
block(true);
}
/**
* Function used to stop code that calls acceptTrainingSet. This is
* needed as classifier construction is performed inside a separate
* thread of execution.
*
* @param tf a <code>boolean</code> value
*/
private synchronized void block(boolean tf) {
if (tf) {
try {
// only block if thread is still doing something useful!
if (m_ioThread.isAlive() && m_state != IDLE) {
wait();
}
} catch (InterruptedException ex) {
}
} else {
notifyAll();
}
}
/**
* Returns true if the named event can be generated at this time
*
* @param eventName the event
* @return a <code>boolean</code> value
*/
public boolean eventGeneratable(String eventName) {
if (eventName.compareTo("instance") == 0) {
if (!(m_Loader instanceof weka.core.converters.IncrementalConverter)) {
return false;
}
if (m_dataSetEventTargets > 0) {
return false;
}
/* for (int i = 0; i < m_listeners.size(); i++) {
if (m_listeners.elementAt(i) instanceof DataSourceListener) {
return false;
}
} */
}
if (eventName.compareTo("dataSet") == 0) {
if (!(m_Loader instanceof weka.core.converters.BatchConverter)) {
return false;
}
if (m_instanceEventTargets > 0) {
return false;
}
/* for (int i = 0; i < m_listeners.size(); i++) {
if (m_listeners.elementAt(i) instanceof InstanceListener) {
return false;
}
} */
}
return true;
}
/**
* Add a listener
*
* @param dsl a <code>DataSourceListener</code> value
*/
public synchronized void addDataSourceListener(DataSourceListener dsl) {
super.addDataSourceListener(dsl);
m_dataSetEventTargets ++;
// pass on any current instance format
try{
if((m_Loader instanceof DatabaseLoader && m_dbSet && m_dataFormat == null) ||
(!(m_Loader instanceof DatabaseLoader) && m_dataFormat == null)) {
m_dataFormat = m_Loader.getStructure();
m_dbSet = false;
}
}catch(Exception ex){
}
notifyStructureAvailable(m_dataFormat);
}
/**
* Remove a listener
*
* @param dsl a <code>DataSourceListener</code> value
*/
public synchronized void removeDataSourceListener(DataSourceListener dsl) {
super.removeDataSourceListener(dsl);
m_dataSetEventTargets --;
}
/**
* Add an instance listener
*
* @param dsl a <code>InstanceListener</code> value
*/
public synchronized void addInstanceListener(InstanceListener dsl) {
super.addInstanceListener(dsl);
m_instanceEventTargets ++;
try{
if((m_Loader instanceof DatabaseLoader && m_dbSet && m_dataFormat == null) ||
(!(m_Loader instanceof DatabaseLoader) && m_dataFormat == null)) {
m_dataFormat = m_Loader.getStructure();
m_dbSet = false;
}
}catch(Exception ex){
}
// pass on any current instance format
notifyStructureAvailable(m_dataFormat);
}
/**
* Remove an instance listener
*
* @param dsl a <code>InstanceListener</code> value
*/
public synchronized void removeInstanceListener(InstanceListener dsl) {
super.removeInstanceListener(dsl);
m_instanceEventTargets --;
}
public static void main(String [] args) {
try {
final javax.swing.JFrame jf = new javax.swing.JFrame();
jf.getContentPane().setLayout(new java.awt.BorderLayout());
final Loader tv = new Loader();
jf.getContentPane().add(tv, java.awt.BorderLayout.CENTER);
jf.addWindowListener(new java.awt.event.WindowAdapter() {
public void windowClosing(java.awt.event.WindowEvent e) {
jf.dispose();
System.exit(0);
}
});
jf.setSize(800,600);
jf.setVisible(true);
} catch (Exception ex) {
ex.printStackTrace();
}
}
private Object readResolve() throws ObjectStreamException {
// try and reset the Loader
if (m_Loader != null) {
try {
m_Loader.reset();
} catch (Exception ex) {
}
}
return this;
}
}