/*
* 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.
*/
/*
* PreprocessPanel.java
* Copyright (C) 2003 Richard Kirkby, Len Trigg
*
*/
package weka.gui.explorer;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.Writer;
import java.net.URL;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.ListSelectionModel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.filechooser.FileFilter;
import weka.core.Instances;
import weka.core.SerializedObject;
import weka.core.converters.Loader;
import weka.experiment.InstanceQuery;
import weka.filters.Filter;
import weka.filters.UnsupervisedFilter;
import weka.gui.AttributeListPanel;
import weka.gui.AttributeSummaryPanel;
import weka.gui.ExtensionFileFilter;
import weka.gui.FileEditor;
import weka.gui.GenericArrayEditor;
import weka.gui.GenericObjectEditor;
import weka.gui.InstancesSummaryPanel;
import weka.gui.Logger;
import weka.gui.PropertyDialog;
import weka.gui.SysErrLog;
import weka.gui.TaskLogger;
import weka.gui.PropertyPanel;
import weka.gui.AttributeVisualizationPanel;
import weka.core.UnassignedClassException;
/**
* This panel controls simple preprocessing of instances. Summary
* information on instances and attributes is shown. Filters may be
* configured to alter the set of instances. Altered instances may
* also be saved.
*
* @author Richard Kirkby (rkirkby@cs.waikato.ac.nz)
* @author Len Trigg (trigg@cs.waikato.ac.nz)
* @version $Revision: 1.1.1.1 $
*/
public class PreprocessPanel extends JPanel {
/** Displays simple stats on the working instances */
protected InstancesSummaryPanel m_InstSummaryPanel =
new InstancesSummaryPanel();
/** Click to load base instances from a file */
protected JButton m_OpenFileBut = new JButton("Open file...");
/** Click to load base instances from a URL */
protected JButton m_OpenURLBut = new JButton("Open URL...");
/** Click to load base instances from a Database */
protected JButton m_OpenDBBut = new JButton("Open DB...");
/** Lets the user enter a DB query */
protected GenericObjectEditor m_DatabaseQueryEditor =
new GenericObjectEditor();
/** Click to revert back to the last saved point */
protected JButton m_UndoBut = new JButton("Undo");
/** Click to apply filters and save the results */
protected JButton m_SaveBut = new JButton("Save...");
/** Panel to let the user toggle attributes */
protected AttributeListPanel m_AttPanel = new AttributeListPanel();
/** Displays summary stats on the selected attribute */
protected AttributeSummaryPanel m_AttSummaryPanel =
new AttributeSummaryPanel();
/** Lets the user configure the filter */
protected GenericObjectEditor m_FilterEditor =
new GenericObjectEditor();
/** Filter configuration */
protected PropertyPanel m_FilterPanel = new PropertyPanel(m_FilterEditor);
/** Click to apply filters and save the results */
protected JButton m_ApplyFilterBut = new JButton("Apply");
/** Filter to ensure only arff files are selected */
protected FileFilter m_ArffFilter =
new ExtensionFileFilter(Instances.FILE_EXTENSION, "Arff data files");
/** The file chooser for selecting arff files */
protected JFileChooser m_FileChooser
= new JFileChooser(new File(System.getProperty("user.dir")));
/** Stores the last URL that instances were loaded from */
protected String m_LastURL = "http://";
/** Stores the last sql query executed */
protected String m_SQLQ = new String("SELECT * FROM ?");
/** The working instances */
protected Instances m_Instances;
/** The visualization of the attribute values */
protected AttributeVisualizationPanel m_AttVisualizePanel = new AttributeVisualizationPanel();
/** Keeps track of undo points */
protected File[] m_tempUndoFiles = new File[20]; // set number of undo ops here
/** The next available slot for an undo point */
protected int m_tempUndoIndex = 0;
/**
* Manages sending notifications to people when we change the set of
* working instances.
*/
protected PropertyChangeSupport m_Support = new PropertyChangeSupport(this);
/** A thread for loading/saving instances from a file or URL */
protected Thread m_IOThread;
/** The message logger */
protected Logger m_Log = new SysErrLog();
static {
java.beans.PropertyEditorManager
.registerEditor(java.io.File.class,
FileEditor.class);
java.beans.PropertyEditorManager
.registerEditor(weka.core.SelectedTag.class,
weka.gui.SelectedTagEditor.class);
java.beans.PropertyEditorManager
.registerEditor(weka.filters.UnsupervisedFilter.class,
weka.gui.GenericObjectEditor.class);
java.beans.PropertyEditorManager
.registerEditor(weka.attributeSelection.ASSearch.class,
weka.gui.GenericObjectEditor.class);
java.beans.PropertyEditorManager
.registerEditor(weka.attributeSelection.ASEvaluation.class,
weka.gui.GenericObjectEditor.class);
java.beans.PropertyEditorManager
.registerEditor(weka.experiment.InstanceQuery.class,
weka.gui.GenericObjectEditor.class);
java.beans.PropertyEditorManager
.registerEditor(weka.core.converters.Loader.class,
weka.gui.GenericObjectEditor.class);
}
/**
* Creates the instances panel with no initial instances.
*/
public PreprocessPanel() {
// Create/Configure/Connect components
try {
m_DatabaseQueryEditor.setClassType(weka.experiment.InstanceQuery.class);
m_DatabaseQueryEditor.setValue(new weka.experiment.InstanceQuery());
((GenericObjectEditor.GOEPanel)m_DatabaseQueryEditor.getCustomEditor())
.addOkListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
setInstancesFromDBQ();
}
});
} catch (Exception ex) {
}
m_FilterEditor.setClassType(weka.filters.UnsupervisedFilter.class);
m_OpenFileBut.setToolTipText("Open a set of instances from a file");
m_OpenURLBut.setToolTipText("Open a set of instances from a URL");
m_OpenDBBut.setToolTipText("Open a set of instances from a database");
m_UndoBut.setToolTipText("Undo the last change to the dataset");
m_SaveBut.setToolTipText("Save the working relation to a file");
m_ApplyFilterBut.setToolTipText("Apply the current filter to the data");
m_FileChooser.setFileFilter(m_ArffFilter);
m_FileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
m_OpenURLBut.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
setInstancesFromURLQ();
}
});
m_OpenDBBut.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
PropertyDialog pd = new PropertyDialog(m_DatabaseQueryEditor,100,100);
}
});
m_OpenFileBut.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
setInstancesFromFileQ();
}
});
m_UndoBut.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
undo();
}
});
m_SaveBut.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
saveWorkingInstancesToFileQ();
}
});
m_ApplyFilterBut.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
applyFilter();
}
});
m_AttPanel.getSelectionModel()
.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
if (!e.getValueIsAdjusting()) {
ListSelectionModel lm = (ListSelectionModel) e.getSource();
for (int i = e.getFirstIndex(); i <= e.getLastIndex(); i++) {
if (lm.isSelectedIndex(i)) {
m_AttSummaryPanel.setAttribute(i);
m_AttVisualizePanel.setAttribute(i);
break;
}
}
}
}
});
m_InstSummaryPanel.setBorder(BorderFactory
.createTitledBorder("Current relation"));
m_AttPanel.setBorder(BorderFactory
.createTitledBorder("Attributes"));
m_AttSummaryPanel.setBorder(BorderFactory
.createTitledBorder("Selected attribute"));
m_UndoBut.setEnabled(false);
m_ApplyFilterBut.setEnabled(false);
// Set up the GUI layout
JPanel buttons = new JPanel();
buttons.setBorder(BorderFactory.createEmptyBorder(10, 5, 10, 5));
buttons.setLayout(new GridLayout(1, 6, 5, 5));
buttons.add(m_OpenFileBut);
buttons.add(m_OpenURLBut);
buttons.add(m_OpenDBBut);
buttons.add(m_UndoBut);
buttons.add(m_SaveBut);
JPanel attInfo = new JPanel();
attInfo.setLayout(new BorderLayout());
attInfo.add(m_AttPanel, BorderLayout.CENTER);
JPanel filter = new JPanel();
filter.setBorder(BorderFactory
.createTitledBorder("Filter"));
filter.setLayout(new BorderLayout());
filter.add(m_FilterPanel, BorderLayout.CENTER);
filter.add(m_ApplyFilterBut, BorderLayout.EAST);
JPanel attVis = new JPanel();
attVis.setLayout( new GridLayout(2,1) );
attVis.add(m_AttSummaryPanel);
attVis.add(m_AttVisualizePanel);
JPanel lhs = new JPanel();
lhs.setLayout(new BorderLayout());
lhs.add(m_InstSummaryPanel, BorderLayout.NORTH);
lhs.add(attInfo, BorderLayout.CENTER);
JPanel rhs = new JPanel();
rhs.setLayout(new BorderLayout());
rhs.add(attVis, BorderLayout.CENTER);
JPanel relation = new JPanel();
relation.setLayout(new GridLayout(1, 2));
relation.add(lhs);
relation.add(rhs);
JPanel middle = new JPanel();
middle.setLayout(new BorderLayout());
middle.add(filter, BorderLayout.NORTH);
middle.add(relation, BorderLayout.CENTER);
setLayout(new BorderLayout());
add(buttons, BorderLayout.NORTH);
add(middle, BorderLayout.CENTER);
}
/**
* Sets the Logger to receive informational messages
*
* @param newLog the Logger that will now get info messages
*/
public void setLog(Logger newLog) {
m_Log = newLog;
}
/**
* Tells the panel to use a new base set of instances.
*
* @param inst a set of Instances
*/
public void setInstances(Instances inst) {
m_Instances = inst;
try {
Runnable r = new Runnable() {
public void run() {
m_InstSummaryPanel.setInstances(m_Instances);
m_AttPanel.setInstances(m_Instances);
m_AttSummaryPanel.setInstances(m_Instances);
m_AttVisualizePanel.setInstances(m_Instances);
// select the first attribute in the list
m_AttPanel.getSelectionModel().setSelectionInterval(0, 0);
m_AttSummaryPanel.setAttribute(0);
m_AttVisualizePanel.setAttribute(0);
m_ApplyFilterBut.setEnabled(true);
m_Log.logMessage("Base relation is now "
+ m_Instances.relationName()
+ " (" + m_Instances.numInstances()
+ " instances)");
m_Log.statusMessage("OK");
// Fire a propertychange event
m_Support.firePropertyChange("", null, null);
}
};
if (SwingUtilities.isEventDispatchThread()) {
r.run();
} else {
SwingUtilities.invokeAndWait(r);
}
} catch (Exception ex) {
JOptionPane.showMessageDialog(this,
"Problem setting base instances:\n"
+ ex.getMessage(),
"Instances",
JOptionPane.ERROR_MESSAGE);
}
}
/**
* Gets the working set of instances.
*
* @return the working instances
*/
public Instances getInstances() {
return m_Instances;
}
/**
* Adds a PropertyChangeListener who will be notified of value changes.
*
* @param l a value of type 'PropertyChangeListener'
*/
public void addPropertyChangeListener(PropertyChangeListener l) {
m_Support.addPropertyChangeListener(l);
}
/**
* Removes a PropertyChangeListener.
*
* @param l a value of type 'PropertyChangeListener'
*/
public void removePropertyChangeListener(PropertyChangeListener l) {
m_Support.removePropertyChangeListener(l);
}
/**
* Passes the dataset through the filter that has been configured for use.
*/
protected void applyFilter() {
if (m_IOThread == null) {
m_IOThread = new Thread() {
public void run() {
try {
Filter filter = (Filter) m_FilterEditor.getValue();
if (filter != null) {
if (m_Log instanceof TaskLogger) {
((TaskLogger)m_Log).taskStarted();
}
m_Log.statusMessage("Saving undo information");
addUndoPoint();
m_Log.statusMessage("Passing dataset through filter "
+ filter.getClass().getName());
filter.setInputFormat(m_Instances);
m_Instances = filter.useFilter(m_Instances, filter);
setInstances(m_Instances);
if (m_Log instanceof TaskLogger) {
((TaskLogger)m_Log).taskFinished();
}
}
} catch (Exception ex) {
if (m_Log instanceof TaskLogger) {
((TaskLogger)m_Log).taskFinished();
}
// Pop up an error optionpane
JOptionPane.showMessageDialog(PreprocessPanel.this,
"Problem filtering instances:\n"
+ ex.getMessage(),
"Apply Filter",
JOptionPane.ERROR_MESSAGE);
ex.printStackTrace();
m_Log.logMessage("Problem filtering instances: " + ex.getMessage());
}
m_IOThread = null;
}
};
m_IOThread.setPriority(Thread.MIN_PRIORITY); // UI has most priority
m_IOThread.start();
} else {
JOptionPane.showMessageDialog(this,
"Can't apply filter at this time,\n"
+ "currently busy with other IO",
"Apply Filter",
JOptionPane.WARNING_MESSAGE);
}
}
/**
* Queries the user for a file to save instances as, then saves the
* instances in a background process. This is done in the IO
* thread, and an error message is popped up if the IO thread is busy.
*/
public void saveWorkingInstancesToFileQ() {
if (m_IOThread == null) {
int returnVal = m_FileChooser.showSaveDialog(this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
File sFile = m_FileChooser.getSelectedFile();
if (!sFile.getName().toLowerCase().endsWith(Instances.FILE_EXTENSION)) {
sFile = new File(sFile.getParent(), sFile.getName()
+ Instances.FILE_EXTENSION);
}
File selected = sFile;
saveInstancesToFile(selected, m_Instances);
}
} else {
JOptionPane.showMessageDialog(this,
"Can't save at this time,\n"
+ "currently busy with other IO",
"Save Instances",
JOptionPane.WARNING_MESSAGE);
}
}
/**
* Queries the user for a file to load instances from, then loads the
* instances in a background process. This is done in the IO
* thread, and an error message is popped up if the IO thread is busy.
*/
public void setInstancesFromFileQ() {
if (m_IOThread == null) {
int returnVal = m_FileChooser.showOpenDialog(this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
File selected = m_FileChooser.getSelectedFile();
try {
addUndoPoint();
} catch (Exception ignored) {}
setInstancesFromFile(selected);
}
} else {
JOptionPane.showMessageDialog(this,
"Can't load at this time,\n"
+ "currently busy with other IO",
"Load Instances",
JOptionPane.WARNING_MESSAGE);
}
}
/**
* Queries the user for a URL to a database to load instances from,
* then loads the instances in a background process. This is done in the IO
* thread, and an error message is popped up if the IO thread is busy.
*/
public void setInstancesFromDBQ() {
if (m_IOThread == null) {
try {
InstanceQuery InstQ =
(InstanceQuery)m_DatabaseQueryEditor.getValue();
InstQ.connectToDatabase();
try {
addUndoPoint();
} catch (Exception ignored) {}
setInstancesFromDB(InstQ);
} catch (Exception ex) {
JOptionPane.showMessageDialog(this,
"Problem connecting to database:\n"
+ ex.getMessage(),
"Load Instances",
JOptionPane.ERROR_MESSAGE);
}
} else {
JOptionPane.showMessageDialog(this,
"Can't load at this time,\n"
+ "currently busy with other IO",
"Load Instances",
JOptionPane.WARNING_MESSAGE);
}
}
/**
* Queries the user for a URL to load instances from, then loads the
* instances in a background process. This is done in the IO
* thread, and an error message is popped up if the IO thread is busy.
*/
public void setInstancesFromURLQ() {
if (m_IOThread == null) {
try {
String urlName = (String) JOptionPane.showInputDialog(this,
"Enter the source URL",
"Load Instances",
JOptionPane.QUESTION_MESSAGE,
null,
null,
m_LastURL);
if (urlName != null) {
m_LastURL = urlName;
URL url = new URL(urlName);
try {
addUndoPoint();
} catch (Exception ignored) {}
setInstancesFromURL(url);
}
} catch (Exception ex) {
ex.printStackTrace();
JOptionPane.showMessageDialog(this,
"Problem with URL:\n"
+ ex.getMessage(),
"Load Instances",
JOptionPane.ERROR_MESSAGE);
}
} else {
JOptionPane.showMessageDialog(this,
"Can't load at this time,\n"
+ "currently busy with other IO",
"Load Instances",
JOptionPane.WARNING_MESSAGE);
}
}
/**
* Saves the current instances to the supplied file.
*
* @param f a value of type 'File'
* @param inst the instances to save
*/
protected void saveInstancesToFile(final File f, final Instances inst) {
if (m_IOThread == null) {
m_IOThread = new Thread() {
public void run() {
try {
m_Log.statusMessage("Saving to file...");
Writer w = new BufferedWriter(new FileWriter(f));
Instances h = new Instances(inst, 0);
w.write(h.toString());
w.write("\n");
for (int i = 0; i < inst.numInstances(); i++) {
w.write(inst.instance(i).toString());
w.write("\n");
}
w.close();
m_Log.statusMessage("OK");
} catch (Exception ex) {
ex.printStackTrace();
m_Log.logMessage(ex.getMessage());
}
m_IOThread = null;
}
};
m_IOThread.setPriority(Thread.MIN_PRIORITY); // UI has most priority
m_IOThread.start();
} else {
JOptionPane.showMessageDialog(this,
"Can't save at this time,\n"
+ "currently busy with other IO",
"Save Instances",
JOptionPane.WARNING_MESSAGE);
}
}
/**
* Pops up generic object editor with list of conversion filters
*
* @param f the File
*/
private void converterQuery(final File f) {
final GenericObjectEditor convEd = new GenericObjectEditor(true);
try {
convEd.setClassType(weka.core.converters.Loader.class);
convEd.setValue(new weka.core.converters.CSVLoader());
((GenericObjectEditor.GOEPanel)convEd.getCustomEditor())
.addOkListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
tryConverter((Loader)convEd.getValue(), f);
}
});
} catch (Exception ex) {
}
PropertyDialog pd = new PropertyDialog(convEd, 100, 100);
}
/**
* Applies the selected converter
*
* @param cnv the converter to apply to the input file
* @param f the input file
*/
private void tryConverter(final Loader cnv, final File f) {
if (m_IOThread == null) {
m_IOThread = new Thread() {
public void run() {
try {
cnv.setSource(f);
Instances inst = cnv.getDataSet();
setInstances(inst);
} catch (Exception ex) {
m_Log.statusMessage(cnv.getClass().getName()+" failed to load "
+f.getName());
JOptionPane.showMessageDialog(PreprocessPanel.this,
cnv.getClass().getName()+" failed to load '"
+ f.getName() + "'.\n"
+ "Reason:\n" + ex.getMessage(),
"Convert File",
JOptionPane.ERROR_MESSAGE);
m_IOThread = null;
converterQuery(f);
}
m_IOThread = null;
}
};
m_IOThread.setPriority(Thread.MIN_PRIORITY); // UI has most priority
m_IOThread.start();
}
}
/**
* Loads results from a set of instances contained in the supplied
* file. This is started in the IO thread, and a dialog is popped up
* if there's a problem.
*
* @param f a value of type 'File'
*/
public void setInstancesFromFile(final File f) {
if (m_IOThread == null) {
m_IOThread = new Thread() {
public void run() {
try {
m_Log.statusMessage("Reading from file...");
Reader r = new BufferedReader(new FileReader(f));
setInstances(new Instances(r));
r.close();
} catch (Exception ex) {
m_Log.statusMessage("File '" + f.getName() + "' not recognised as an arff file.");
m_IOThread = null;
if (JOptionPane.showOptionDialog(PreprocessPanel.this,
"File '" + f.getName()
+ "' not recognised as an arff file.\n"
+ "Reason:\n" + ex.getMessage(),
"Load Instances",
0,
JOptionPane.ERROR_MESSAGE,
null,
new String[] {"OK", "Use Converter"},
null) == 1) {
converterQuery(f);
}
}
m_IOThread = null;
}
};
m_IOThread.setPriority(Thread.MIN_PRIORITY); // UI has most priority
m_IOThread.start();
} else {
JOptionPane.showMessageDialog(this,
"Can't load at this time,\n"
+ "currently busy with other IO",
"Load Instances",
JOptionPane.WARNING_MESSAGE);
}
}
/**
* Loads instances from a database
*
* @param iq the InstanceQuery object to load from (this is assumed
* to have been already connected to a valid database).
*/
public void setInstancesFromDB(final InstanceQuery iq) {
if (m_IOThread == null) {
m_IOThread = new Thread() {
public void run() {
try {
m_Log.statusMessage("Reading from database...");
final Instances i = iq.retrieveInstances();
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
setInstances(new Instances(i));
}
});
iq.disconnectFromDatabase();
} catch (Exception ex) {
m_Log.statusMessage("Problem executing DB query "+m_SQLQ);
JOptionPane.showMessageDialog(PreprocessPanel.this,
"Couldn't read from database:\n"
+ ex.getMessage(),
"Load Instances",
JOptionPane.ERROR_MESSAGE);
}
m_IOThread = null;
}
};
m_IOThread.setPriority(Thread.MIN_PRIORITY); // UI has most priority
m_IOThread.start();
} else {
JOptionPane.showMessageDialog(this,
"Can't load at this time,\n"
+ "currently busy with other IO",
"Load Instances",
JOptionPane.WARNING_MESSAGE);
}
}
/**
* Loads instances from a URL.
*
* @param u the URL to load from.
*/
public void setInstancesFromURL(final URL u) {
if (m_IOThread == null) {
m_IOThread = new Thread() {
public void run() {
try {
m_Log.statusMessage("Reading from URL...");
Reader r = new BufferedReader(
new InputStreamReader(u.openStream()));
setInstances(new Instances(r));
r.close();
} catch (Exception ex) {
ex.printStackTrace();
m_Log.statusMessage("Problem reading " + u);
JOptionPane.showMessageDialog(PreprocessPanel.this,
"Couldn't read from URL:\n"
+ u + "\n"
+ ex.getMessage(),
"Load Instances",
JOptionPane.ERROR_MESSAGE);
}
m_IOThread = null;
}
};
m_IOThread.setPriority(Thread.MIN_PRIORITY); // UI has most priority
m_IOThread.start();
} else {
JOptionPane.showMessageDialog(this,
"Can't load at this time,\n"
+ "currently busy with other IO",
"Load Instances",
JOptionPane.WARNING_MESSAGE);
}
}
/**
* Backs up the current state of the dataset, so the changes can be undone.
*/
public void addUndoPoint() throws Exception {
// create temporary file
File tempFile = File.createTempFile("weka", null);
tempFile.deleteOnExit();
// write current dataset to file
Writer w = new BufferedWriter(new FileWriter(tempFile));
Instances h = new Instances(m_Instances, 0);
w.write(h.toString());
w.write("\n");
for (int i = 0; i < m_Instances.numInstances(); i++) {
w.write(m_Instances.instance(i).toString());
w.write("\n");
}
w.close();
// update undo file list
if (m_tempUndoFiles[m_tempUndoIndex] != null) {
// remove undo points that are too old
m_tempUndoFiles[m_tempUndoIndex].delete();
}
m_tempUndoFiles[m_tempUndoIndex] = tempFile;
if (++m_tempUndoIndex >= m_tempUndoFiles.length) {
// wrap pointer around
m_tempUndoIndex = 0;
}
m_UndoBut.setEnabled(true);
}
/**
* Reverts to the last backed up version of the dataset.
*/
public void undo() {
if (--m_tempUndoIndex < 0) {
// wrap pointer around
m_tempUndoIndex = m_tempUndoFiles.length-1;
}
if (m_tempUndoFiles[m_tempUndoIndex] != null) {
// load instances from the temporary file
setInstancesFromFile(m_tempUndoFiles[m_tempUndoIndex]);
// update undo file list
m_tempUndoFiles[m_tempUndoIndex] = null;
}
// update undo button
int temp = m_tempUndoIndex-1;
if (temp < 0) {
temp = m_tempUndoFiles.length-1;
}
m_UndoBut.setEnabled(m_tempUndoFiles[temp] != null);
}
/**
* Tests out the instance-preprocessing panel from the command line.
*
* @param args ignored
*/
public static void main(String [] args) {
try {
final JFrame jf = new JFrame("Weka Knowledge Explorer: Preprocess");
jf.getContentPane().setLayout(new BorderLayout());
final PreprocessPanel sp = new PreprocessPanel();
jf.getContentPane().add(sp, BorderLayout.CENTER);
weka.gui.LogPanel lp = new weka.gui.LogPanel();
sp.setLog(lp);
jf.getContentPane().add(lp, BorderLayout.SOUTH);
jf.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
jf.dispose();
System.exit(0);
}
});
jf.pack();
jf.setSize(800, 600);
jf.setVisible(true);
} catch (Exception ex) {
ex.printStackTrace();
System.err.println(ex.getMessage());
}
}
}