weka.gui.ConverterFileChooser.java Source code

Java tutorial

Introduction

Here is the source code for weka.gui.ConverterFileChooser.java

Source

/*
 *   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 3 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, see <http://www.gnu.org/licenses/>.
 */

/*
 * ConverterFileChooser.java
 * Copyright (C) 2006-2012 University of Waikato, Hamilton, New Zealand
 */

package weka.gui;

import weka.core.Capabilities;
import weka.core.Instances;
import weka.core.WekaPackageClassLoaderManager;
import weka.core.converters.AbstractFileLoader;
import weka.core.converters.AbstractFileSaver;
import weka.core.converters.AbstractLoader;
import weka.core.converters.AbstractSaver;
import weka.core.converters.ConverterResources;
import weka.core.converters.ConverterUtils;
import weka.core.converters.FileSourcedConverter;

import javax.swing.JCheckBox;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.filechooser.FileFilter;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.Vector;

/**
 * A specialized JFileChooser that lists all available file Loaders and Savers.
 * To list only savers that can handle the data that is about to be saved, one
 * can set a Capabilities filter.
 * 
 * @author fracpete (fracpete at waikato dot ac dot nz)
 * @version $Revision$
 * @see #setCapabilitiesFilter(Capabilities)
 */
public class ConverterFileChooser extends WekaFileChooser {

    /** for serialization. */
    private static final long serialVersionUID = -5373058011025481738L;

    /** unhandled type of dialog. */
    public final static int UNHANDLED_DIALOG = 0;

    /** the loader dialog. */
    public final static int LOADER_DIALOG = 1;

    /** the saver dialog. */
    public final static int SAVER_DIALOG = 2;

    /** the file filters for the loaders. */
    protected static Vector<ExtensionFileFilter> m_LoaderFileFilters;

    /** the file filters for the savers. */
    protected static Vector<ExtensionFileFilter> m_SaverFileFilters;

    /** the type of dialog to display. */
    protected int m_DialogType;

    /** the converter that was chosen by the user. */
    protected Object m_CurrentConverter;

    /** the propertychangelistener. */
    protected PropertyChangeListener m_Listener;

    /** the last filter that was used for opening/saving. */
    protected FileFilter m_LastFilter;

    /** the Capabilities filter for the savers. */
    protected Capabilities m_CapabilitiesFilter;

    /**
     * whether to popup a dialog in case the file already exists (only save
     * dialog).
     */
    protected boolean m_OverwriteWarning = true;

    /** whether the file to be opened must exist (only open dialog). */
    protected boolean m_FileMustExist = true;

    /** the checkbox for bringing up the GenericObjectEditor. */
    protected JCheckBox m_CheckBoxOptions;

    /** the GOE for displaying the options of a loader/saver. */
    protected GenericObjectEditor m_Editor;

    /** whether the GOE was OKed or Canceled. */
    protected int m_EditorResult;

    /**
     * whether to display only core converters (hardcoded in ConverterUtils).
     * Necessary for RMI/Remote Experiments for instance.
     * 
     * @see weka.core.converters.ConverterResources#CORE_FILE_LOADERS
     * @see weka.core.converters.ConverterResources#CORE_FILE_SAVERS
     */
    protected boolean m_CoreConvertersOnly = false;

    static {
        initDefaultFilters();
    }

    /**
     * Initialize the default set of filters for loaders and savers
     */
    public static void initDefaultFilters() {
        ConverterUtils.initialize();
        initFilters(true, ConverterUtils.getFileLoaders());
        initFilters(false, ConverterUtils.getFileSavers());
    }

    /**
     * onstructs a FileChooser pointing to the user's default directory.
     */
    public ConverterFileChooser() {
        super();
    }

    /**
     * Constructs a FileChooser using the given File as the path.
     * 
     * @param currentDirectory the path to start in
     */
    public ConverterFileChooser(File currentDirectory) {
        super(currentDirectory);
    }

