weka.gui.beans.DataVisualizer.java Source code

Java tutorial

Introduction

Here is the source code for weka.gui.beans.DataVisualizer.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/>.
 */

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

package weka.gui.beans;

import weka.core.*;
import weka.core.PluginManager;
import weka.gui.Logger;
import weka.gui.visualize.PlotData2D;
import weka.gui.visualize.VisualizePanel;

import javax.swing.*;
import java.awt.BorderLayout;
import java.awt.GraphicsEnvironment;
import java.awt.image.BufferedImage;
import java.beans.EventSetDescriptor;
import java.beans.PropertyChangeListener;
import java.beans.VetoableChangeListener;
import java.beans.beancontext.BeanContext;
import java.beans.beancontext.BeanContextChild;
import java.beans.beancontext.BeanContextChildSupport;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.EventObject;
import java.util.List;
import java.util.Vector;

/**
 * Bean that encapsulates weka.gui.visualize.VisualizePanel
 * 
 * @author <a href="mailto:mhall@cs.waikato.ac.nz">Mark Hall</a>
 * @version $Revision$
 */
public class DataVisualizer extends JPanel
        implements DataSourceListener, TrainingSetListener, TestSetListener, Visible, UserRequestAcceptor,
        Serializable, BeanContextChild, HeadlessEventCollector, EnvironmentHandler, BeanCommon, EventConstraints {

    /** for serialization */
    private static final long serialVersionUID = 1949062132560159028L;

    protected BeanVisual m_visual = new BeanVisual("DataVisualizer",
            BeanVisual.ICON_PATH + "DefaultDataVisualizer.gif",
            BeanVisual.ICON_PATH + "DefaultDataVisualizer_animated.gif");

    protected transient Instances m_visualizeDataSet;

    protected transient JFrame m_popupFrame;

    protected boolean m_framePoppedUp = false;

    /**
     * True if this bean's appearance is the design mode appearance
     */
    protected boolean m_design;

    /**
     * BeanContex that this bean might be contained within
     */
    protected transient BeanContext m_beanContext = null;

    private VisualizePanel m_visPanel;

    /** Events received and stored during headless execution */
    protected List<EventObject> m_headlessEvents;

    /**
     * Set to true when processing events stored during headless execution. Used
     * to prevent sending ImageEvents to listeners a second time (since these will
     * have been passed on during headless execution).
     */
    protected transient boolean m_processingHeadlessEvents = false;

    protected ArrayList<ImageListener> m_imageListeners = new ArrayList<ImageListener>();

    protected List<Object> m_listenees = new ArrayList<Object>();

    /**
     * Objects listening for data set events
     */
    private final Vector<DataSourceListener> m_dataSetListeners = new Vector<DataSourceListener>();

    /** For rendering plots to encapsulate in ImageEvents */
    protected transient List<Instances> m_offscreenPlotData;
    protected transient OffscreenChartRenderer m_offscreenRenderer;

    /** Name of the renderer to use for offscreen chart rendering */
    protected String m_offscreenRendererName = "Weka Chart Renderer";

    /**
     * The name of the attribute to use for the x-axis of offscreen plots. If left
     * empty, False Positive Rate is used for threshold curves
     */
    protected String m_xAxis = "";

    /**
     * The name of the attribute to use for the y-axis of offscreen plots. If left
     * empty, True Positive Rate is used for threshold curves
     */
    protected String m_yAxis = "";

    /**
     * Additional options for the offscreen renderer
     */
    protected String m_additionalOptions = "";

    /** Width of offscreen plots */
    protected String m_width = "500";

    /** Height of offscreen plots */
    protected String m_height = "400";

    /**
     * The environment variables.
     */
    protected transient Environment m_env;

    /**
     * BeanContextChild support
     */
    protected BeanContextChildSupport m_bcSupport = new BeanContextChildSupport(this);

    public DataVisualizer() {
        java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment();
        if (!GraphicsEnvironment.isHeadless()) {
            appearanceFinal();
        } else {
            m_headlessEvents = new ArrayList<EventObject>();
        }
    }

    /**
     * Global info for this bean
     * 
     * @return a <code>String</code> value
     */
    public String globalInfo() {
        return "Visualize incoming data/training/test sets in a 2D scatter plot.";
    }

    protected void appearanceDesign() {
        m_visPanel = null;
        removeAll();
        useDefaultVisual();
        setLayout(new BorderLayout());
        add(m_visual, BorderLayout.CENTER);
    }

    protected void appearanceFinal() {
        java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment();

        removeAll();
        if (!GraphicsEnvironment.isHeadless()) {
            setLayout(new BorderLayout());
            setUpFinal();
        }
    }

    protected void setUpFinal() {
        if (m_visPanel == null) {
            m_visPanel = new VisualizePanel();
        }
        add(m_visPanel, BorderLayout.CENTER);
    }

    /**
     * Accept a training set
     * 
     * @param e a <code>TrainingSetEvent</code> value
     */
    @Override
    public void acceptTrainingSet(TrainingSetEvent e) {
        Instances trainingSet = e.getTrainingSet();
        DataSetEvent dse = new DataSetEvent(this, trainingSet);
        acceptDataSet(dse);
    }

    /**
     * Accept a test set
     * 
     * @param e a <code>TestSetEvent</code> value
     */
    @Override
    public void acceptTestSet(TestSetEvent e) {
        Instances testSet = e.getTestSet();
        DataSetEvent dse = new DataSetEvent(this, testSet);
        acceptDataSet(dse);
    }

    /**
     * Accept a data set
     * 
     * @param e a <code>DataSetEvent</code> value
     */
    @Override
    public synchronized void acceptDataSet(DataSetEvent e) {
        // ignore structure only events
        if (e.isStructureOnly()) {
            return;
        }
        m_visualizeDataSet = new Instances(e.getDataSet());
        if (m_visualizeDataSet.classIndex() < 0) {
            m_visualizeDataSet.setClassIndex(m_visualizeDataSet.numAttributes() - 1);
        }
        if (!m_design) {
            try {
                setInstances(m_visualizeDataSet);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        } else {
            if (m_headlessEvents != null) {
                m_headlessEvents = new ArrayList<EventObject>();
                m_headlessEvents.add(e);
            }
        }

        // pass on the event to any listeners
        notifyDataSetListeners(e);

        renderOffscreenImage(e);
    }

    protected void renderOffscreenImage(DataSetEvent e) {
        if (m_env == null) {
            m_env = Environment.getSystemWide();
        }

        if (m_imageListeners.size() > 0 && !m_processingHeadlessEvents) {
            // configure the renderer (if necessary)
            setupOffscreenRenderer();

            m_offscreenPlotData = new ArrayList<Instances>();
            Instances predictedI = e.getDataSet();
            if (predictedI.classIndex() >= 0 && predictedI.classAttribute().isNominal()) {
                // set up multiple series - one for each class
                Instances[] classes = new Instances[predictedI.numClasses()];
                for (int i = 0; i < predictedI.numClasses(); i++) {
                    classes[i] = new Instances(predictedI, 0);
                    classes[i].setRelationName(predictedI.classAttribute().value(i));
                }
                for (int i = 0; i < predictedI.numInstances(); i++) {
                    Instance current = predictedI.instance(i);
                    classes[(int) current.classValue()].add((Instance) current.copy());
                }
                for (Instances classe : classes) {
                    m_offscreenPlotData.add(classe);
                }
            } else {
                m_offscreenPlotData.add(new Instances(predictedI));
            }

            List<String> options = new ArrayList<String>();
            String additional = m_additionalOptions;
            if (m_additionalOptions != null && m_additionalOptions.length() > 0) {
                try {
                    additional = m_env.substitute(additional);
                } catch (Exception ex) {
                }
            }
            if (additional != null && additional.indexOf("-color") < 0) {
                // for WekaOffscreenChartRenderer only
                if (additional.length() > 0) {
                    additional += ",";
                }
                if (predictedI.classIndex() >= 0) {
                    additional += "-color=" + predictedI.classAttribute().name();
                } else {
                    additional += "-color=/last";
                }
            }
            String[] optionsParts = additional.split(",");
            for (String p : optionsParts) {
                options.add(p.trim());
            }

            String xAxis = m_xAxis;
            try {
                xAxis = m_env.substitute(xAxis);
            } catch (Exception ex) {
            }

            String yAxis = m_yAxis;
            try {
                yAxis = m_env.substitute(yAxis);
            } catch (Exception ex) {
            }

            String width = m_width;
            String height = m_height;
            int defWidth = 500;
            int defHeight = 400;
            try {
                width = m_env.substitute(width);
                height = m_env.substitute(height);

                defWidth = Integer.parseInt(width);
                defHeight = Integer.parseInt(height);
            } catch (Exception ex) {
            }

            try {
                BufferedImage osi = m_offscreenRenderer.renderXYScatterPlot(defWidth, defHeight,
                        m_offscreenPlotData, xAxis, yAxis, options);

                ImageEvent ie = new ImageEvent(this, osi);
                notifyImageListeners(ie);
            } catch (Exception e1) {
                e1.printStackTrace();
            }
        }
    }

    /**
     * Notify all image listeners of a ImageEvent
     * 
     * @param te a <code>ImageEvent</code> value
     */
    @SuppressWarnings("unchecked")
    protected void notifyImageListeners(ImageEvent te) {
        ArrayList<ImageListener> l;
        synchronized (this) {
            l = (ArrayList<ImageListener>) m_imageListeners.clone();
        }
        if (l.size() > 0) {
            for (int i = 0; i < l.size(); i++) {
                l.get(i).acceptImage(te);
            }
        }
    }

    /**
     * Get the list of events processed in headless mode. May return null or an
     * empty list if not running in headless mode or no events were processed
     * 
     * @return a list of EventObjects or null.
     */
    @Override
    public List<EventObject> retrieveHeadlessEvents() {
        return m_headlessEvents;
    }

    /**
     * Process a list of events that have been collected earlier. Has no affect if
     * the component is running in headless mode.
     * 
     * @param headless a list of EventObjects to process.
     */
    @Override
    public void processHeadlessEvents(List<EventObject> headless) {
        // only process if we're not headless
        if (!java.awt.GraphicsEnvironment.isHeadless()) {
            m_processingHeadlessEvents = true;
            for (EventObject e : headless) {
                if (e instanceof DataSetEvent) {
                    acceptDataSet((DataSetEvent) e);
                }
            }
        }
        m_processingHeadlessEvents = false;
    }

    /**
     * Set the visual appearance of this bean
     * 
     * @param newVisual a <code>BeanVisual</code> value
     */
    @Override
    public void setVisual(BeanVisual newVisual) {
        m_visual = newVisual;
    }

    /**
     * Return the visual appearance of this bean
     */
    @Override
    public BeanVisual getVisual() {
        return m_visual;
    }

    /**
     * Use the default appearance for this bean
     */
    @Override
    public void useDefaultVisual() {
        m_visual.loadIcons(BeanVisual.ICON_PATH + "DefaultDataVisualizer.gif",
                BeanVisual.ICON_PATH + "DefaultDataVisualizer_animated.gif");
    }

    /**
     * Describe <code>enumerateRequests</code> method here.
     * 
     * @return an <code>Enumeration</code> value
     */
    @Override
    public Enumeration<String> enumerateRequests() {
        Vector<String> newVector = new Vector<String>(0);
        if (m_visualizeDataSet != null) {
            newVector.addElement("Show plot");
        }
        return newVector.elements();
    }

    /**
     * Add a property change listener to this bean
     * 
     * @param name the name of the property of interest
     * @param pcl a <code>PropertyChangeListener</code> value
     */
    @Override
    public void addPropertyChangeListener(String name, PropertyChangeListener pcl) {
        m_bcSupport.addPropertyChangeListener(name, pcl);
    }

    /**
     * Remove a property change listener from this bean
     * 
     * @param name the name of the property of interest
     * @param pcl a <code>PropertyChangeListener</code> value
     */
    @Override
    public void removePropertyChangeListener(String name, PropertyChangeListener pcl) {
        m_bcSupport.removePropertyChangeListener(name, pcl);
    }

    /**
     * Add a vetoable change listener to this bean
     * 
     * @param name the name of the property of interest
     * @param vcl a <code>VetoableChangeListener</code> value
     */
    @Override
    public void addVetoableChangeListener(String name, VetoableChangeListener vcl) {
        m_bcSupport.addVetoableChangeListener(name, vcl);
    }

    /**
     * Remove a vetoable change listener from this bean
     * 
     * @param name the name of the property of interest
     * @param vcl a <code>VetoableChangeListener</code> value
     */
    @Override
    public void removeVetoableChangeListener(String name, VetoableChangeListener vcl) {
        m_bcSupport.removeVetoableChangeListener(name, vcl);
    }

    /**
     * Set a bean context for this bean
     * 
     * @param bc a <code>BeanContext</code> value
     */
    @Override
    public void setBeanContext(BeanContext bc) {
        m_beanContext = bc;
        m_design = m_beanContext.isDesignTime();
        if (m_design) {
            appearanceDesign();
        } else {
            java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment();
            if (!GraphicsEnvironment.isHeadless()) {
                appearanceFinal();
            }
        }
    }

    /**
     * Return the bean context (if any) that this bean is embedded in
     * 
     * @return a <code>BeanContext</code> value
     */
    @Override
    public BeanContext getBeanContext() {
        return m_beanContext;
    }

    /**
     * Set instances for this bean. This method is a convenience method for
     * clients who use this component programatically
     * 
     * @param inst an <code>Instances</code> value
     * @exception Exception if an error occurs
     */
    public void setInstances(Instances inst) throws Exception {
        if (m_design) {
            throw new Exception(
                    "This method is not to be used during design " + "time. It is meant to be used if this "
                            + "bean is being used programatically as as " + "stand alone component.");
        }
        m_visualizeDataSet = inst;
        PlotData2D pd1 = new PlotData2D(m_visualizeDataSet);
        String relationName = m_visualizeDataSet.relationName();
        pd1.setPlotName(relationName);
        try {
            m_visPanel.setMasterPlot(pd1);
        } catch (Exception ex) {
            System.err.println("Problem setting up " + "visualization (DataVisualizer)");
            ex.printStackTrace();
        }
    }

    /**
     * Notify all data set listeners of a data set event
     * 
     * @param ge a <code>DataSetEvent</code> value
     */
    @SuppressWarnings("unchecked")
    private void notifyDataSetListeners(DataSetEvent ge) {
        Vector<DataSourceListener> l;
        synchronized (this) {
            l = (Vector<DataSourceListener>) m_dataSetListeners.clone();
        }
        if (l.size() > 0) {
            for (int i = 0; i < l.size(); i++) {
                l.elementAt(i).acceptDataSet(ge);
            }
        }
    }

    /**
     * Describe <code>performRequest</code> method here.
     * 
     * @param request a <code>String</code> value
     * @exception IllegalArgumentException if an error occurs
     */
    @Override
    public void performRequest(String request) {
        if (request.compareTo("Show plot") == 0) {
            try {
                // popup visualize panel
                if (!m_framePoppedUp) {
                    m_framePoppedUp = true;
                    final VisualizePanel vis = new VisualizePanel();
                    PlotData2D pd1 = new PlotData2D(m_visualizeDataSet);

                    String relationName = m_visualizeDataSet.relationName();

                    // A bit of a nasty hack. Allows producers of instances-based
                    // events to specify that the points should be connected
                    if (relationName.startsWith("__")) {
                        boolean[] connect = new boolean[m_visualizeDataSet.numInstances()];
                        for (int i = 1; i < connect.length; i++) {
                            connect[i] = true;
                        }
                        pd1.setConnectPoints(connect);
                        relationName = relationName.substring(2);
                    }
                    pd1.setPlotName(relationName);
                    try {
                        vis.setMasterPlot(pd1);
                    } catch (Exception ex) {
                        System.err.println("Problem setting up " + "visualization (DataVisualizer)");
                        ex.printStackTrace();
                    }
                    final JFrame jf = Utils.getWekaJFrame("Visualize", m_visual);
                    jf.getContentPane().setLayout(new BorderLayout());
                    jf.getContentPane().add(vis, BorderLayout.CENTER);
                    jf.addWindowListener(new java.awt.event.WindowAdapter() {
                        @Override
                        public void windowClosing(java.awt.event.WindowEvent e) {
                            jf.dispose();
                            m_framePoppedUp = false;
                        }
                    });
                    jf.pack();
                    jf.setSize(800, 600);
                    jf.setLocationRelativeTo(SwingUtilities.getWindowAncestor(m_visual));
                    jf.setVisible(true);
                    m_popupFrame = jf;
                } else {
                    m_popupFrame.toFront();
                }
            } catch (Exception ex) {
                ex.printStackTrace();
                m_framePoppedUp = false;
            }
        } else {
            throw new IllegalArgumentException(request + " not supported (DataVisualizer)");
        }
    }

    protected void setupOffscreenRenderer() {
        if (m_offscreenRenderer == null) {
            if (m_offscreenRendererName == null || m_offscreenRendererName.length() == 0) {
                m_offscreenRenderer = new WekaOffscreenChartRenderer();
                return;
            }

            if (m_offscreenRendererName.equalsIgnoreCase("weka chart renderer")) {
                m_offscreenRenderer = new WekaOffscreenChartRenderer();
            } else {
                try {
                    Object r = PluginManager.getPluginInstance("weka.gui.beans.OffscreenChartRenderer",
                            m_offscreenRendererName);
                    if (r != null && r instanceof weka.gui.beans.OffscreenChartRenderer) {
                        m_offscreenRenderer = (OffscreenChartRenderer) r;
                    } else {
                        // use built-in default
                        m_offscreenRenderer = new WekaOffscreenChartRenderer();
                    }
                } catch (Exception ex) {
                    // use built-in default
                    m_offscreenRenderer = new WekaOffscreenChartRenderer();
                }
            }
        }
    }

    /**
     * Add a listener
     * 
     * @param dsl a <code>DataSourceListener</code> value
     */
    public synchronized void addDataSourceListener(DataSourceListener dsl) {
        m_dataSetListeners.addElement(dsl);
    }

    /**
     * Remove a listener
     * 
     * @param dsl a <code>DataSourceListener</code> value
     */
    public synchronized void removeDataSourceListener(DataSourceListener dsl) {
        m_dataSetListeners.remove(dsl);
    }

    public static void main(String[] args) {
        try {
            if (args.length != 1) {
                System.err.println("Usage: DataVisualizer <dataset>");
                System.exit(1);
            }
            java.io.Reader r = new java.io.BufferedReader(new java.io.FileReader(args[0]));
            Instances inst = new Instances(r);
            final javax.swing.JFrame jf = new javax.swing.JFrame();
            jf.getContentPane().setLayout(new java.awt.BorderLayout());
            final DataVisualizer as = new DataVisualizer();
            as.setInstances(inst);

            jf.getContentPane().add(as, java.awt.BorderLayout.CENTER);
            jf.addWindowListener(new java.awt.event.WindowAdapter() {
                @Override
                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();
            System.err.println(ex.getMessage());
        }
    }

    @Override
    public void setEnvironment(Environment env) {
        m_env = env;
    }

    /**
     * Set the name of the attribute for the x-axis in offscreen plots. This
     * defaults to "False Positive Rate" for threshold curves if not specified.
     * 
     * @param xAxis the name of the xAxis
     */
    public void setOffscreenXAxis(String xAxis) {
        m_xAxis = xAxis;
    }

    /**
     * Get the name of the attribute for the x-axis in offscreen plots
     * 
     * @return the name of the xAxis
     */
    public String getOffscreenXAxis() {
        return m_xAxis;
    }

    /**
     * Set the name of the attribute for the y-axis in offscreen plots. This
     * defaults to "True Positive Rate" for threshold curves if not specified.
     * 
     * @param yAxis the name of the xAxis
     */
    public void setOffscreenYAxis(String yAxis) {
        m_yAxis = yAxis;
    }

    /**
     * Get the name of the attribute for the y-axix of offscreen plots.
     * 
     * @return the name of the yAxis.
     */
    public String getOffscreenYAxis() {
        return m_yAxis;
    }

    /**
     * Set the width (in pixels) of the offscreen image to generate.
     * 
     * @param width the width in pixels.
     */
    public void setOffscreenWidth(String width) {
        m_width = width;
    }

    /**
     * Get the width (in pixels) of the offscreen image to generate.
     * 
     * @return the width in pixels.
     */
    public String getOffscreenWidth() {
        return m_width;
    }

    /**
     * Set the height (in pixels) of the offscreen image to generate
     * 
     * @param height the height in pixels
     */
    public void setOffscreenHeight(String height) {
        m_height = height;
    }

    /**
     * Get the height (in pixels) of the offscreen image to generate
     * 
     * @return the height in pixels
     */
    public String getOffscreenHeight() {
        return m_height;
    }

    /**
     * Set the name of the renderer to use for offscreen chart rendering
     * operations
     * 
     * @param rendererName the name of the renderer to use
     */
    public void setOffscreenRendererName(String rendererName) {
        m_offscreenRendererName = rendererName;
        m_offscreenRenderer = null;
    }

    /**
     * Get the name of the renderer to use for offscreen chart rendering
     * operations
     * 
     * @return the name of the renderer to use
     */
    public String getOffscreenRendererName() {
        return m_offscreenRendererName;
    }

    /**
     * Set the additional options for the offscreen renderer
     * 
     * @param additional additional options
     */
    public void setOffscreenAdditionalOpts(String additional) {
        m_additionalOptions = additional;
    }

    /**
     * Get the additional options for the offscreen renderer
     * 
     * @return the additional options
     */
    public String getOffscreenAdditionalOpts() {
        return m_additionalOptions;
    }

    /**
     * Add an image listener
     * 
     * @param cl a <code>ImageListener</code> value
     */
    public synchronized void addImageListener(ImageListener cl) {
        m_imageListeners.add(cl);
    }

    /**
     * Remove an image listener
     * 
     * @param cl a <code>ImageListener</code> value
     */
    public synchronized void removeImageListener(ImageListener cl) {
        m_imageListeners.remove(cl);
    }

    /**
     * Set a custom (descriptive) name for this bean
     * 
     * @param name the name to use
     */
    @Override
    public void setCustomName(String name) {
        m_visual.setText(name);
    }

    /**
     * Get the custom (descriptive) name for this bean (if one has been set)
     * 
     * @return the custom name (or the default name)
     */
    @Override
    public String getCustomName() {
        // TODO Auto-generated method stub
        return m_visual.getText();
    }

    /**
     * Stop any processing that the bean might be doing.
     */
    @Override
    public void stop() {
    }

    /**
     * Returns true if. at this time, the bean is busy with some (i.e. perhaps a
     * worker thread is performing some calculation).
     * 
     * @return true if the bean is busy.
     */
    @Override
    public boolean isBusy() {
        return false;
    }

    /**
     * Set a logger
     * 
     * @param logger a <code>Logger</code> value
     */
    @Override
    public void setLog(Logger logger) {
    }

    /**
     * Returns true if, at this time, the object will accept a connection via the
     * supplied EventSetDescriptor
     * 
     * @param esd the EventSetDescriptor
     * @return true if the object will accept a connection
     */
    @Override
    public boolean connectionAllowed(EventSetDescriptor esd) {
        return connectionAllowed(esd.getName());
    }

    /**
     * Returns true if, at this time, the object will accept a connection via the
     * named event
     * 
     * @param eventName the name of the event
     * @return true if the object will accept a connection
     */
    @Override
    public boolean connectionAllowed(String eventName) {
        return eventName.equals("dataSet") || eventName.equals("trainingSet") || eventName.equals("testSet");
    }

    /**
     * Notify this object that it has been registered as a listener with a source
     * for recieving events described by the named event This object is
     * responsible for recording this fact.
     * 
     * @param eventName the event
     * @param source the source with which this object has been registered as a
     *          listener
     */
    @Override
    public void connectionNotification(String eventName, Object source) {
        if (connectionAllowed(eventName)) {
            m_listenees.add(source);
        }
    }

    /**
     * Notify this object that it has been deregistered as a listener with a
     * source for named event. This object is responsible for recording this fact.
     * 
     * @param eventName the event
     * @param source the source with which this object has been registered as a
     *          listener
     */
    @Override
    public void disconnectionNotification(String eventName, Object source) {
        m_listenees.remove(source);
    }

    /**
     * Returns true, if at the current time, the named event could be generated.
     * Assumes that supplied event names are names of events that could be
     * generated by this bean.
     * 
     * @param eventName the name of the event in question
     * @return true if the named event could be generated at this point in time
     */
    @Override
    public boolean eventGeneratable(String eventName) {
        if (m_listenees.size() == 0) {
            return false;
        }

        boolean ok = false;
        for (Object o : m_listenees) {
            if (o instanceof EventConstraints) {
                if (((EventConstraints) o).eventGeneratable("dataSet")
                        || ((EventConstraints) o).eventGeneratable("trainingSet")
                        || ((EventConstraints) o).eventGeneratable("testSet")) {
                    ok = true;
                    break;
                }
            }
        }
        return ok;
    }
}