    /**
     * Constructs a FileChooser using the given path.
     * 
     * @param currentDirectory the path to start in
     */
    public ConverterFileChooser(String currentDirectory) {
        super(currentDirectory);
    }

    /**
     * Further initializations.
     */
    protected void initialize() {
        JPanel panel;

        super.initialize();

        panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
        m_AccessoryPanel.add(panel, BorderLayout.NORTH);
        m_CheckBoxOptions = new JCheckBox("Invoke options dialog");
        m_CheckBoxOptions.setMnemonic('I');
        panel.add(m_CheckBoxOptions);

        m_Editor = new GenericObjectEditor(false);
        ((GenericObjectEditor.GOEPanel) m_Editor.getCustomEditor()).addOkListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                m_EditorResult = JFileChooser.APPROVE_OPTION;
                m_CurrentConverter = m_Editor.getValue();
                // thanks to serialization and transient readers/streams, we have
                // to set the file again to initialize the converter again
                try {
                    ((FileSourcedConverter) m_CurrentConverter)
                            .setFile(((FileSourcedConverter) m_CurrentConverter).retrieveFile());
                } catch (Exception ex) {
                    // ignored
                }
            }
        });
        ((GenericObjectEditor.GOEPanel) m_Editor.getCustomEditor()).addCancelListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                m_EditorResult = JFileChooser.CANCEL_OPTION;
            }
        });
    }

    /**
     * filters out all non-core loaders if only those should be displayed.
     * 
     * @param list the list of filters to check
     * @return the filtered list of filters
     * @see #m_CoreConvertersOnly
     */
    protected Vector<ExtensionFileFilter> filterNonCoreLoaderFileFilters(Vector<ExtensionFileFilter> list) {
        Vector<ExtensionFileFilter> result;
        int i;
        ExtensionFileFilter filter;
        AbstractLoader loader;

        if (!getCoreConvertersOnly()) {
            result = list;
        } else {
            result = new Vector<ExtensionFileFilter>();
            for (i = 0; i < list.size(); i++) {
                filter = list.get(i);
                loader = ConverterUtils.getLoaderForExtension(filter.getExtensions()[0]);
                if (ConverterResources.isCoreFileLoader(loader.getClass().getName())) {
                    result.add(filter);
                }
            }
        }

        return result;
    }

    /**
     * filters out all non-core savers if only those should be displayed.
     * 
     * @param list the list of filters to check
     * @return the filtered list of filters
     * @see #m_CoreConvertersOnly
     */
    protected Vector<ExtensionFileFilter> filterNonCoreSaverFileFilters(Vector<ExtensionFileFilter> list) {
        Vector<ExtensionFileFilter> result;
        int i;
        ExtensionFileFilter filter;
        AbstractSaver saver;

        if (!getCoreConvertersOnly()) {
            result = list;
        } else {
            result = new Vector<ExtensionFileFilter>();
            for (i = 0; i < list.size(); i++) {
                filter = list.get(i);
                saver = ConverterUtils.getSaverForExtension(filter.getExtensions()[0]);
                if (ConverterResources.isCoreFileSaver(saver.getClass().getName())) {
                    result.add(filter);
                }
            }
        }

        return result;
    }

    /**
     * filters the list of file filters according to the currently set.
     * Capabilities
     * 
     * @param list the filters to check
     * @return the filtered list of filters
     */
    protected Vector<ExtensionFileFilter> filterSaverFileFilters(Vector<ExtensionFileFilter> list) {
        Vector<ExtensionFileFilter> result;
        int i;
        ExtensionFileFilter filter;
        AbstractSaver saver;

        if (m_CapabilitiesFilter == null) {
            result = list;
        } else {
            result = new Vector<ExtensionFileFilter>();

            for (i = 0; i < list.size(); i++) {
                filter = list.get(i);
                saver = ConverterUtils.getSaverForExtension(filter.getExtensions()[0]);
                if (saver.getCapabilities().supports(m_CapabilitiesFilter)) {
                    result.add(filter);
                }
            }
        }

        return result;
    }

    /**
     * initializes the ExtensionFileFilters.
     * 
     * @param loader if true then the loader filter are initialized
     * @param classnames the classnames of the converters
     */
    protected static void initFilters(boolean loader, Vector<String> classnames) {
        int i;
        int n;
        String classname;
        Class<?> cls;
        String[] ext;
        String desc;
        FileSourcedConverter converter;
        ExtensionFileFilter filter;

        if (loader) {
            m_LoaderFileFilters = new Vector<ExtensionFileFilter>();
        } else {
            m_SaverFileFilters = new Vector<ExtensionFileFilter>();
        }

        for (i = 0; i < classnames.size(); i++) {
            classname = classnames.get(i);

            // get data from converter
            try {
                cls = WekaPackageClassLoaderManager.forName(classname);
                converter = (FileSourcedConverter) cls.newInstance();
                ext = converter.getFileExtensions();
                desc = converter.getFileDescription();
            } catch (Exception e) {
                cls = null;
                converter = null;
                ext = new String[0];
                desc = "";
            }

            if (converter == null) {
                continue;
            }

            // loader?
            if (loader) {
                for (n = 0; n < ext.length; n++) {
                    filter = new ExtensionFileFilter(ext[n], desc + " (*" + ext[n] + ")");
                    m_LoaderFileFilters.add(filter);
                }
            } else {
                for (n = 0; n < ext.length; n++) {
                    filter = new ExtensionFileFilter(ext[n], desc + " (*" + ext[n] + ")");
                    m_SaverFileFilters.add(filter);
                }
            }
        }
    }

    /**
     * initializes the GUI.
     * 
     * @param dialogType the type of dialog to setup the GUI for
     */
    protected void initGUI(int dialogType) {
        Vector<ExtensionFileFilter> list;
        int i;
        boolean acceptAll;

        // backup current state
        acceptAll = isAcceptAllFileFilterUsed();

        // setup filters
        resetChoosableFileFilters();
        setAcceptAllFileFilterUsed(acceptAll);
        if (dialogType == LOADER_DIALOG) {
            list = filterNonCoreLoaderFileFilters(m_LoaderFileFilters);
        } else {
            list = filterSaverFileFilters(filterNonCoreSaverFileFilters(m_SaverFileFilters));
        }
        for (i = 0; i < list.size(); i++) {
            addChoosableFileFilter(list.get(i));
        }
        if (list.size() > 0) {
            if ((m_LastFilter == null) || (!list.contains(m_LastFilter))) {
                setFileFilter(list.get(0));
            } else {
                setFileFilter(m_LastFilter);
            }
        }

        // listener
        if (m_Listener != null) {
            removePropertyChangeListener(m_Listener);
        }
        m_Listener = new PropertyChangeListener() {
            @Override
            public void propertyChange(PropertyChangeEvent evt) {
                // filter changed
                if (evt.getPropertyName().equals(FILE_FILTER_CHANGED_PROPERTY)) {
                    updateCurrentConverter();
                }
            }
        };
        addPropertyChangeListener(m_Listener);

        // initial setup
        if (dialogType == LOADER_DIALOG) {
            m_Editor.setClassType(AbstractFileLoader.class);
            m_Editor.setValue(new weka.core.converters.ArffLoader());
        } else {
            m_Editor.setClassType(AbstractFileSaver.class);
            m_Editor.setValue(new weka.core.converters.ArffSaver());
        }

        updateCurrentConverter();
    }

    /**
     * sets the capabilities that the savers must have. use null if all should be
     * listed.
     * 
     * @param value the minimum Capabilities the savers must have
     */
    public void setCapabilitiesFilter(Capabilities value) {
        m_CapabilitiesFilter = (Capabilities) value.clone();
    }

    /**
     * returns the capabilities filter for the savers, can be null if all are
     * listed.
     * 
     * @return the minimum Capabilities the savers must have
     */
    public Capabilities getCapabilitiesFilter() {
        if (m_CapabilitiesFilter != null) {
            return (Capabilities) m_CapabilitiesFilter.clone();
        } else {
            return null;
        }
    }

    /**
     * Whether a warning is popped up if the file that is to be saved already
     * exists (only save dialog).
     * 
     * @param value if true a warning will be popup
     */
    public void setOverwriteWarning(boolean value) {
        m_OverwriteWarning = value;
    }

    /**
     * Returns whether a popup appears with a warning that the file already exists
     * (only save dialog).
     * 
     * @return true if a warning pops up
     */
    public boolean getOverwriteWarning() {
        return m_OverwriteWarning;
    }

    /**
     * Whether the selected file must exst (only open dialog).
     * 
     * @param value if true the file must exist
     */
    public void setFileMustExist(boolean value) {
        m_FileMustExist = value;
    }

    /**
     * Returns whether the selected file must exist (only open dialog).
     * 
     * @return true if the file must exist
     */
    public boolean getFileMustExist() {
        return m_FileMustExist;
    }

    /**
     * Whether to display only the hardocded core converters. Necessary for
     * RMI/Remote Experiments (dynamic class discovery doesn't work there!).
     * 
     * @param value if true only the core converters will be displayed
     * @see #m_CoreConvertersOnly
     */
    public void setCoreConvertersOnly(boolean value) {
        m_CoreConvertersOnly = value;
    }

    /**
     * Returns whether only the hardcoded core converters are displayed. Necessary
     * for RMI/REmote Experiments (dynamic class discovery doesn't work there!).
     * 
     * @return true if the file must exist
     * @see #m_CoreConvertersOnly
     */
    public boolean getCoreConvertersOnly() {
        return m_CoreConvertersOnly;
    }

    /**
     * Pops a custom file chooser dialog with a custom approve button. Throws an
     * exception, if the dialog type is UNHANDLED_DIALOG.
     * 
     * @param parent the parent of this dialog
     * @param approveButtonText the text for the OK button
     * @return the user's action
     */
    @Override
    public int showDialog(Component parent, String approveButtonText) {
        if (m_DialogType == UNHANDLED_DIALOG) {
            throw new IllegalStateException("Either use showOpenDialog or showSaveDialog!");
        } else {
            return super.showDialog(parent, approveButtonText);
        }
    }

    /**
     * Pops up an "Open File" file chooser dialog.
     * 
     * @param parent the parent of this file chooser
     * @return the result of the user's action
     */
    @Override
    public int showOpenDialog(Component parent) {
        m_DialogType = LOADER_DIALOG;
        m_CurrentConverter = null;

        initGUI(LOADER_DIALOG);

        int result = super.showOpenDialog(parent);

        m_DialogType = UNHANDLED_DIALOG;
        removePropertyChangeListener(m_Listener);

        // do we have to add the extension?
        if ((result == APPROVE_OPTION) && (getSelectedFile().isFile())) {
            if (getFileFilter() instanceof ExtensionFileFilter) {
                String filename = getSelectedFile().getAbsolutePath();
                String[] extensions = ((ExtensionFileFilter) getFileFilter()).getExtensions();
                if (!filename.endsWith(extensions[0])) {
                    filename += extensions[0];
                    setSelectedFile(new File(filename));
                }
            }
        }

        // does file exist?
        if ((result == APPROVE_OPTION) && (getFileMustExist()) && (getSelectedFile().isFile())
                && (!getSelectedFile().exists())) {
            int retVal = JOptionPane.showConfirmDialog(parent,
                    "The file '" + getSelectedFile() + "' does not exist - please select again!");
            if (retVal == JOptionPane.OK_OPTION) {
                result = showOpenDialog(parent);
            } else {
                result = CANCEL_OPTION;
            }
        }

        if (result == APPROVE_OPTION) {
            m_LastFilter = getFileFilter();
            configureCurrentConverter(LOADER_DIALOG);

            // bring up options dialog?
            if (m_CheckBoxOptions.isSelected() && m_CurrentConverter != null) {
                m_EditorResult = JFileChooser.CANCEL_OPTION;
                m_Editor.setValue(m_CurrentConverter);
                PropertyDialog pd;
                if (PropertyDialog.getParentDialog(this) != null) {
                    pd = new PropertyDialog(PropertyDialog.getParentDialog(this), m_Editor);
                } else {
                    pd = new PropertyDialog(PropertyDialog.getParentFrame(this), m_Editor);
                }
                pd.setVisible(true);
                result = m_EditorResult;
            }
        }

        return result;
    }

    /**
     * Pops up an "Save File" file chooser dialog.
     * 
     * @param parent the parent of this file chooser
     * @return the result of the user's action
     */
    @Override
    public int showSaveDialog(Component parent) {
        m_DialogType = SAVER_DIALOG;
        m_CurrentConverter = null;

        initGUI(SAVER_DIALOG);

        boolean acceptAll = isAcceptAllFileFilterUsed();

        // using "setAcceptAllFileFilterUsed" messes up the currently selected
        // file filter/file, hence backup/restore of currently selected
        // file filter/file
        FileFilter currentFilter = getFileFilter();
        File currentFile = getSelectedFile();
        setAcceptAllFileFilterUsed(false);
        setFileFilter(currentFilter);
        setSelectedFile(currentFile);

        int result = super.showSaveDialog(parent);

        // do we have to add the extension?
        if (result == APPROVE_OPTION) {
            if (getFileFilter() instanceof ExtensionFileFilter) {
                String filename = getSelectedFile().getAbsolutePath();
                String[] extensions = ((ExtensionFileFilter) getFileFilter()).getExtensions();
                if (!filename.endsWith(extensions[0])) {
                    filename += extensions[0];
                    setSelectedFile(new File(filename));
                }
            }
        }

        // using "setAcceptAllFileFilterUsed" messes up the currently selected
        // file filter/file, hence backup/restore of currently selected
        // file filter/file
        currentFilter = getFileFilter();
        currentFile = getSelectedFile();
        setAcceptAllFileFilterUsed(acceptAll);
        setFileFilter(currentFilter);
        setSelectedFile(currentFile);

        m_DialogType = UNHANDLED_DIALOG;
        removePropertyChangeListener(m_Listener);

        // overwrite the file?
        if ((result == APPROVE_OPTION) && (getOverwriteWarning()) && (getSelectedFile().exists())) {
            int retVal = JOptionPane.showConfirmDialog(parent,
                    "The file '" + getSelectedFile() + "' already exists - overwrite it?");
            if (retVal == JOptionPane.OK_OPTION) {
                result = APPROVE_OPTION;
            } else if (retVal == JOptionPane.NO_OPTION) {
                result = showSaveDialog(parent);
            } else {
                result = CANCEL_OPTION;
            }
        }

        if (result == APPROVE_OPTION) {
            m_LastFilter = getFileFilter();
            // configureCurrentConverter(SAVER_DIALOG);

            // bring up options dialog?
            if (m_CheckBoxOptions.isSelected()) {
                m_EditorResult = JFileChooser.CANCEL_OPTION;
                m_Editor.setValue(m_CurrentConverter);
                PropertyDialog pd;
                if (PropertyDialog.getParentDialog(this) != null) {
                    pd = new PropertyDialog(PropertyDialog.getParentDialog(this), m_Editor);
                } else {
                    pd = new PropertyDialog(PropertyDialog.getParentFrame(this), m_Editor);
                }
                pd.setVisible(true);
                result = m_EditorResult;
                // configureCurrentConverter(SAVER_DIALOG);
            }
        }

        return result;
    }

    /**
     * returns the loader that was chosen by the user, can be null in case the
     * user aborted the dialog or the save dialog was shown.
     * 
     * @return the chosen loader, if any
     */
    public AbstractFileLoader getLoader() {
        configureCurrentConverter(LOADER_DIALOG);

        if (m_CurrentConverter instanceof AbstractFileSaver) {
            return null;
        } else {
            return (AbstractFileLoader) m_CurrentConverter;
        }
    }

    /**
     * returns the saver that was chosen by the user, can be null in case the user
     * aborted the dialog or the open dialog was shown.
     * 
     * @return the chosen saver, if any
     */
    public AbstractFileSaver getSaver() {
        configureCurrentConverter(SAVER_DIALOG);

        if (m_CurrentConverter instanceof AbstractFileLoader) {
            return null;
        } else {
            return (AbstractFileSaver) m_CurrentConverter;
        }
    }

    /**
     * sets the current converter according to the current filefilter.
     */
    protected void updateCurrentConverter() {
        String[] extensions;
        Object newConverter;

        if (getFileFilter() == null) {
            return;
        }

        if (!isAcceptAllFileFilterUsed()) {
            // determine current converter
            extensions = ((ExtensionFileFilter) getFileFilter()).getExtensions();
            if (m_DialogType == LOADER_DIALOG) {
                newConverter = ConverterUtils.getLoaderForExtension(extensions[0]);
            } else {
                newConverter = ConverterUtils.getSaverForExtension(extensions[0]);
            }

            try {
                if (m_CurrentConverter == null) {
                    m_CurrentConverter = newConverter;
                } else {
                    if (!m_CurrentConverter.getClass().equals(newConverter.getClass())) {
                        m_CurrentConverter = newConverter;
                    }
                }
            } catch (Exception e) {
                m_CurrentConverter = null;
                e.printStackTrace();
            }
        } else {
            m_CurrentConverter = null;
        }
    }

    /**
     * configures the current converter.
     * 
     * @param dialogType the type of dialog to configure for
     */
    protected void configureCurrentConverter(int dialogType) {
        String filename;
        File currFile;

        if ((getSelectedFile() == null) || (getSelectedFile().isDirectory())) {
            return;
        }

        filename = getSelectedFile().getAbsolutePath();

        if (m_CurrentConverter == null) {
            if (dialogType == LOADER_DIALOG) {
                m_CurrentConverter = ConverterUtils.getLoaderForFile(filename);
            } else if (dialogType == SAVER_DIALOG) {
                m_CurrentConverter = ConverterUtils.getSaverForFile(filename);
            } else {
                throw new IllegalStateException("Cannot determine loader/saver!");
            }

            // none found?
            if (m_CurrentConverter == null) {
                return;
            }
        }

        try {
            currFile = ((FileSourcedConverter) m_CurrentConverter).retrieveFile();
            if ((currFile == null) || (!currFile.getAbsolutePath().equals(filename))) {
                ((FileSourcedConverter) m_CurrentConverter).setFile(new File(filename));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * For testing the file chooser.
     * 
     * @param args the commandline options - ignored
     * @throws Exception if something goes wrong with loading/saving
     */
    public static void main(String[] args) throws Exception {
        ConverterFileChooser fc;
        int retVal;
        AbstractFileLoader loader;
        AbstractFileSaver saver;
        Instances data;

        fc = new ConverterFileChooser();
        retVal = fc.showOpenDialog(null);

        // load file
        if (retVal == ConverterFileChooser.APPROVE_OPTION) {
            loader = fc.getLoader();
            data = loader.getDataSet();
            retVal = fc.showSaveDialog(null);

            // save file
            if (retVal == ConverterFileChooser.APPROVE_OPTION) {
                saver = fc.getSaver();
                saver.setInstances(data);
                saver.writeBatch();
            } else {
                System.out.println("Saving aborted!");
            }
        } else {
            System.out.println("Loading aborted!");
        }
    }
}