All Downloads are FREE. Search and download functionalities are using the official Maven repository.

weka.classifiers.bayes.net.GUI Maven / Gradle / Ivy

Go to download

The Waikato Environment for Knowledge Analysis (WEKA), a machine learning workbench. This is the stable version. Apart from bugfixes, this version does not receive any other updates.

There is a newer version: 3.8.6
Show newest version
/*
 *   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 .
 */

/*
 *    GUI.java
 *    Copyright (C) 2007-2012 University of Waikato, Hamilton, New Zealand
 *
 */
package weka.classifiers.bayes.net;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.beans.PropertyEditor;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;
import javax.swing.table.AbstractTableModel;

import weka.classifiers.bayes.net.MarginCalculator.JunctionTreeNode;
import weka.core.Instances;
import weka.core.OptionHandler;
import weka.core.SerializedObject;
import weka.core.Utils;
import weka.core.converters.AbstractFileLoader;
import weka.core.converters.AbstractFileSaver;
import weka.core.converters.ArffSaver;
import weka.core.converters.ConverterUtils;
import weka.gui.ConverterFileChooser;
import weka.gui.ExtensionFileFilter;
import weka.gui.GenericObjectEditor;
import weka.gui.LookAndFeel;
import weka.gui.PropertyDialog;
import weka.gui.graphvisualizer.BIFFormatException;
import weka.gui.graphvisualizer.BIFParser;
import weka.gui.graphvisualizer.GraphEdge;
import weka.gui.graphvisualizer.GraphNode;
import weka.gui.graphvisualizer.HierarchicalBCEngine;
import weka.gui.graphvisualizer.LayoutCompleteEvent;
import weka.gui.graphvisualizer.LayoutCompleteEventListener;
import weka.gui.graphvisualizer.LayoutEngine;
import weka.gui.visualize.PrintablePanel;

/**
 * GUI interface to Bayesian Networks. Allows editing Bayesian networks on
 * screen and provides GUI interface to various Bayesian network facilities in
 * Weka, including random network generation, data set generation and Bayesion
 * network inference.
 * 
 * @author Remco Bouckaert ([email protected])
 * @version $Revision: 10153 $
 */
public class GUI extends JPanel implements LayoutCompleteEventListener {

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

  /** The current LayoutEngine */
  protected LayoutEngine m_layoutEngine;

  /** Panel actually displaying the graph */
  protected GraphPanel m_GraphPanel;

  /** Container of Bayesian network */
  EditableBayesNet m_BayesNet = new EditableBayesNet(true);

  /** String containing file name storing current network */
  protected String m_sFileName = "";
  /** used for calculating marginals in Bayesian netwowrks */
  MarginCalculator m_marginCalculator = null;

  /**
   * used for calculating marginals in Bayesian netwowrks when evidence is
   * present
   */
  MarginCalculator m_marginCalculatorWithEvidence = null;

  /**
   * flag indicating whether marginal distributions of each of the nodes should
   * be shown in display.
   */
  boolean m_bViewMargins = false;
  boolean m_bViewCliques = false;

  /** The menu bar */
  private JMenuBar m_menuBar;

  /** data selected from file. Used to train a Bayesian network on */
  Instances m_Instances = null;

  /** Text field for specifying zoom */
  final JTextField m_jTfZoom;
  /** toolbar containing buttons at top of window */
  final JToolBar m_jTbTools;
  /** status bar at bottom of window */
  final JLabel m_jStatusBar;
  /** TextField for node's width */
  private final JTextField m_jTfNodeWidth = new JTextField(3);
  /** TextField for nodes height */
  private final JTextField m_jTfNodeHeight = new JTextField(3);
  /** this contains the m_GraphPanel GraphPanel */
  JScrollPane m_jScrollPane;

  /** path for icons */
  private final String ICONPATH = "weka/classifiers/bayes/net/icons/";

  /** current zoom value */
  private double m_fScale = 1;

  /** standard width of node */
  private int m_nNodeHeight = 2 * getFontMetrics(getFont()).getHeight();
  /** standard height of node */
  final static int DEFAULT_NODE_WIDTH = 50;
  private int m_nNodeWidth = DEFAULT_NODE_WIDTH;
  /** width of node, allowing for some padding */
  final static int PADDING = 10;
  private int m_nPaddedNodeWidth = DEFAULT_NODE_WIDTH + PADDING;

  /** used when using zoomIn and zoomOut buttons */
  private final int[] m_nZoomPercents = { 10, 25, 50, 75, 100, 125, 150, 175,
    200, 225, 250, 275, 300, 350, 400, 450, 500, 550, 600, 650, 700, 800, 900,
    999 };

  /** actions triggered by GUI events */
  Action a_new = new ActionNew();

  Action a_quit = new ActionQuit();
  Action a_save = new ActionSave();
  ActionExport a_export = new ActionExport();
  ActionPrint a_print = new ActionPrint();
  Action a_load = new ActionLoad();
  Action a_zoomin = new ActionZoomIn();
  Action a_zoomout = new ActionZoomOut();
  Action a_layout = new ActionLayout();

  Action a_saveas = new ActionSaveAs();

  Action a_viewtoolbar = new ActionViewToolbar();

  Action a_viewstatusbar = new ActionViewStatusbar();

  Action a_networkgenerator = new ActionGenerateNetwork();

  Action a_datagenerator = new ActionGenerateData();

  Action a_datasetter = new ActionSetData();

  Action a_learn = new ActionLearn();
  Action a_learnCPT = new ActionLearnCPT();

  Action a_help = new ActionHelp();

  Action a_about = new ActionAbout();

  ActionAddNode a_addnode = new ActionAddNode();

  Action a_delnode = new ActionDeleteNode();
  Action a_cutnode = new ActionCutNode();
  Action a_copynode = new ActionCopyNode();
  Action a_pastenode = new ActionPasteNode();
  Action a_selectall = new ActionSelectAll();

  Action a_addarc = new ActionAddArc();

  Action a_delarc = new ActionDeleteArc();

  Action a_undo = new ActionUndo();

  Action a_redo = new ActionRedo();

  Action a_alignleft = new ActionAlignLeft();
  Action a_alignright = new ActionAlignRight();
  Action a_aligntop = new ActionAlignTop();
  Action a_alignbottom = new ActionAlignBottom();
  Action a_centerhorizontal = new ActionCenterHorizontal();
  Action a_centervertical = new ActionCenterVertical();
  Action a_spacehorizontal = new ActionSpaceHorizontal();
  Action a_spacevertical = new ActionSpaceVertical();

  /** node currently selected through right clicking */
  int m_nCurrentNode = -1;
  /** selection of nodes */
  Selection m_Selection = new Selection();
  /** selection rectangle drawn through dragging with left mouse button */
  Rectangle m_nSelectedRect = null;

  class Selection {
    ArrayList m_selected;

    public Selection() {
      m_selected = new ArrayList();
    } // c'tor

    public ArrayList getSelected() {
      return m_selected;
    }

    void updateGUI() {
      if (m_selected.size() > 0) {
        a_cutnode.setEnabled(true);
        a_copynode.setEnabled(true);
      } else {
        a_cutnode.setEnabled(false);
        a_copynode.setEnabled(false);
      }
      if (m_selected.size() > 1) {
        a_alignleft.setEnabled(true);
        a_alignright.setEnabled(true);
        a_aligntop.setEnabled(true);
        a_alignbottom.setEnabled(true);
        a_centerhorizontal.setEnabled(true);
        a_centervertical.setEnabled(true);
        a_spacehorizontal.setEnabled(true);
        a_spacevertical.setEnabled(true);
      } else {
        a_alignleft.setEnabled(false);
        a_alignright.setEnabled(false);
        a_aligntop.setEnabled(false);
        a_alignbottom.setEnabled(false);
        a_centerhorizontal.setEnabled(false);
        a_centervertical.setEnabled(false);
        a_spacehorizontal.setEnabled(false);
        a_spacevertical.setEnabled(false);
      }
    } // updateGUI

    public void addToSelection(int nNode) {
      for (int iNode = 0; iNode < m_selected.size(); iNode++) {
        if (nNode == m_selected.get(iNode)) {
          return;
        }
      }
      m_selected.add(nNode);
      updateGUI();
    } // addToSelection

    public void addToSelection(int[] iNodes) {
      for (int iNode2 : iNodes) {
        addToSelection(iNode2);
      }
      updateGUI();
    } // addToSelection

    public void addToSelection(Rectangle selectedRect) {
      for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) {
        if (contains(selectedRect, iNode)) {
          addToSelection(iNode);
        }
      }
    } // addToSelection

    public void selectAll() {
      m_selected.clear();
      for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) {
        m_selected.add(iNode);
      }
      updateGUI();
    } // selectAll

    boolean contains(Rectangle rect, int iNode) {
      return rect.intersects((m_BayesNet.getPositionX(iNode)) * m_fScale,
        (m_BayesNet.getPositionY(iNode)) * m_fScale, m_nPaddedNodeWidth
          * m_fScale, m_nNodeHeight * m_fScale);
    } // contains

    public void removeFromSelection(int nNode) {
      for (int iNode = 0; iNode < m_selected.size(); iNode++) {
        if (nNode == m_selected.get(iNode)) {
          m_selected.remove(iNode);
        }
      }
      updateGUI();
    } // removeFromSelection

    public void toggleSelection(int nNode) {
      for (int iNode = 0; iNode < m_selected.size(); iNode++) {
        if (nNode == m_selected.get(iNode)) {
          m_selected.remove(iNode);
          updateGUI();
          return;
        }
      }
      addToSelection(nNode);
    } // toggleSelection

    public void toggleSelection(Rectangle selectedRect) {
      for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) {
        if (contains(selectedRect, iNode)) {
          toggleSelection(iNode);
        }
      }
    } // toggleSelection

    public void clear() {
      m_selected.clear();
      updateGUI();
    }

    public void draw(Graphics g) {
      if (m_selected.size() == 0) {
        return;
      }

      for (int iNode = 0; iNode < m_selected.size(); iNode++) {
        int nNode = m_selected.get(iNode);
        int nPosX = m_BayesNet.getPositionX(nNode);
        int nPosY = m_BayesNet.getPositionY(nNode);
        g.setColor(Color.BLACK);
        int nXRC = nPosX + m_nPaddedNodeWidth - m_nNodeWidth
          - (m_nPaddedNodeWidth - m_nNodeWidth) / 2;
        int nYRC = nPosY;
        int d = 5;
        g.fillRect(nXRC, nYRC, d, d);
        g.fillRect(nXRC, nYRC + m_nNodeHeight, d, d);
        g.fillRect(nXRC + m_nNodeWidth, nYRC, d, d);
        g.fillRect(nXRC + m_nNodeWidth, nYRC + m_nNodeHeight, d, d);
      }
    } // draw
  } // Selection

  ClipBoard m_clipboard = new ClipBoard();

  class ClipBoard {
    String m_sText = null;

    public ClipBoard() {
      if (a_pastenode != null) {
        a_pastenode.setEnabled(false);
      }
    }

    public boolean hasText() {
      return m_sText != null;
    }

    public String getText() {
      return m_sText;
    }

    public void setText(String sText) {
      m_sText = sText;
      a_pastenode.setEnabled(true);
    }
  } // class ClipBoard

  /**
   * Base class used for definining actions with a name, tool tip text, possibly
   * an icon and accelerator key.
   * */
  class MyAction extends AbstractAction {
    /** for serialization */
    private static final long serialVersionUID = -2038911111935517L;

    public MyAction(String sName, String sToolTipText, String sIcon,
      String sAcceleratorKey) {
      super(sName);
      // setToolTipText(sToolTipText);
      putValue(Action.SHORT_DESCRIPTION, sToolTipText);
      putValue(Action.LONG_DESCRIPTION, sToolTipText);
      if (sAcceleratorKey.length() > 0) {
        KeyStroke keyStroke = KeyStroke.getKeyStroke(sAcceleratorKey);
        putValue(Action.ACCELERATOR_KEY, keyStroke);
      }
      putValue(Action.MNEMONIC_KEY, (int) sName.charAt(0));
      java.net.URL tempURL = ClassLoader.getSystemResource(ICONPATH + sIcon
        + ".png");
      if (tempURL != null) {
        putValue(Action.SMALL_ICON, new ImageIcon(tempURL));
      } else {
        putValue(Action.SMALL_ICON, new ImageIcon(new BufferedImage(20, 20,
          BufferedImage.TYPE_4BYTE_ABGR)));
        // System.err.println(ICONPATH + sIcon +
        // ".png not found for weka.gui.graphvisualizer.Graph");
      }
    } // c'tor

    /*
     * Place holder. Should be implemented by derived classes. (non-Javadoc)
     * 
     * @see
     * java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
     */
    @Override
    public void actionPerformed(ActionEvent ae) {
    }
  } // class MyAction

  class ActionGenerateNetwork extends MyAction {
    /** for serialization */
    private static final long serialVersionUID = -2038911085935517L;

    public ActionGenerateNetwork() {
      super("Generate Network", "Generate Random Bayesian Network",
        "generate.network", "ctrl N");
    } // c'tor

    int m_nNrOfNodes = 10;

    int m_nNrOfArcs = 15;

    int m_nCardinality = 2;

    int m_nSeed = 123;

    JDialog dlg = null;

    @Override
    public void actionPerformed(ActionEvent ae) {
      if (dlg == null) {
        dlg = new JDialog();
        dlg.setTitle("Generate Random Bayesian Network Options");

        final JLabel jLbNrOfNodes = new JLabel("Nr of nodes");
        final JTextField jTfNrOfNodes = new JTextField(3);
        jTfNrOfNodes.setHorizontalAlignment(JTextField.CENTER);
        jTfNrOfNodes.setText("" + m_nNrOfNodes);
        final JLabel jLbNrOfArcs = new JLabel("Nr of arcs");
        final JTextField jTfNrOfArcs = new JTextField(3);
        jTfNrOfArcs.setHorizontalAlignment(JTextField.CENTER);
        jTfNrOfArcs.setText("" + m_nNrOfArcs);
        final JLabel jLbCardinality = new JLabel("Cardinality");
        final JTextField jTfCardinality = new JTextField(3);
        jTfCardinality.setHorizontalAlignment(JTextField.CENTER);
        jTfCardinality.setText("" + m_nCardinality);
        final JLabel jLbSeed = new JLabel("Random seed");
        final JTextField jTfSeed = new JTextField(3);
        jTfSeed.setHorizontalAlignment(JTextField.CENTER);
        jTfSeed.setText("" + m_nSeed);

        JButton jBtGo;
        jBtGo = new JButton("Generate Network");

        jBtGo.addActionListener(new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent ae) {
            try {
              BayesNetGenerator generator = new BayesNetGenerator();
              m_BayesNet = generator;
              m_BayesNet.clearUndoStack();

              String[] options = new String[8];
              options[0] = "-N";
              options[1] = "" + jTfNrOfNodes.getText();
              options[2] = "-A";
              options[3] = "" + jTfNrOfArcs.getText();
              options[4] = "-C";
              options[5] = "" + jTfCardinality.getText();
              options[6] = "-S";
              options[7] = "" + jTfSeed.getText();
              generator.setOptions(options);
              generator.generateRandomNetwork();
              // Convert to EditableBayesNet
              // This ensures the getOptions() called by GenericObjectEditor to
              // get the correct result.
              BIFReader bifReader = new BIFReader();
              bifReader.processString(m_BayesNet.toXMLBIF03());
              m_BayesNet = new EditableBayesNet(bifReader);

              updateStatus();
              layoutGraph();
              a_datagenerator.setEnabled(true);
              m_Instances = null;
              ;
              a_learn.setEnabled(false);
              a_learnCPT.setEnabled(false);

              dlg.setVisible(false);
            } catch (Exception e) {
              e.printStackTrace();
            }
          }
        });

        JButton jBtCancel;
        jBtCancel = new JButton("Cancel");
        jBtCancel.setMnemonic('C');
        jBtCancel.addActionListener(new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent ae) {
            dlg.setVisible(false);
          }
        });
        GridBagConstraints gbc = new GridBagConstraints();
        dlg.setLayout(new GridBagLayout());

        Container c = new Container();
        c.setLayout(new GridBagLayout());
        gbc.gridwidth = 2;
        gbc.insets = new Insets(8, 0, 0, 0);
        gbc.anchor = GridBagConstraints.NORTHWEST;
        gbc.gridwidth = GridBagConstraints.RELATIVE;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        c.add(jLbNrOfNodes, gbc);
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        c.add(jTfNrOfNodes, gbc);
        gbc.gridwidth = GridBagConstraints.RELATIVE;
        c.add(jLbNrOfArcs, gbc);
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        c.add(jTfNrOfArcs, gbc);
        gbc.gridwidth = GridBagConstraints.RELATIVE;
        c.add(jLbCardinality, gbc);
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        c.add(jTfCardinality, gbc);
        gbc.gridwidth = GridBagConstraints.RELATIVE;
        c.add(jLbSeed, gbc);
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        c.add(jTfSeed, gbc);

        gbc.fill = GridBagConstraints.HORIZONTAL;
        dlg.add(c, gbc);
        dlg.add(jBtGo);
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        dlg.add(jBtCancel);
      }
      dlg.pack();
      dlg.setLocation(100, 100);
      dlg.setVisible(true);
      dlg.setSize(dlg.getPreferredSize());
      dlg.setVisible(false);
      dlg.setVisible(true);
      dlg.repaint();
    } // actionPerformed
  } // class ActionGenerate

  class ActionGenerateData extends MyAction {
    /** for serialization */
    private static final long serialVersionUID = -2038911085935516L;

    public ActionGenerateData() {
      super("Generate Data", "Generate Random Instances from Network",
        "generate.data", "ctrl D");
    } // c'tor

    int m_nNrOfInstances = 100;

    int m_nSeed = 1234;

    String m_sFile = "";

    JDialog dlg = null;

    @Override
    public void actionPerformed(ActionEvent ae) {
      if (dlg == null) {
        dlg = new JDialog();
        dlg.setTitle("Generate Random Data Options");

        final JLabel jLbNrOfInstances = new JLabel("Nr of instances");
        final JTextField jTfNrOfInstances = new JTextField(3);
        jTfNrOfInstances.setHorizontalAlignment(JTextField.CENTER);
        jTfNrOfInstances.setText("" + m_nNrOfInstances);
        final JLabel jLbSeed = new JLabel("Random seed");
        final JTextField jTfSeed = new JTextField(3);
        jTfSeed.setHorizontalAlignment(JTextField.CENTER);
        jTfSeed.setText("" + m_nSeed);
        final JLabel jLbFile = new JLabel("Output file (optional)");
        final JTextField jTfFile = new JTextField(12);
        jTfFile.setHorizontalAlignment(JTextField.CENTER);
        jTfFile.setText(m_sFile);

        JButton jBtGo;
        jBtGo = new JButton("Generate Data");

        jBtGo.addActionListener(new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent ae) {
            try {
              String tmpfilename = "tmp.bif.file.xml";
              BayesNetGenerator generator = new BayesNetGenerator();
              String[] options = new String[4];
              options[0] = "-M";
              options[1] = "" + jTfNrOfInstances.getText();
              options[2] = "-F";
              options[3] = tmpfilename;
              FileWriter outfile = new FileWriter(tmpfilename);
              StringBuffer text = new StringBuffer();
              if (m_marginCalculator == null) {
                m_marginCalculator = new MarginCalculator();
                m_marginCalculator.calcMargins(m_BayesNet);
              }
              text.append(m_marginCalculator.toXMLBIF03());
              outfile.write(text.toString());
              outfile.close();

              generator.setOptions(options);
              generator.generateRandomNetwork();
              generator.generateInstances();
              m_Instances = generator.m_Instances;
              a_learn.setEnabled(true);
              a_learnCPT.setEnabled(true);

              m_sFile = jTfFile.getText();
              if (m_sFile != null && !m_sFile.equals("")) {
                AbstractFileSaver saver = ConverterUtils
                  .getSaverForFile(m_sFile);
                // no idea what the format is, so let's save it as ARFF file
                if (saver == null) {
                  saver = new ArffSaver();
                }
                saver.setFile(new File(m_sFile));
                saver.setInstances(m_Instances);
                saver.writeBatch();
              }

            } catch (Exception e) {
              e.printStackTrace();
            }
            dlg.setVisible(false);
          }
        });

        JButton jBtFile = new JButton("Browse");
        jBtFile.addActionListener(new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent ae) {
            ConverterFileChooser fc = new ConverterFileChooser(System
              .getProperty("user.dir"));
            fc.setDialogTitle("Save Instances As");
            int rval = fc.showSaveDialog(GUI.this);

            if (rval == JFileChooser.APPROVE_OPTION) {
              String filename = fc.getSelectedFile().toString();
              jTfFile.setText(filename);
            }
            dlg.setVisible(true);
          }
        });
        JButton jBtCancel;
        jBtCancel = new JButton("Cancel");
        jBtCancel.setMnemonic('C');
        jBtCancel.addActionListener(new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent ae) {
            dlg.setVisible(false);
          }
        });
        GridBagConstraints gbc = new GridBagConstraints();
        dlg.setLayout(new GridBagLayout());

        Container c = new Container();
        c.setLayout(new GridBagLayout());
        gbc.gridwidth = 2;
        gbc.insets = new Insets(8, 0, 0, 0);
        gbc.anchor = GridBagConstraints.NORTHWEST;
        gbc.gridwidth = GridBagConstraints.RELATIVE;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        c.add(jLbNrOfInstances, gbc);
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        c.add(jTfNrOfInstances, gbc);
        gbc.gridwidth = GridBagConstraints.RELATIVE;
        c.add(jLbSeed, gbc);
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        c.add(jTfSeed, gbc);
        gbc.gridwidth = GridBagConstraints.RELATIVE;
        c.add(jLbFile, gbc);
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        c.add(jTfFile, gbc);
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        c.add(jBtFile, gbc);

        gbc.fill = GridBagConstraints.HORIZONTAL;
        dlg.add(c, gbc);
        dlg.add(jBtGo);
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        dlg.add(jBtCancel);
      }
      dlg.setLocation(100, 100);
      dlg.setVisible(true);
      dlg.setSize(dlg.getPreferredSize());
      dlg.setVisible(false);
      dlg.setVisible(true);
      dlg.repaint();

    } // actionPerformed
  } // class ActionGenerateData

  class ActionLearn extends MyAction {
    /** for serialization */
    private static final long serialVersionUID = -2038911085935516L;

    public ActionLearn() {
      super("Learn Network", "Learn Bayesian Network", "learn", "ctrl L");
      setEnabled(false);
    } // c'tor

    JDialog dlg = null;

    @Override
    public void actionPerformed(ActionEvent ae) {
      if (dlg == null) {
        dlg = new JDialog();
        dlg.setTitle("Learn Bayesian Network");

        final JButton jBtOptions = new JButton("Options");
        jBtOptions.addActionListener(new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent ae) {
            // m_BayesNet = new EditableBayesNet();
            try {
              GenericObjectEditor.registerEditors();
              GenericObjectEditor ce = new GenericObjectEditor(true);
              ce.setClassType(weka.classifiers.Classifier.class);
              ce.setValue(m_BayesNet);

              PropertyDialog pd;
              if (PropertyDialog.getParentDialog(GUI.this) != null) {
                pd = new PropertyDialog(PropertyDialog
                  .getParentDialog(GUI.this), ce, 100, 100);
              } else {
                pd = new PropertyDialog(
                  PropertyDialog.getParentFrame(GUI.this), ce, 100, 100);
              }
              pd.addWindowListener(new WindowAdapter() {
                @Override
                public void windowClosing(WindowEvent e) {
                  PropertyEditor pe = ((PropertyDialog) e.getSource())
                    .getEditor();
                  Object c = pe.getValue();
                  String options = "";
                  if (c instanceof OptionHandler) {
                    options = Utils.joinOptions(((OptionHandler) c)
                      .getOptions());
                    try {
                      m_BayesNet.setOptions(((OptionHandler) c).getOptions());
                    } catch (Exception e2) {
                      e2.printStackTrace();
                    }
                  }
                  System.out.println(c.getClass().getName() + " " + options);
                  System.exit(0);
                }
              });
              pd.setVisible(true);
            } catch (Exception ex) {
              ex.printStackTrace();
              System.err.println(ex.getMessage());
            }
            m_BayesNet.clearUndoStack();
            a_undo.setEnabled(false);
            a_redo.setEnabled(false);
          }
        });

        final JTextField jTfOptions = new JTextField(40);
        jTfOptions.setHorizontalAlignment(JTextField.CENTER);
        jTfOptions.setText("" + Utils.joinOptions(m_BayesNet.getOptions()));

        JButton jBtGo;
        jBtGo = new JButton("Learn");

        jBtGo.addActionListener(new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent ae) {
            try {
              m_BayesNet.buildClassifier(m_Instances);
              layoutGraph();
              updateStatus();
              m_BayesNet.clearUndoStack();

              dlg.setVisible(false);
            } catch (Exception e) {
              e.printStackTrace();
            }
            dlg.setVisible(false);
          }
        });

        JButton jBtCancel;
        jBtCancel = new JButton("Cancel");
        jBtCancel.setMnemonic('C');
        jBtCancel.addActionListener(new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent ae) {
            dlg.setVisible(false);
          }
        });
        GridBagConstraints gbc = new GridBagConstraints();
        dlg.setLayout(new GridBagLayout());

        Container c = new Container();
        c.setLayout(new GridBagLayout());
        gbc.gridwidth = 2;
        gbc.insets = new Insets(8, 0, 0, 0);
        gbc.anchor = GridBagConstraints.NORTHWEST;
        gbc.gridwidth = GridBagConstraints.RELATIVE;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        c.add(jBtOptions, gbc);
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        c.add(jTfOptions, gbc);

        gbc.fill = GridBagConstraints.HORIZONTAL;
        dlg.add(c, gbc);
        dlg.add(jBtGo);
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        dlg.add(jBtCancel);
      }
      dlg.setLocation(100, 100);
      dlg.setVisible(true);
      dlg.setSize(dlg.getPreferredSize());
      dlg.setVisible(false);
      dlg.setVisible(true);
      dlg.repaint();
    } // actionPerformed
  } // class ActionLearn

  class ActionLearnCPT extends MyAction {
    /** for serialization */
    private static final long serialVersionUID = -2022211085935516L;

    public ActionLearnCPT() {
      super("Learn CPT", "Learn conditional probability tables", "learncpt", "");
      setEnabled(false);
    } // c'tor

    @Override
    public void actionPerformed(ActionEvent ae) {
      if (m_Instances == null) {
        JOptionPane.showMessageDialog(null,
          "Select instances to learn from first (menu Tools/Set Data)");
        return;
      }
      try {
        m_BayesNet.setData(m_Instances);
      } catch (Exception e) {
        JOptionPane.showMessageDialog(null,
          "Data set is not compatible with network.\n" + e.getMessage()
            + "\nChoose other instances (menu Tools/Set Data)");
        return;
      }
      try {
        m_BayesNet.estimateCPTs();
        m_BayesNet.clearUndoStack();
      } catch (Exception e) {
        e.printStackTrace();
      }
      updateStatus();
    } // actionPerformed
  } // class ActionLearnCPT

  class ActionSetData extends MyAction {
    /** for serialization */
    private static final long serialVersionUID = -2038911085935519L;

    public ActionSetData() {
      super("Set Data", "Set Data File", "setdata", "ctrl A");
    } // c'tor

    @Override
    public void actionPerformed(ActionEvent ae) {
      ConverterFileChooser fc = new ConverterFileChooser(
        System.getProperty("user.dir"));
      fc.setDialogTitle("Set Data File");
      int rval = fc.showOpenDialog(GUI.this);

      if (rval == JFileChooser.APPROVE_OPTION) {
        AbstractFileLoader loader = fc.getLoader();
        try {
          if (loader != null) {
            m_Instances = loader.getDataSet();
          }
          if (m_Instances.classIndex() == -1) {
            m_Instances.setClassIndex(m_Instances.numAttributes() - 1);
          }
          a_learn.setEnabled(true);
          a_learnCPT.setEnabled(true);
          repaint();
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
    }
  } // class ActionSetData

  class ActionUndo extends MyAction {
    /** for serialization */
    private static final long serialVersionUID = -3038910085935519L;

    public ActionUndo() {
      super("Undo", "Undo", "undo", "ctrl Z");
      setEnabled(false);
    } // c'tor

    @Override
    public boolean isEnabled() {
      return m_BayesNet.canUndo();
    }

    @Override
    public void actionPerformed(ActionEvent ae) {
      String sMsg = m_BayesNet.undo();
      m_jStatusBar.setText("Undo action performed: " + sMsg);
      // if (!sMsg.equals("")) {
      // JOptionPane.showMessageDialog(null, sMsg, "Undo action successful",
      // JOptionPane.INFORMATION_MESSAGE);
      // }
      a_redo.setEnabled(m_BayesNet.canRedo());
      a_undo.setEnabled(m_BayesNet.canUndo());
      m_Selection.clear();
      updateStatus();
      repaint();
    }
  } // ActionUndo

  class ActionRedo extends MyAction {
    /** for serialization */
    private static final long serialVersionUID = -4038910085935519L;

    public ActionRedo() {
      super("Redo", "Redo", "redo", "ctrl Y");
      setEnabled(false);
    } // c'tor

    @Override
    public boolean isEnabled() {
      return m_BayesNet.canRedo();
    }

    @Override
    public void actionPerformed(ActionEvent ae) {
      String sMsg = m_BayesNet.redo();
      m_jStatusBar.setText("Redo action performed: " + sMsg);
      // if (!sMsg.equals("")) {
      // JOptionPane.showMessageDialog(null, sMsg, "Redo action successful",
      // JOptionPane.INFORMATION_MESSAGE);
      // }
      m_Selection.clear();
      updateStatus();
      repaint();
    }
  } // ActionRedo

  class ActionAddNode extends MyAction {
    /** for serialization */
    private static final long serialVersionUID = -2038910085935519L;

    public ActionAddNode() {
      super("Add Node", "Add Node", "addnode", "");
    } // c'tor

    JDialog dlg = null;

    JTextField jTfName = new JTextField(20);

    JTextField jTfCard = new JTextField(3);

    int m_X = Integer.MAX_VALUE;
    int m_Y;

    public void addNode(int nX, int nY) {
      m_X = nX;
      m_Y = nY;
      addNode();
    } // addNode

    void addNode() {
      if (dlg == null) {
        dlg = new JDialog();
        dlg.setTitle("Add node");
        JLabel jLbName = new JLabel("Name");
        jTfName.setHorizontalAlignment(JTextField.CENTER);
        JLabel jLbCard = new JLabel("Cardinality");
        jTfCard.setHorizontalAlignment(JTextField.CENTER);
        jTfCard.setText("2");

        JButton jBtCancel;
        jBtCancel = new JButton("Cancel");
        jBtCancel.setMnemonic('C');
        jBtCancel.addActionListener(new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent ae) {
            dlg.setVisible(false);
          }
        });
        JButton jBtOk = new JButton("Ok");
        jBtOk.setMnemonic('O');
        jBtOk.addActionListener(new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent ae) {
            String sName = jTfName.getText();
            if (sName.length() <= 0) {
              JOptionPane.showMessageDialog(null,
                "Name should have at least one character");
              return;
            }
            int nCard = new Integer(jTfCard.getText()).intValue();
            if (nCard <= 1) {
              JOptionPane.showMessageDialog(null,
                "Cardinality should be larger than 1");
              return;
            }
            try {
              if (m_X < Integer.MAX_VALUE) {
                m_BayesNet.addNode(sName, nCard, m_X, m_Y);
              } else {
                m_BayesNet.addNode(sName, nCard);
              }
              m_jStatusBar.setText(m_BayesNet.lastActionMsg());
              a_undo.setEnabled(true);
              a_redo.setEnabled(false);
              // GraphNode n = new GraphNode("id" + m_nodes.size(), sName);
              // n.probs = m_BayesNet.getDistribution(sName);
              // n.outcomes = m_BayesNet.getValues(sName);
              // n.x = 100 + m_nodes.size() * 10;
              // n.y = 100 + m_nodes.size() * 10;
              // m_nodes.addElement(n);
            } catch (Exception e) {
              e.printStackTrace();
            }
            repaint();
            dlg.setVisible(false);
          }
        });
        dlg.setLayout(new GridLayout(3, 2, 10, 10));
        dlg.add(jLbName);
        dlg.add(jTfName);
        dlg.add(jLbCard);
        dlg.add(jTfCard);
        dlg.add(jBtOk);
        dlg.add(jBtCancel);
        dlg.setSize(dlg.getPreferredSize());
      }
      jTfName.setText("Node" + (m_BayesNet.getNrOfNodes() + 1));
      dlg.setVisible(true);
    } // addNode

    @Override
    public void actionPerformed(ActionEvent ae) {
      m_X = Integer.MAX_VALUE;
      addNode();
    }
  } // class ActionAddNode

  class ActionDeleteNode extends MyAction {
    /** for serialization */
    private static final long serialVersionUID = -2038912085935519L;

    public ActionDeleteNode() {
      super("Delete Node", "Delete Node", "delnode", "DELETE");
    } // c'tor

    @Override
    public void actionPerformed(ActionEvent ae) {
      if (m_Selection.getSelected().size() > 0) {
        m_BayesNet.deleteSelection(m_Selection.getSelected());
        m_jStatusBar.setText(m_BayesNet.lastActionMsg());
        m_Selection.clear();
        updateStatus();
        repaint();
      } else {
        String[] options = new String[m_BayesNet.getNrOfNodes()];
        for (int i = 0; i < options.length; i++) {
          options[i] = m_BayesNet.getNodeName(i);
        }
        String sResult = (String) JOptionPane.showInputDialog(null,
          "Select node to delete", "Nodes", 0, null, options, options[0]);
        if (sResult != null && !sResult.equals("")) {
          int iNode = m_BayesNet.getNode2(sResult);
          deleteNode(iNode);
        }
      }
    }
  } // class ActionDeleteNode

  class ActionCopyNode extends MyAction {
    /** for serialization */
    private static final long serialVersionUID = -2038732085935519L;

    public ActionCopyNode() {
      super("Copy", "Copy Nodes", "copy", "ctrl C");
    } // c'tor

    public ActionCopyNode(String sName, String sToolTipText, String sIcon,
      String sAcceleratorKey) {
      super(sName, sToolTipText, sIcon, sAcceleratorKey);
    } // c'rot

    @Override
    public void actionPerformed(ActionEvent ae) {
      copy();
    }

    public void copy() {
      String sXML = m_BayesNet.toXMLBIF03(m_Selection.getSelected());
      m_clipboard.setText(sXML);
    } // copy
  } // class ActionCopyNode

  class ActionCutNode extends ActionCopyNode {
    /** for serialization */
    private static final long serialVersionUID = -2038822085935519L;

    public ActionCutNode() {
      super("Cut", "Cut Nodes", "cut", "ctrl X");
    } // c'tor

    @Override
    public void actionPerformed(ActionEvent ae) {
      copy();
      m_BayesNet.deleteSelection(m_Selection.getSelected());
      m_jStatusBar.setText(m_BayesNet.lastActionMsg());
      m_Selection.clear();
      a_undo.setEnabled(true);
      a_redo.setEnabled(false);
      repaint();
    }
  } // class ActionCutNode

  class ActionPasteNode extends MyAction {
    /** for serialization */
    private static final long serialVersionUID = -2038732085935519L;

    public ActionPasteNode() {
      super("Paste", "Paste Nodes", "paste", "ctrl V");
    } // c'tor

    @Override
    public void actionPerformed(ActionEvent ae) {
      try {
        m_BayesNet.paste(m_clipboard.getText());
        updateStatus();
        m_jStatusBar.setText(m_BayesNet.lastActionMsg());
      } catch (Exception e) {
        e.printStackTrace();
      }
    }

    @Override
    public boolean isEnabled() {
      return m_clipboard.hasText();
    }
  } // class ActionPasteNode

  class ActionSelectAll extends MyAction {
    /** for serialization */
    private static final long serialVersionUID = -2038642085935519L;

    public ActionSelectAll() {
      super("Select All", "Select All Nodes", "selectall", "ctrl A");
    } // c'tor

    @Override
    public void actionPerformed(ActionEvent ae) {
      m_Selection.selectAll();
      repaint();
    }
  } // class ActionSelectAll

  class ActionExport extends MyAction {
    boolean m_bIsExporting = false;
    /** for serialization */
    private static final long serialVersionUID = -3027642085935519L;

    public ActionExport() {
      super("Export", "Export to graphics file", "export", "");
    } // c'tor

    @Override
    public void actionPerformed(ActionEvent ae) {
      m_bIsExporting = true;
      m_GraphPanel.saveComponent();
      m_bIsExporting = false;
      repaint();
    }

    public boolean isExporting() {
      return m_bIsExporting;
    }
  } // class ActionExport

  class ActionAlignLeft extends MyAction {
    /** for serialization */
    private static final long serialVersionUID = -3138642085935519L;

    public ActionAlignLeft() {
      super("Align Left", "Align Left", "alignleft", "");
    } // c'tor

    @Override
    public void actionPerformed(ActionEvent ae) {
      m_BayesNet.alignLeft(m_Selection.getSelected());
      m_jStatusBar.setText(m_BayesNet.lastActionMsg());
      a_undo.setEnabled(true);
      a_redo.setEnabled(false);
      repaint();
    }
  } // class ActionAlignLeft

  class ActionAlignRight extends MyAction {
    /** for serialization */
    private static final long serialVersionUID = -4238642085935519L;

    public ActionAlignRight() {
      super("Align Right", "Align Right", "alignright", "");
    } // c'tor

    @Override
    public void actionPerformed(ActionEvent ae) {
      m_BayesNet.alignRight(m_Selection.getSelected());
      m_jStatusBar.setText(m_BayesNet.lastActionMsg());
      a_undo.setEnabled(true);
      a_redo.setEnabled(false);
      repaint();
    }
  } // class ActionAlignRight

  class ActionAlignTop extends MyAction {
    /** for serialization */
    private static final long serialVersionUID = -5338642085935519L;

    public ActionAlignTop() {
      super("Align Top", "Align Top", "aligntop", "");
    } // c'tor

    @Override
    public void actionPerformed(ActionEvent ae) {
      m_BayesNet.alignTop(m_Selection.getSelected());
      m_jStatusBar.setText(m_BayesNet.lastActionMsg());
      a_undo.setEnabled(true);
      a_redo.setEnabled(false);
      repaint();
    }
  } // class ActionAlignTop

  class ActionAlignBottom extends MyAction {
    /** for serialization */
    private static final long serialVersionUID = -6438642085935519L;

    public ActionAlignBottom() {
      super("Align Bottom", "Align Bottom", "alignbottom", "");
    } // c'tor

    @Override
    public void actionPerformed(ActionEvent ae) {
      m_BayesNet.alignBottom(m_Selection.getSelected());
      m_jStatusBar.setText(m_BayesNet.lastActionMsg());
      a_undo.setEnabled(true);
      a_redo.setEnabled(false);
      repaint();
    }
  } // class ActionAlignBottom

  class ActionCenterHorizontal extends MyAction {
    /** for serialization */
    private static final long serialVersionUID = -7538642085935519L;

    public ActionCenterHorizontal() {
      super("Center Horizontal", "Center Horizontal", "centerhorizontal", "");
    } // c'tor

    @Override
    public void actionPerformed(ActionEvent ae) {
      m_BayesNet.centerHorizontal(m_Selection.getSelected());
      m_jStatusBar.setText(m_BayesNet.lastActionMsg());
      a_undo.setEnabled(true);
      a_redo.setEnabled(false);
      repaint();
    }
  } // class ActionCenterHorizontal

  class ActionCenterVertical extends MyAction {
    /** for serialization */
    private static final long serialVersionUID = -8638642085935519L;

    public ActionCenterVertical() {
      super("Center Vertical", "Center Vertical", "centervertical", "");
    } // c'tor

    @Override
    public void actionPerformed(ActionEvent ae) {
      m_BayesNet.centerVertical(m_Selection.getSelected());
      m_jStatusBar.setText(m_BayesNet.lastActionMsg());
      a_undo.setEnabled(true);
      a_redo.setEnabled(false);
      repaint();
    }
  } // class ActionCenterVertical

  class ActionSpaceHorizontal extends MyAction {
    /** for serialization */
    private static final long serialVersionUID = -9738642085935519L;

    public ActionSpaceHorizontal() {
      super("Space Horizontal", "Space Horizontal", "spacehorizontal", "");
    } // c'tor

    @Override
    public void actionPerformed(ActionEvent ae) {
      m_BayesNet.spaceHorizontal(m_Selection.getSelected());
      m_jStatusBar.setText(m_BayesNet.lastActionMsg());
      a_undo.setEnabled(true);
      a_redo.setEnabled(false);
      repaint();
    }
  } // class ActionSpaceHorizontal

  class ActionSpaceVertical extends MyAction {
    /** for serialization */
    private static final long serialVersionUID = -838642085935519L;

    public ActionSpaceVertical() {
      super("Space Vertical", "Space Vertical", "spacevertical", "");
    } // c'tor

    @Override
    public void actionPerformed(ActionEvent ae) {
      m_BayesNet.spaceVertical(m_Selection.getSelected());
      m_jStatusBar.setText(m_BayesNet.lastActionMsg());
      a_undo.setEnabled(true);
      a_redo.setEnabled(false);
      repaint();
    }
  } // class ActionSpaceVertical

  class ActionAddArc extends MyAction {
    /** for serialization */
    private static final long serialVersionUID = -2038913085935519L;

    public ActionAddArc() {
      super("Add Arc", "Add Arc", "addarc", "");
    } // c'tor

    @Override
    public void actionPerformed(ActionEvent ae) {
      try {
        String[] options = new String[m_BayesNet.getNrOfNodes()];
        for (int i = 0; i < options.length; i++) {
          options[i] = m_BayesNet.getNodeName(i);
        }
        String sChild = (String) JOptionPane.showInputDialog(null,
          "Select child node", "Nodes", 0, null, options, options[0]);
        if (sChild == null || sChild.equals("")) {
          return;
        }
        int iChild = m_BayesNet.getNode(sChild);
        addArcInto(iChild);
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  } // class ActionAddArc

  class ActionDeleteArc extends MyAction {
    /** for serialization */
    private static final long serialVersionUID = -2038914085935519L;

    public ActionDeleteArc() {
      super("Delete Arc", "Delete Arc", "delarc", "");
    } // c'tor

    @Override
    public void actionPerformed(ActionEvent ae) {
      int nEdges = 0;
      for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) {
        nEdges += m_BayesNet.getNrOfParents(iNode);
      }
      String[] options = new String[nEdges];
      int i = 0;
      for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) {
        for (int iParent = 0; iParent < m_BayesNet.getNrOfParents(iNode); iParent++) {
          int nParent = m_BayesNet.getParent(iNode, iParent);
          String sEdge = m_BayesNet.getNodeName(nParent);
          sEdge += " -> ";
          sEdge += m_BayesNet.getNodeName(iNode);
          options[i++] = sEdge;
        }

      }
      deleteArc(options);
    }
  } // class ActionDeleteArc

  class ActionNew extends MyAction {
    /** for serialization */
    private static final long serialVersionUID = -2038911085935515L;

    public ActionNew() {
      super("New", "New Network", "new", "");
    } // c'tor

    @Override
    public void actionPerformed(ActionEvent ae) {
      m_sFileName = "";
      m_BayesNet = new EditableBayesNet(true);
      updateStatus();
      layoutGraph();
      a_datagenerator.setEnabled(false);
      m_BayesNet.clearUndoStack();
      m_jStatusBar.setText("New Network");
      m_Selection = new Selection();
      repaint();
    }
  } // class ActionNew

  class ActionLoad extends MyAction {
    /** for serialization */
    private static final long serialVersionUID = -2038911085935515L;

    public ActionLoad() {
      super("Load", "Load Graph", "open", "ctrl O");
    } // c'tor

    @Override
    public void actionPerformed(ActionEvent ae) {
      JFileChooser fc = new JFileChooser(System.getProperty("user.dir"));
      ExtensionFileFilter ef1 = new ExtensionFileFilter(".arff", "ARFF files");
      ExtensionFileFilter ef2 = new ExtensionFileFilter(".xml", "XML BIF files");
      fc.addChoosableFileFilter(ef1);
      fc.addChoosableFileFilter(ef2);
      fc.setDialogTitle("Load Graph");
      int rval = fc.showOpenDialog(GUI.this);

      if (rval == JFileChooser.APPROVE_OPTION) {
        String sFileName = fc.getSelectedFile().toString();
        if (sFileName.endsWith(ef1.getExtensions()[0])) {
          initFromArffFile(sFileName);
        } else {
          try {
            readBIFFromFile(sFileName);
          } catch (Exception e) {
            e.printStackTrace();
          }
        }
        m_jStatusBar.setText("Loaded " + sFileName);
        updateStatus();
      }
    }
  } // class ActionLoad

  class ActionViewStatusbar extends MyAction {
    /** for serialization */
    private static final long serialVersionUID = -20389330812354L;

    public ActionViewStatusbar() {
      super("View statusbar", "View statusbar", "statusbar", "");
    } // c'tor

    @Override
    public void actionPerformed(ActionEvent ae) {
      m_jStatusBar.setVisible(!m_jStatusBar.isVisible());
    } // actionPerformed
  } // class ActionViewStatusbar

  class ActionViewToolbar extends MyAction {
    /** for serialization */
    private static final long serialVersionUID = -20389110812354L;

    public ActionViewToolbar() {
      super("View toolbar", "View toolbar", "toolbar", "");
    } // c'tor

    @Override
    public void actionPerformed(ActionEvent ae) {
      m_jTbTools.setVisible(!m_jTbTools.isVisible());
    } // actionPerformed
  } // class ActionViewToolbar

  class ActionSave extends MyAction {
    /** for serialization */
    private static final long serialVersionUID = -20389110859355156L;

    public ActionSave() {
      super("Save", "Save Graph", "save", "ctrl S");
    } // c'tor

    public ActionSave(String sName, String sToolTipText, String sIcon,
      String sAcceleratorKey) {
      super(sName, sToolTipText, sIcon, sAcceleratorKey);
    } // c'tor

    @Override
    public void actionPerformed(ActionEvent ae) {
      if (!m_sFileName.equals("")) {
        saveFile(m_sFileName);
        m_BayesNet.isSaved();
        m_jStatusBar.setText("Saved as " + m_sFileName);
      } else {
        if (saveAs()) {
          m_BayesNet.isSaved();
          m_jStatusBar.setText("Saved as " + m_sFileName);
        }
      }
    } // actionPerformed

    ExtensionFileFilter ef1 = new ExtensionFileFilter(".xml", "XML BIF files");

    boolean saveAs() {
      JFileChooser fc = new JFileChooser(System.getProperty("user.dir"));
      fc.addChoosableFileFilter(ef1);
      fc.setDialogTitle("Save Graph As");
      if (!m_sFileName.equals("")) {
        // can happen on actionQuit
        fc.setSelectedFile(new File(m_sFileName));
      }
      int rval = fc.showSaveDialog(GUI.this);

      if (rval == JFileChooser.APPROVE_OPTION) {
        // System.out.println("Saving to file \""+
        // f.getAbsoluteFile().toString()+"\"");
        String sFileName = fc.getSelectedFile().toString();
        if (!sFileName.endsWith(".xml")) {
          sFileName = sFileName.concat(".xml");
        }
        saveFile(sFileName);
        return true;
      }
      return false;
    } // saveAs

    protected void saveFile(String sFileName) {
      try {
        FileWriter outfile = new FileWriter(sFileName);
        outfile.write(m_BayesNet.toXMLBIF03());
        outfile.close();
        m_sFileName = sFileName;
        m_jStatusBar.setText("Saved as " + m_sFileName);
      } catch (IOException e) {
        e.printStackTrace();
      }
    } // saveFile
  } // class ActionSave

  class ActionSaveAs extends ActionSave {
    /** for serialization */
    private static final long serialVersionUID = -20389110859354L;

    public ActionSaveAs() {
      super("Save As", "Save Graph As", "saveas", "");
    } // c'tor

    @Override
    public void actionPerformed(ActionEvent ae) {
      saveAs();
    } // actionPerformed
  } // class ActionSaveAs

  class ActionPrint extends ActionSave {
    /** for serialization */
    private static final long serialVersionUID = -20389001859354L;
    boolean m_bIsPrinting = false;

    public ActionPrint() {
      super("Print", "Print Graph", "print", "ctrl P");
    } // c'tor

    @Override
    public void actionPerformed(ActionEvent ae) {
      PrinterJob printJob = PrinterJob.getPrinterJob();
      printJob.setPrintable(m_GraphPanel);
      if (printJob.printDialog()) {
        try {
          m_bIsPrinting = true;
          printJob.print();
          m_bIsPrinting = false;
        } catch (PrinterException pe) {
          m_jStatusBar.setText("Error printing: " + pe);
          m_bIsPrinting = false;
        }
      }
      m_jStatusBar.setText("Print");
    } // actionPerformed

    public boolean isPrinting() {
      return m_bIsPrinting;
    }

  } // class ActionPrint

  class ActionQuit extends ActionSave {
    /** for serialization */
    private static final long serialVersionUID = -2038911085935515L;

    public ActionQuit() {
      super("Exit", "Exit Program", "exit", "");
    } // c'tor

    @Override
    public void actionPerformed(ActionEvent ae) {
      if (m_BayesNet.isChanged()) {
        int result = JOptionPane.showConfirmDialog(null,
          "Network changed. Do you want to save it?", "Save before closing?",
          JOptionPane.YES_NO_CANCEL_OPTION);
        if (result == JOptionPane.CANCEL_OPTION) {
          return;
        }
        if (result == JOptionPane.YES_OPTION) {
          if (!saveAs()) {
            return;
          }
        }
      }
      System.exit(0);
    }
  } // class ActionQuit

  class ActionHelp extends MyAction {
    /** for serialization */
    private static final long serialVersionUID = -20389110859354L;

    public ActionHelp() {
      super("Help", "Bayesian Network Workbench Help", "help", "");
    } // c'tor

    @Override
    public void actionPerformed(ActionEvent ae) {
      JOptionPane.showMessageDialog(null,
        "See Weka Homepage\nhttp://www.cs.waikato.ac.nz/ml", "Help Message",
        JOptionPane.PLAIN_MESSAGE);
    }
  } // class ActionHelp

  class ActionAbout extends MyAction {
    /** for serialization */
    private static final long serialVersionUID = -20389110859353L;

    public ActionAbout() {
      super("About", "Help about", "about", "");
    } // c'tor

    @Override
    public void actionPerformed(ActionEvent ae) {
      JOptionPane.showMessageDialog(null,
        "Bayesian Network Workbench\nPart of Weka\n2007", "About Message",
        JOptionPane.PLAIN_MESSAGE);
    }
  } // class ActionAbout

  class ActionZoomIn extends MyAction {
    /** for serialization */
    private static final long serialVersionUID = -2038911085935515L;

    public ActionZoomIn() {
      super("Zoom in", "Zoom in", "zoomin", "+");
    } // c'tor

    @Override
    public void actionPerformed(ActionEvent ae) {
      int i = 0, s = (int) (m_fScale * 100);
      if (s < 300) {
        i = s / 25;
      } else if (s < 700) {
        i = 6 + s / 50;
      } else {
        i = 13 + s / 100;
      }

      if (s >= 999) {
        setEnabled(false);
        return;
      } else if (s >= 10) {
        if (i >= 22) {
          setEnabled(false);
        }
        if (s == 10 && !a_zoomout.isEnabled()) {
          a_zoomout.setEnabled(true);
        }
        m_jTfZoom.setText(m_nZoomPercents[i + 1] + "%");
        m_fScale = m_nZoomPercents[i + 1] / 100D;
      } else {
        if (!a_zoomout.isEnabled()) {
          a_zoomout.setEnabled(true);
        }
        m_jTfZoom.setText(m_nZoomPercents[0] + "%");
        m_fScale = m_nZoomPercents[0] / 100D;
      }
      setAppropriateSize();
      m_GraphPanel.repaint();
      m_GraphPanel.invalidate();
      m_jScrollPane.revalidate();
      m_jStatusBar.setText("Zooming in");
    }
  } // class ActionZoomIn

  class ActionZoomOut extends MyAction {
    /** for serialization */
    private static final long serialVersionUID = -203891108593551L;

    public ActionZoomOut() {
      super("Zoom out", "Zoom out", "zoomout", "-");
    } // c'tor

    @Override
    public void actionPerformed(ActionEvent ae) {
      int i = 0, s = (int) (m_fScale * 100);
      if (s < 300) {
        i = (int) Math.ceil(s / 25D);
      } else if (s < 700) {
        i = 6 + (int) Math.ceil(s / 50D);
      } else {
        i = 13 + (int) Math.ceil(s / 100D);
      }

      if (s <= 10) {
        setEnabled(false);
      } else if (s < 999) {
        if (i <= 1) {
          setEnabled(false);
        }
        m_jTfZoom.setText(m_nZoomPercents[i - 1] + "%");
        m_fScale = m_nZoomPercents[i - 1] / 100D;
      } else {
        if (!a_zoomin.isEnabled()) {
          a_zoomin.setEnabled(true);
        }
        m_jTfZoom.setText(m_nZoomPercents[22] + "%");
        m_fScale = m_nZoomPercents[22] / 100D;
      }
      setAppropriateSize();
      m_GraphPanel.repaint();
      m_GraphPanel.invalidate();
      m_jScrollPane.revalidate();
      m_jStatusBar.setText("Zooming out");
    }
  } // class ActionZoomOut

  class ActionLayout extends MyAction {
    /** for serialization */
    private static final long serialVersionUID = -203891108593551L;

    public ActionLayout() {
      super("Layout", "Layout Graph", "layout", "ctrl L");
    } // c'tor

    JDialog dlg = null;

    @Override
    public void actionPerformed(ActionEvent ae) {
      if (dlg == null) {
        dlg = new JDialog();
        dlg.setTitle("Graph Layout Options");
        final JCheckBox jCbCustomNodeSize = new JCheckBox("Custom Node Size");
        final JLabel jLbNodeWidth = new JLabel("Width");
        final JLabel jLbNodeHeight = new JLabel("Height");

        m_jTfNodeWidth.setHorizontalAlignment(JTextField.CENTER);
        m_jTfNodeWidth.setText("" + m_nNodeWidth);
        m_jTfNodeHeight.setHorizontalAlignment(JTextField.CENTER);
        m_jTfNodeHeight.setText("" + m_nNodeHeight);
        jLbNodeWidth.setEnabled(false);
        m_jTfNodeWidth.setEnabled(false);
        jLbNodeHeight.setEnabled(false);
        m_jTfNodeHeight.setEnabled(false);

        jCbCustomNodeSize.addActionListener(new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent ae) {
            if (((JCheckBox) ae.getSource()).isSelected()) {
              jLbNodeWidth.setEnabled(true);
              m_jTfNodeWidth.setEnabled(true);
              jLbNodeHeight.setEnabled(true);
              m_jTfNodeHeight.setEnabled(true);
            } else {
              jLbNodeWidth.setEnabled(false);
              m_jTfNodeWidth.setEnabled(false);
              jLbNodeHeight.setEnabled(false);
              m_jTfNodeHeight.setEnabled(false);
              setAppropriateSize();
              setAppropriateNodeSize();
            }
          }
        });
        JButton jBtLayout;
        jBtLayout = new JButton("Layout Graph");
        jBtLayout.setMnemonic('L');

        jBtLayout.addActionListener(new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent ae) {
            int tmpW, tmpH;

            if (jCbCustomNodeSize.isSelected()) {
              try {
                tmpW = Integer.parseInt(m_jTfNodeWidth.getText());
              } catch (NumberFormatException ne) {
                JOptionPane.showMessageDialog(GUI.this.getParent(),
                  "Invalid integer entered for node width.", "Error",
                  JOptionPane.ERROR_MESSAGE);
                tmpW = m_nNodeWidth;
                m_jTfNodeWidth.setText("" + m_nNodeWidth);

              }
              try {
                tmpH = Integer.parseInt(m_jTfNodeHeight.getText());
              } catch (NumberFormatException ne) {
                JOptionPane.showMessageDialog(GUI.this.getParent(),
                  "Invalid integer entered for node height.", "Error",
                  JOptionPane.ERROR_MESSAGE);
                tmpH = m_nNodeHeight;
                m_jTfNodeWidth.setText("" + m_nNodeHeight);
              }

              if (tmpW != m_nNodeWidth || tmpH != m_nNodeHeight) {
                m_nNodeWidth = tmpW;
                m_nPaddedNodeWidth = m_nNodeWidth + PADDING;
                m_nNodeHeight = tmpH;
              }
            }
            // JButton bt = (JButton) ae.getSource();
            // bt.setEnabled(false);
            dlg.setVisible(false);
            updateStatus();
            layoutGraph();
            m_jStatusBar.setText("Laying out Bayes net");
          }
        });
        JButton jBtCancel;
        jBtCancel = new JButton("Cancel");
        jBtCancel.setMnemonic('C');
        jBtCancel.addActionListener(new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent ae) {
            dlg.setVisible(false);
          }
        });
        GridBagConstraints gbc = new GridBagConstraints();
        dlg.setLayout(new GridBagLayout());
        // dlg.add(m_le.getControlPanel());

        Container c = new Container();
        c.setLayout(new GridBagLayout());

        gbc.gridwidth = 1;
        gbc.insets = new Insets(8, 0, 0, 0);
        gbc.anchor = GridBagConstraints.NORTHWEST;
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        c.add(jCbCustomNodeSize, gbc);
        gbc.gridwidth = GridBagConstraints.RELATIVE;
        c.add(jLbNodeWidth, gbc);
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        c.add(m_jTfNodeWidth, gbc);
        gbc.gridwidth = GridBagConstraints.RELATIVE;
        c.add(jLbNodeHeight, gbc);
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        c.add(m_jTfNodeHeight, gbc);
        gbc.fill = GridBagConstraints.HORIZONTAL;
        dlg.add(c, gbc);
        dlg.add(jBtLayout);
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        dlg.add(jBtCancel);
      }
      dlg.setLocation(100, 100);
      dlg.setVisible(true);
      dlg.setSize(dlg.getPreferredSize());
      dlg.setVisible(false);
      dlg.setVisible(true);
      dlg.repaint();
    }
  } // class ActionLayout

  /**
   * Constructor
* Sets up the gui and initializes all the other previously uninitialized * variables. */ public GUI() { m_GraphPanel = new GraphPanel(); m_jScrollPane = new JScrollPane(m_GraphPanel); // creating a new layout engine and adding this class as its listener // to receive layoutComplete events m_jTfZoom = new JTextField("100%"); m_jTfZoom.setMinimumSize(m_jTfZoom.getPreferredSize()); m_jTfZoom.setHorizontalAlignment(JTextField.CENTER); m_jTfZoom.setToolTipText("Zoom"); m_jTfZoom.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent ae) { JTextField jt = (JTextField) ae.getSource(); try { int i = -1; i = jt.getText().indexOf('%'); if (i == -1) { i = Integer.parseInt(jt.getText()); } else { i = Integer.parseInt(jt.getText().substring(0, i)); } if (i <= 999) { m_fScale = i / 100D; } jt.setText((int) (m_fScale * 100) + "%"); if (m_fScale > 0.1) { if (!a_zoomout.isEnabled()) { a_zoomout.setEnabled(true); } } else { a_zoomout.setEnabled(false); } if (m_fScale < 9.99) { if (!a_zoomin.isEnabled()) { a_zoomin.setEnabled(true); } } else { a_zoomin.setEnabled(false); } setAppropriateSize(); // m_GraphPanel.clearBuffer(); m_GraphPanel.repaint(); m_GraphPanel.invalidate(); m_jScrollPane.revalidate(); } catch (NumberFormatException ne) { JOptionPane.showMessageDialog(GUI.this.getParent(), "Invalid integer entered for zoom.", "Error", JOptionPane.ERROR_MESSAGE); jt.setText((m_fScale * 100) + "%"); } } }); GridBagConstraints gbc = new GridBagConstraints(); final JPanel p = new JPanel(new GridBagLayout()); p.setBorder(BorderFactory.createCompoundBorder( BorderFactory.createTitledBorder("ExtraControls"), BorderFactory.createEmptyBorder(4, 4, 4, 4))); p.setPreferredSize(new Dimension(0, 0)); m_jTbTools = new JToolBar(); m_jTbTools.setFloatable(false); m_jTbTools.setLayout(new GridBagLayout()); gbc.anchor = GridBagConstraints.NORTHWEST; gbc.gridwidth = GridBagConstraints.REMAINDER; gbc.insets = new Insets(0, 0, 0, 0); m_jTbTools.add(p, gbc); gbc.gridwidth = 1; m_jTbTools.add(a_new); m_jTbTools.add(a_save); m_jTbTools.add(a_load); m_jTbTools.addSeparator(new Dimension(2, 2)); m_jTbTools.add(a_cutnode); m_jTbTools.add(a_copynode); m_jTbTools.add(a_pastenode); m_jTbTools.addSeparator(new Dimension(2, 2)); m_jTbTools.add(a_undo); m_jTbTools.add(a_redo); m_jTbTools.addSeparator(new Dimension(2, 2)); m_jTbTools.add(a_alignleft); m_jTbTools.add(a_alignright); m_jTbTools.add(a_aligntop); m_jTbTools.add(a_alignbottom); m_jTbTools.add(a_centerhorizontal); m_jTbTools.add(a_centervertical); m_jTbTools.add(a_spacehorizontal); m_jTbTools.add(a_spacevertical); m_jTbTools.addSeparator(new Dimension(2, 2)); m_jTbTools.add(a_zoomin); gbc.fill = GridBagConstraints.VERTICAL; gbc.weighty = 1; JPanel p2 = new JPanel(new BorderLayout()); p2.setPreferredSize(m_jTfZoom.getPreferredSize()); p2.setMinimumSize(m_jTfZoom.getPreferredSize()); p2.add(m_jTfZoom, BorderLayout.CENTER); m_jTbTools.add(p2, gbc); gbc.weighty = 0; gbc.fill = GridBagConstraints.NONE; m_jTbTools.add(a_zoomout); m_jTbTools.addSeparator(new Dimension(2, 2)); // jTbTools.add(jBtExtraControls, gbc); m_jTbTools.add(a_layout); m_jTbTools.addSeparator(new Dimension(4, 2)); gbc.weightx = 1; gbc.fill = GridBagConstraints.BOTH; // jTbTools.add(m_layoutEngine.getProgressBar(), gbc); m_jStatusBar = new JLabel("Status bar"); this.setLayout(new BorderLayout()); this.add(m_jTbTools, BorderLayout.NORTH); this.add(m_jScrollPane, BorderLayout.CENTER); this.add(m_jStatusBar, BorderLayout.SOUTH); updateStatus(); a_datagenerator.setEnabled(false); makeMenuBar(); } /** * Get the menu bar for this application. * * @return the menu bar */ public JMenuBar getMenuBar() { return m_menuBar; } private void makeMenuBar() { m_menuBar = new JMenuBar(); JMenu fileMenu = new JMenu("File"); fileMenu.setMnemonic('F'); m_menuBar.add(fileMenu); fileMenu.add(a_new); fileMenu.add(a_load); fileMenu.add(a_save); fileMenu.add(a_saveas); fileMenu.addSeparator(); fileMenu.add(a_print); fileMenu.add(a_export); fileMenu.addSeparator(); fileMenu.add(a_quit); JMenu editMenu = new JMenu("Edit"); editMenu.setMnemonic('E'); m_menuBar.add(editMenu); editMenu.add(a_undo); editMenu.add(a_redo); editMenu.addSeparator(); editMenu.add(a_selectall); editMenu.add(a_delnode); editMenu.add(a_cutnode); editMenu.add(a_copynode); editMenu.add(a_pastenode); editMenu.addSeparator(); editMenu.add(a_addnode); editMenu.add(a_addarc); editMenu.add(a_delarc); editMenu.addSeparator(); editMenu.add(a_alignleft); editMenu.add(a_alignright); editMenu.add(a_aligntop); editMenu.add(a_alignbottom); editMenu.add(a_centerhorizontal); editMenu.add(a_centervertical); editMenu.add(a_spacehorizontal); editMenu.add(a_spacevertical); JMenu toolMenu = new JMenu("Tools"); toolMenu.setMnemonic('T'); toolMenu.add(a_networkgenerator); toolMenu.add(a_datagenerator); toolMenu.add(a_datasetter); toolMenu.add(a_learn); toolMenu.add(a_learnCPT); toolMenu.addSeparator(); toolMenu.add(a_layout); toolMenu.addSeparator(); final JCheckBoxMenuItem viewMargins = new JCheckBoxMenuItem("Show Margins", false); viewMargins.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent ae) { boolean bPrev = m_bViewMargins; m_bViewMargins = viewMargins.getState(); if (bPrev == false && viewMargins.getState() == true) { updateStatus(); } repaint(); } }); toolMenu.add(viewMargins); final JCheckBoxMenuItem viewCliques = new JCheckBoxMenuItem("Show Cliques", false); viewCliques.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent ae) { boolean bPrev = m_bViewCliques; m_bViewCliques = viewCliques.getState(); if (bPrev == false && viewCliques.getState() == true) { updateStatus(); } repaint(); } }); toolMenu.add(viewCliques); m_menuBar.add(toolMenu); JMenu viewMenu = new JMenu("View"); viewMenu.setMnemonic('V'); m_menuBar.add(viewMenu); viewMenu.add(a_zoomin); viewMenu.add(a_zoomout); viewMenu.addSeparator(); viewMenu.add(a_viewtoolbar); viewMenu.add(a_viewstatusbar); JMenu helpMenu = new JMenu("Help"); helpMenu.setMnemonic('H'); m_menuBar.add(helpMenu); helpMenu.add(a_help); helpMenu.add(a_about); } /** * This method sets the node size that is appropriate considering the maximum * label size that is present. It is used internally when custom node size * checkbox is unchecked. */ protected void setAppropriateNodeSize() { int strWidth; FontMetrics fm = this.getFontMetrics(this.getFont()); int nMaxStringWidth = DEFAULT_NODE_WIDTH; if (nMaxStringWidth == 0) { for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) { strWidth = fm.stringWidth(m_BayesNet.getNodeName(iNode)); if (strWidth > nMaxStringWidth) { nMaxStringWidth = strWidth; } } } m_nNodeWidth = nMaxStringWidth + 4; m_nPaddedNodeWidth = m_nNodeWidth + PADDING; m_jTfNodeWidth.setText("" + m_nNodeWidth); m_nNodeHeight = 2 * fm.getHeight(); m_jTfNodeHeight.setText("" + m_nNodeHeight); } /** * Sets the preferred size for m_GraphPanel GraphPanel to the minimum size * that is neccessary to display the graph. */ public void setAppropriateSize() { int maxX = 0, maxY = 0; m_GraphPanel.setScale(m_fScale, m_fScale); for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) { int nPosX = m_BayesNet.getPositionX(iNode); int nPosY = m_BayesNet.getPositionY(iNode); if (maxX < nPosX) { maxX = nPosX + 100; } if (maxY < nPosY) { maxY = nPosY; } } m_GraphPanel.setPreferredSize(new Dimension((int) ((maxX + m_nPaddedNodeWidth + 2) * m_fScale), (int) ((maxY + m_nNodeHeight + 2) * m_fScale))); m_GraphPanel.revalidate(); } // setAppropriateSize /** * This method is an implementation for LayoutCompleteEventListener class. It * sets the size appropriate for m_GraphPanel GraphPanel and and revalidates * it's container JScrollPane once a LayoutCompleteEvent is received from the * LayoutEngine. Also, it updates positions of the Bayesian network stored in * m_BayesNet. */ @Override public void layoutCompleted(LayoutCompleteEvent le) { LayoutEngine layoutEngine = m_layoutEngine; // (LayoutEngine) // le.getSource(); ArrayList nPosX = new ArrayList(m_BayesNet.getNrOfNodes()); ArrayList nPosY = new ArrayList(m_BayesNet.getNrOfNodes()); for (int iNode = 0; iNode < layoutEngine.getNodes().size(); iNode++) { GraphNode gNode = layoutEngine.getNodes().get(iNode); if (gNode.nodeType == GraphNode.NORMAL) { nPosX.add(gNode.x); nPosY.add(gNode.y); } } m_BayesNet.layoutGraph(nPosX, nPosY); m_jStatusBar.setText("Graph layed out"); a_undo.setEnabled(true); a_redo.setEnabled(false); setAppropriateSize(); m_GraphPanel.invalidate(); m_jScrollPane.revalidate(); m_GraphPanel.repaint(); } // layoutCompleted /** * BIF reader
* Reads a graph description in XMLBIF03 from an file with name sFileName */ public void readBIFFromFile(String sFileName) throws BIFFormatException, IOException { m_sFileName = sFileName; try { BIFReader bayesNet = new BIFReader(); bayesNet.processFile(sFileName); m_BayesNet = new EditableBayesNet(bayesNet); updateStatus(); a_datagenerator.setEnabled(m_BayesNet.getNrOfNodes() > 0); m_BayesNet.clearUndoStack(); } catch (Exception ex) { ex.printStackTrace(); return; } setAppropriateNodeSize(); setAppropriateSize(); } // readBIFFromFile /* * read arff file from file sFileName and start new Bayesian network with * nodes representing attributes in data set. */ void initFromArffFile(String sFileName) { try { Instances instances = new Instances(new FileReader(sFileName)); m_BayesNet = new EditableBayesNet(instances); m_Instances = instances; a_learn.setEnabled(true); a_learnCPT.setEnabled(true); setAppropriateNodeSize(); setAppropriateSize(); } catch (Exception ex) { ex.printStackTrace(); return; } } // initFromArffFile /** * The panel which contains the actual Bayeian network. */ private class GraphPanel extends PrintablePanel implements Printable { /** for serialization */ private static final long serialVersionUID = -3562813603236753173L; /** node drawing modes */ final static int HIGHLIGHTED = 1; final static int NORMAL = 0; public GraphPanel() { super(); this.addMouseListener(new GraphVisualizerMouseListener()); this.addMouseMotionListener(new GraphVisualizerMouseMotionListener()); this.setToolTipText(""); } // c'tor /* * For showing instructions when hovering over a node (non-Javadoc) * * @see javax.swing.JComponent#getToolTipText(java.awt.event.MouseEvent) */ @Override public String getToolTipText(MouseEvent me) { int x, y; Rectangle r; x = y = 0; r = new Rectangle(0, 0, (int) (m_nPaddedNodeWidth * m_fScale), (int) (m_nNodeHeight * m_fScale)); x += me.getX(); y += me.getY(); for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) { r.x = (int) (m_BayesNet.getPositionX(iNode) * m_fScale); r.y = (int) (m_BayesNet.getPositionY(iNode) * m_fScale); if (r.contains(x, y)) { return m_BayesNet.getNodeName(iNode) + " (right click to manipulate this node)"; } } return null; } // getToolTipText /* * Code for showing the graph in the panel. (non-Javadoc) * * @see javax.swing.JComponent#paintComponent(java.awt.Graphics) */ @Override public void paintComponent(Graphics gr) { Graphics2D g = (Graphics2D) gr; RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); rh.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED); g.setRenderingHints(rh); g.scale(m_fScale, m_fScale); Rectangle r = g.getClipBounds(); g.clearRect(r.x, r.y, r.width, r.height); if (m_bViewCliques) { m_nClique = 1; viewCliques(g, m_marginCalculator.m_root); } for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) { drawNode(g, iNode, NORMAL); } if (!a_export.isExporting() && !a_print.isPrinting()) { m_Selection.draw(g); } if (m_nSelectedRect != null) { g.drawRect((int) (m_nSelectedRect.x / m_fScale), (int) (m_nSelectedRect.y / m_fScale), (int) (m_nSelectedRect.width / m_fScale), (int) (m_nSelectedRect.height / m_fScale)); } } // paintComponent /** * number of the clique being drawn. Used for selecting the color of the * clique */ int m_nClique = 1; /* * draws cliques in junction tree. */ void viewCliques(Graphics g, JunctionTreeNode node) { int[] nodes = node.m_nNodes; g.setColor(new Color(m_nClique % 7 * 256 / 7, (m_nClique % 2 * 256 / 2), (m_nClique % 3 * 256 / 3))); int dX = m_nPaddedNodeWidth / 2 + m_nClique; int dY = m_nNodeHeight / 2; int nPosX = 0; int nPosY = 0; String sStr = ""; for (int j = 0; j < nodes.length; j++) { nPosX += m_BayesNet.getPositionX(nodes[j]); nPosY += m_BayesNet.getPositionY(nodes[j]); sStr += " " + nodes[j]; for (int k = j + 1; k < nodes.length; k++) { g.drawLine(m_BayesNet.getPositionX(nodes[j]) + dX, m_BayesNet.getPositionY(nodes[j]) + dY, m_BayesNet.getPositionX(nodes[k]) + dX, m_BayesNet.getPositionY(nodes[k]) + dY); } } m_nClique++; nPosX /= nodes.length; nPosY /= nodes.length; g.drawString("Clique " + m_nClique + "(" + sStr + ")", nPosX, nPosY); for (int iChild = 0; iChild < node.m_children.size(); iChild++) { viewCliques(g, (JunctionTreeNode) node.m_children.elementAt(iChild)); } } // viewCliques /* * Draw a node with index iNode on Graphics g at position Drawing mode can * be NORMAL or HIGHLIGHTED. */ protected void drawNode(Graphics g, int iNode, int mode) { int nPosX = m_BayesNet.getPositionX(iNode); int nPosY = m_BayesNet.getPositionY(iNode); g.setColor(this.getBackground().darker().darker()); FontMetrics fm = getFontMetrics(getFont()); if (mode == HIGHLIGHTED) { g.setXORMode(Color.green); // g.setColor(Color.green); } g.fillOval(nPosX + m_nPaddedNodeWidth - m_nNodeWidth - (m_nPaddedNodeWidth - m_nNodeWidth) / 2, nPosY, m_nNodeWidth, m_nNodeHeight); g.setColor(Color.white); if (mode == HIGHLIGHTED) { g.setXORMode(Color.red); } // Draw the node's label if it can fit inside the node's // current width otherwise just display its node nr // if it can fit in node's current width if (fm.stringWidth(m_BayesNet.getNodeName(iNode)) <= m_nNodeWidth) { g.drawString(m_BayesNet.getNodeName(iNode), nPosX + m_nPaddedNodeWidth / 2 - fm.stringWidth(m_BayesNet.getNodeName(iNode)) / 2, nPosY + m_nNodeHeight / 2 + fm.getHeight() / 2 - 2); } else if (fm.stringWidth("" + iNode) <= m_nNodeWidth) { g.drawString("" + iNode, nPosX + m_nPaddedNodeWidth / 2 - fm.stringWidth("" + iNode) / 2, nPosY + m_nNodeHeight / 2 + fm.getHeight() / 2 - 2); } if (mode == HIGHLIGHTED) { g.setXORMode(Color.green); } if (m_bViewMargins) { if (m_BayesNet.getEvidence(iNode) < 0) { g.setColor(new Color(0, 128, 0)); } else { g.setColor(new Color(128, 0, 0)); } double[] P = m_BayesNet.getMargin(iNode); for (int iValue = 0; iValue < P.length; iValue++) { String sP = P[iValue] + ""; if (sP.charAt(0) == '0') { sP = sP.substring(1); } if (sP.length() > 5) { sP = sP.substring(1, 5); } g.fillRect(nPosX + m_nPaddedNodeWidth, nPosY + iValue * 10 + 2, (int) (P[iValue] * 100), 8); g.drawString(m_BayesNet.getNodeValue(iNode, iValue) + " " + sP, nPosX + m_nPaddedNodeWidth + (int) (P[iValue] * 100), nPosY + iValue * 10 + 10); } } if (m_bViewCliques) { return; } g.setColor(Color.black); // Drawing all incoming edges into the node, for (int iParent = 0; iParent < m_BayesNet.getNrOfParents(iNode); iParent++) { int nParent = m_BayesNet.getParent(iNode, iParent); int nPosX1 = nPosX + m_nPaddedNodeWidth / 2; int nPosY1 = nPosY + m_nNodeHeight; int nPosX2 = m_BayesNet.getPositionX(nParent); int nPosY2 = m_BayesNet.getPositionY(nParent); int nPosX2b = nPosX2 + m_nPaddedNodeWidth / 2; int nPosY2b = nPosY2; double phi = Math.atan2((nPosX2b - nPosX1 + 0.0) * m_nNodeHeight, (nPosY2b - nPosY1 + 0.0) * m_nNodeWidth); nPosX1 = (int) (nPosX + m_nPaddedNodeWidth / 2 + Math.sin(phi) * m_nNodeWidth / 2); nPosY1 = (int) (nPosY + m_nNodeHeight / 2 + Math.cos(phi) * m_nNodeHeight / 2); nPosX2b = (int) (nPosX2 + m_nPaddedNodeWidth / 2 - Math.sin(phi) * m_nNodeWidth / 2); nPosY2b = (int) (nPosY2 + m_nNodeHeight / 2 - Math.cos(phi) * m_nNodeHeight / 2); drawArrow(g, nPosX2b, nPosY2b, nPosX1, nPosY1); } if (mode == HIGHLIGHTED) { ArrayList children = m_BayesNet.getChildren(iNode); for (int iChild = 0; iChild < children.size(); iChild++) { int nChild = children.get(iChild); int nPosX1 = nPosX + m_nPaddedNodeWidth / 2; int nPosY1 = nPosY; int nPosX2 = m_BayesNet.getPositionX(nChild); int nPosY2 = m_BayesNet.getPositionY(nChild); int nPosX2b = nPosX2 + m_nPaddedNodeWidth / 2; int nPosY2b = nPosY2 + m_nNodeHeight; double phi = Math.atan2((nPosX2b - nPosX1 + 0.0) * m_nNodeHeight, (nPosY2b - nPosY1 + 0.0) * m_nNodeWidth); nPosX1 = (int) (nPosX + m_nPaddedNodeWidth / 2 + Math.sin(phi) * m_nNodeWidth / 2); nPosY1 = (int) (nPosY + m_nNodeHeight / 2 + Math.cos(phi) * m_nNodeHeight / 2); nPosX2b = (int) (nPosX2 + m_nPaddedNodeWidth / 2 - Math.sin(phi) * m_nNodeWidth / 2); nPosY2b = (int) (nPosY2 + m_nNodeHeight / 2 - Math.cos(phi) * m_nNodeHeight / 2); drawArrow(g, nPosX1, nPosY1, nPosX2b, nPosY2b); } } } // drawNode /** * This method draws an arrow on a line from (x1,y1) to (x2,y2). The arrow * head is seated on (x2,y2) and is in the direction of the line. If the * arrow is needed to be drawn in the opposite direction then simply swap * the order of (x1, y1) and (x2, y2) when calling this function. */ protected void drawArrow(Graphics g, int nPosX1, int nPosY1, int nPosX2, int nPosY2) { g.drawLine(nPosX1, nPosY1, nPosX2, nPosY2); if (nPosX1 == nPosX2) { if (nPosY1 < nPosY2) { g.drawLine(nPosX2, nPosY2, nPosX2 + 4, nPosY2 - 8); g.drawLine(nPosX2, nPosY2, nPosX2 - 4, nPosY2 - 8); } else { g.drawLine(nPosX2, nPosY2, nPosX2 + 4, nPosY2 + 8); g.drawLine(nPosX2, nPosY2, nPosX2 - 4, nPosY2 + 8); } } else { // theta=line's angle from base, beta=angle of arrow's side from // line double hyp = 0, base = 0, perp = 0, theta, beta; int nPosX3 = 0, nPosY3 = 0; if (nPosX2 < nPosX1) { base = nPosX1 - nPosX2; hyp = Math.sqrt((nPosX2 - nPosX1) * (nPosX2 - nPosX1) + (nPosY2 - nPosY1) * (nPosY2 - nPosY1)); theta = Math.acos(base / hyp); } else { // x1>x2 as we already checked x1==x2 before base = nPosX1 - nPosX2; hyp = Math.sqrt((nPosX2 - nPosX1) * (nPosX2 - nPosX1) + (nPosY2 - nPosY1) * (nPosY2 - nPosY1)); theta = Math.acos(base / hyp); } beta = 30 * Math.PI / 180; hyp = 8; base = Math.cos(theta - beta) * hyp; perp = Math.sin(theta - beta) * hyp; nPosX3 = (int) (nPosX2 + base); if (nPosY1 < nPosY2) { nPosY3 = (int) (nPosY2 - perp); } else { nPosY3 = (int) (nPosY2 + perp); } g.drawLine(nPosX2, nPosY2, nPosX3, nPosY3); base = Math.cos(theta + beta) * hyp; perp = Math.sin(theta + beta) * hyp; nPosX3 = (int) (nPosX2 + base); if (nPosY1 < nPosY2) { nPosY3 = (int) (nPosY2 - perp); } else { nPosY3 = (int) (nPosY2 + perp); } g.drawLine(nPosX2, nPosY2, nPosX3, nPosY3); } } // drawArrow /** * This method highlights a given node and all its incoming and outgoing * arcs */ public void highLight(int iNode) { Graphics2D g = (Graphics2D) this.getGraphics(); RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); rh.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED); g.setRenderingHints(rh); g.setPaintMode(); g.scale(m_fScale, m_fScale); drawNode(g, iNode, HIGHLIGHTED); } // highlight /** * implementation of Printable, used for printing * * @see Printable */ @Override public int print(Graphics g, PageFormat pageFormat, int pageIndex) { if (pageIndex > 0) { return (NO_SUCH_PAGE); } else { Graphics2D g2d = (Graphics2D) g; g2d.translate(pageFormat.getImageableX(), pageFormat.getImageableY()); double fHeight = pageFormat.getImageableHeight(); double fWidth = pageFormat.getImageableWidth(); int xMax = 1; int yMax = 1; for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) { if (xMax < m_BayesNet.getPositionX(iNode)) { xMax = m_BayesNet.getPositionX(iNode); } if (yMax < m_BayesNet.getPositionY(iNode)) { yMax = m_BayesNet.getPositionY(iNode); } } double fCurrentScale = m_fScale; xMax += m_nPaddedNodeWidth + 100; if (fWidth / xMax < fHeight / yMax) { m_fScale = fWidth / xMax; } else { m_fScale = fHeight / yMax; } // Turn off double buffering paint(g2d); m_fScale = fCurrentScale; // Turn double buffering back on return (PAGE_EXISTS); } } // print } // class GraphPanel /** * Table Model for the Table for editing CPTs */ private class GraphVisualizerTableModel extends AbstractTableModel { /** for serialization */ private static final long serialVersionUID = -4789813491347366596L; /** labels for the columns */ final String[] m_sColumnNames; /** probability table data **/ final double[][] m_fProbs; public GraphVisualizerTableModel(int iNode) { double[][] probs = m_BayesNet.getDistribution(iNode); m_fProbs = new double[probs.length][probs[0].length]; for (int i = 0; i < probs.length; i++) { for (int j = 0; j < probs[0].length; j++) { m_fProbs[i][j] = probs[i][j]; } } m_sColumnNames = m_BayesNet.getValues(iNode); } // c'tor /** * method that generates random CPTs */ public void randomize() { int nProbs = m_fProbs[0].length; Random random = new Random(); for (int i = 0; i < m_fProbs.length; i++) { // get random nrs for (int j = 0; j < nProbs - 1; j++) { m_fProbs[i][j] = random.nextDouble(); } // sort for (int j = 0; j < nProbs - 1; j++) { for (int k = j + 1; k < nProbs - 1; k++) { if (m_fProbs[i][j] > m_fProbs[i][k]) { double h = m_fProbs[i][j]; m_fProbs[i][j] = m_fProbs[i][k]; m_fProbs[i][k] = h; } } } double sum = m_fProbs[i][0]; for (int j = 1; j < nProbs - 1; j++) { m_fProbs[i][j] = m_fProbs[i][j] - sum; sum += m_fProbs[i][j]; } m_fProbs[i][nProbs - 1] = 1.0 - sum; } } // randomize public void setData() { } /** return nr of colums */ @Override public int getColumnCount() { return m_sColumnNames.length; } /** return nr of rows */ @Override public int getRowCount() { return m_fProbs.length; } /** * return name of specified colum * * @param iCol index of the column */ @Override public String getColumnName(int iCol) { return m_sColumnNames[iCol]; } /** * return data point * * @param iRow index of row in table * @param iCol index of column in table */ @Override public Object getValueAt(int iRow, int iCol) { return new Double(m_fProbs[iRow][iCol]); } /** * Set data point, assigns value to CPT entry specified by row and column. * The remainder of the CPT is normalized so that the values add up to 1. IF * a value below zero of over 1 is given, no changes take place. * * @param oProb data point * @param iRow index of row in table * @param iCol index of column in table */ @Override public void setValueAt(Object oProb, int iRow, int iCol) { Double fProb = (Double) oProb; if (fProb < 0 || fProb > 1) { return; } m_fProbs[iRow][iCol] = fProb; double sum = 0; for (int i = 0; i < m_fProbs[iRow].length; i++) { sum += m_fProbs[iRow][i]; } if (sum > 1) { // handle overflow int i = m_fProbs[iRow].length - 1; while (sum > 1) { if (i != iCol) { if (m_fProbs[iRow][i] > sum - 1) { m_fProbs[iRow][i] -= sum - 1; sum = 1; } else { sum -= m_fProbs[iRow][i]; m_fProbs[iRow][i] = 0; } } i--; } } else { // handle underflow int i = m_fProbs[iRow].length - 1; while (sum < 1) { if (i != iCol) { m_fProbs[iRow][i] += 1 - sum; sum = 1; } i--; } } validate(); } // setData /* * JTable uses this method to determine the default renderer/ editor for * each cell. */ @Override public Class getColumnClass(int c) { return getValueAt(0, c).getClass(); } /* * Implemented this to make sure the table is uneditable. */ @Override public boolean isCellEditable(int row, int col) { return true; } } // class GraphVisualizerTableModel /** * Listener class for processing mouseClicked */ private class GraphVisualizerMouseListener extends MouseAdapter { /** * A left mouseclick on a node adds node to selection (depending on shift * and ctrl keys). A right mouseclick on a node pops up menu with actions to * be performed on the node. A right mouseclick outside another node pops up * menu. */ @Override public void mouseClicked(MouseEvent me) { int x, y; Rectangle r = new Rectangle(0, 0, (int) (m_nPaddedNodeWidth * m_fScale), (int) (m_nNodeHeight * m_fScale)); x = me.getX(); y = me.getY(); for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) { r.x = (int) (m_BayesNet.getPositionX(iNode) * m_fScale); r.y = (int) (m_BayesNet.getPositionY(iNode) * m_fScale); if (r.contains(x, y)) { m_nCurrentNode = iNode; if (me.getButton() == MouseEvent.BUTTON3) { handleRightNodeClick(me); } if (me.getButton() == MouseEvent.BUTTON1) { if ((me.getModifiersEx() & MouseEvent.CTRL_DOWN_MASK) != 0) { m_Selection.toggleSelection(m_nCurrentNode); } else if ((me.getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) != 0) { m_Selection.addToSelection(m_nCurrentNode); } else { m_Selection.clear(); m_Selection.addToSelection(m_nCurrentNode); } repaint(); } return; } } if (me.getButton() == MouseEvent.BUTTON3) { handleRightClick(me, (int) (x / m_fScale), (int) (y / m_fScale)); } } // mouseClicked /* * update selection (non-Javadoc) * * @see * java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent) */ @Override public void mouseReleased(MouseEvent me) { if (m_nSelectedRect != null) { if ((me.getModifiersEx() & MouseEvent.CTRL_DOWN_MASK) != 0) { m_Selection.toggleSelection(m_nSelectedRect); } else if ((me.getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) != 0) { m_Selection.addToSelection(m_nSelectedRect); } else { m_Selection.clear(); m_Selection.addToSelection(m_nSelectedRect); } m_nSelectedRect = null; repaint(); } } // mouseReleased /** position clicked on */ int m_nPosX = 0, m_nPosY = 0; /* * pop up menu with actions that apply in general or to selection (if any * exists) */ void handleRightClick(MouseEvent me, int nPosX, int nPosY) { ActionListener act = new ActionListener() { @Override public void actionPerformed(ActionEvent ae) { if (ae.getActionCommand().equals("Add node")) { a_addnode.addNode(m_nPosX, m_nPosY); return; } repaint(); } }; JPopupMenu popupMenu = new JPopupMenu("Choose a value"); JMenuItem addNodeItem = new JMenuItem("Add node"); addNodeItem.addActionListener(act); popupMenu.add(addNodeItem); ArrayList selected = m_Selection.getSelected(); JMenu addArcMenu = new JMenu("Add parent"); popupMenu.add(addArcMenu); if (selected.size() == 0) { addArcMenu.setEnabled(false); } else { int nNodes = m_BayesNet.getNrOfNodes(); boolean[] isNotAllowedAsParent = new boolean[nNodes]; // prevent it being a parent of itself for (int iNode = 0; iNode < selected.size(); iNode++) { isNotAllowedAsParent[selected.get(iNode)] = true; } // prevent a descendant being a parent, since it introduces cycles for (int i = 0; i < nNodes; i++) { for (int iNode = 0; iNode < nNodes; iNode++) { for (int iParent = 0; iParent < m_BayesNet.getNrOfParents(iNode); iParent++) { if (isNotAllowedAsParent[m_BayesNet.getParent(iNode, iParent)]) { isNotAllowedAsParent[iNode] = true; } } } } // prevent nodes that are already a parent for (int iNode = 0; iNode < selected.size(); iNode++) { int nNode = selected.get(iNode); for (int iParent = 0; iParent < m_BayesNet.getNrOfParents(nNode); iParent++) { isNotAllowedAsParent[m_BayesNet.getParent(nNode, iParent)] = true; } } ActionListener addParentAction = new ActionListener() { @Override public void actionPerformed(ActionEvent ae) { try { m_BayesNet.addArc(ae.getActionCommand(), m_Selection.getSelected()); m_jStatusBar.setText(m_BayesNet.lastActionMsg()); updateStatus(); } catch (Exception e) { e.printStackTrace(); } } }; // count nr of remaining candidates int nCandidates = 0; for (int i = 0; i < nNodes; i++) { if (!isNotAllowedAsParent[i]) { JMenuItem item = new JMenuItem(m_BayesNet.getNodeName(i)); item.addActionListener(addParentAction); addArcMenu.add(item); nCandidates++; } } if (nCandidates == 0) { addArcMenu.setEnabled(false); } } m_nPosX = nPosX; m_nPosY = nPosY; popupMenu.setLocation(me.getX(), me.getY()); popupMenu.show(m_GraphPanel, me.getX(), me.getY()); } // handleRightClick /* * pop up menu with actions that apply to node that was clicked on */ void handleRightNodeClick(MouseEvent me) { m_Selection.clear(); repaint(); ActionListener renameValueAction = new ActionListener() { @Override public void actionPerformed(ActionEvent ae) { renameValue(m_nCurrentNode, ae.getActionCommand()); } }; ActionListener delValueAction = new ActionListener() { @Override public void actionPerformed(ActionEvent ae) { delValue(m_nCurrentNode, ae.getActionCommand()); } }; ActionListener addParentAction = new ActionListener() { @Override public void actionPerformed(ActionEvent ae) { try { m_BayesNet.addArc(ae.getActionCommand(), m_BayesNet.getNodeName(m_nCurrentNode)); m_jStatusBar.setText(m_BayesNet.lastActionMsg()); updateStatus(); } catch (Exception e) { e.printStackTrace(); } } }; ActionListener delParentAction = new ActionListener() { @Override public void actionPerformed(ActionEvent ae) { deleteArc(m_nCurrentNode, ae.getActionCommand()); } }; ActionListener delChildAction = new ActionListener() { @Override public void actionPerformed(ActionEvent ae) { deleteArc(ae.getActionCommand(), m_nCurrentNode); } }; ActionListener setAvidenceAction = new ActionListener() { @Override public void actionPerformed(ActionEvent ae) { try { String[] outcomes = m_BayesNet.getValues(m_nCurrentNode); int iValue = 0; while (iValue < outcomes.length && !outcomes[iValue].equals(ae.getActionCommand())) { iValue++; } if (iValue == outcomes.length) { iValue = -1; } if (iValue < outcomes.length) { m_jStatusBar.setText("Set evidence for " + m_BayesNet.getNodeName(m_nCurrentNode)); if (m_BayesNet.getEvidence(m_nCurrentNode) < 0 && iValue >= 0) { m_BayesNet.setEvidence(m_nCurrentNode, iValue); m_marginCalculatorWithEvidence.setEvidence(m_nCurrentNode, iValue); } else { m_BayesNet.setEvidence(m_nCurrentNode, iValue); SerializedObject so = new SerializedObject(m_marginCalculator); m_marginCalculatorWithEvidence = (MarginCalculator) so .getObject(); for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) { if (m_BayesNet.getEvidence(iNode) >= 0) { m_marginCalculatorWithEvidence.setEvidence(iNode, m_BayesNet.getEvidence(iNode)); } } } for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) { m_BayesNet.setMargin(iNode, m_marginCalculatorWithEvidence.getMargin(iNode)); } } } catch (Exception e) { e.printStackTrace(); } repaint(); } }; ActionListener act = new ActionListener() { @Override public void actionPerformed(ActionEvent ae) { if (ae.getActionCommand().equals("Rename")) { renameNode(m_nCurrentNode); return; } if (ae.getActionCommand().equals("Add parent")) { addArcInto(m_nCurrentNode); return; } if (ae.getActionCommand().equals("Add value")) { addValue(); return; } if (ae.getActionCommand().equals("Delete node")) { deleteNode(m_nCurrentNode); return; } if (ae.getActionCommand().equals("Edit CPT")) { editCPT(m_nCurrentNode); return; } repaint(); } }; try { JPopupMenu popupMenu = new JPopupMenu("Choose a value"); JMenu setEvidenceMenu = new JMenu("Set evidence"); String[] outcomes = m_BayesNet.getValues(m_nCurrentNode); for (String outcome : outcomes) { JMenuItem item = new JMenuItem(outcome); item.addActionListener(setAvidenceAction); setEvidenceMenu.add(item); } setEvidenceMenu.addSeparator(); JMenuItem item = new JMenuItem("Clear"); item.addActionListener(setAvidenceAction); setEvidenceMenu.add(item); popupMenu.add(setEvidenceMenu); setEvidenceMenu.setEnabled(m_bViewMargins); popupMenu.addSeparator(); JMenuItem renameItem = new JMenuItem("Rename"); renameItem.addActionListener(act); popupMenu.add(renameItem); JMenuItem delNodeItem = new JMenuItem("Delete node"); delNodeItem.addActionListener(act); popupMenu.add(delNodeItem); JMenuItem editCPTItem = new JMenuItem("Edit CPT"); editCPTItem.addActionListener(act); popupMenu.add(editCPTItem); popupMenu.addSeparator(); JMenu addArcMenu = new JMenu("Add parent"); popupMenu.add(addArcMenu); int nNodes = m_BayesNet.getNrOfNodes(); boolean[] isNotAllowedAsParent = new boolean[nNodes]; // prevent it being a parent of itself isNotAllowedAsParent[m_nCurrentNode] = true; // prevent a descendant being a parent, since it introduces cycles for (int i = 0; i < nNodes; i++) { for (int iNode = 0; iNode < nNodes; iNode++) { for (int iParent = 0; iParent < m_BayesNet.getNrOfParents(iNode); iParent++) { if (isNotAllowedAsParent[m_BayesNet.getParent(iNode, iParent)]) { isNotAllowedAsParent[iNode] = true; } } } } // prevent nodes that are already a parent for (int iParent = 0; iParent < m_BayesNet .getNrOfParents(m_nCurrentNode); iParent++) { isNotAllowedAsParent[m_BayesNet.getParent(m_nCurrentNode, iParent)] = true; } // count nr of remaining candidates int nCandidates = 0; for (int i = 0; i < nNodes; i++) { if (!isNotAllowedAsParent[i]) { item = new JMenuItem(m_BayesNet.getNodeName(i)); item.addActionListener(addParentAction); addArcMenu.add(item); nCandidates++; } } if (nCandidates == 0) { addArcMenu.setEnabled(false); } JMenu delArcMenu = new JMenu("Delete parent"); popupMenu.add(delArcMenu); if (m_BayesNet.getNrOfParents(m_nCurrentNode) == 0) { delArcMenu.setEnabled(false); } for (int iParent = 0; iParent < m_BayesNet .getNrOfParents(m_nCurrentNode); iParent++) { item = new JMenuItem(m_BayesNet.getNodeName(m_BayesNet.getParent( m_nCurrentNode, iParent))); item.addActionListener(delParentAction); delArcMenu.add(item); } JMenu delChildMenu = new JMenu("Delete child"); popupMenu.add(delChildMenu); ArrayList nChildren = m_BayesNet.getChildren(m_nCurrentNode); if (nChildren.size() == 0) { delChildMenu.setEnabled(false); } for (int iChild = 0; iChild < nChildren.size(); iChild++) { item = new JMenuItem(m_BayesNet.getNodeName(nChildren.get(iChild))); item.addActionListener(delChildAction); delChildMenu.add(item); } popupMenu.addSeparator(); JMenuItem addValueItem = new JMenuItem("Add value"); addValueItem.addActionListener(act); popupMenu.add(addValueItem); JMenu renameValue = new JMenu("Rename value"); popupMenu.add(renameValue); for (String outcome : outcomes) { item = new JMenuItem(outcome); item.addActionListener(renameValueAction); renameValue.add(item); } JMenu delValue = new JMenu("Delete value"); popupMenu.add(delValue); if (m_BayesNet.getCardinality(m_nCurrentNode) <= 2) { delValue.setEnabled(false); } for (String outcome : outcomes) { JMenuItem delValueItem = new JMenuItem(outcome); delValueItem.addActionListener(delValueAction); delValue.add(delValueItem); } popupMenu.setLocation(me.getX(), me.getY()); popupMenu.show(m_GraphPanel, me.getX(), me.getY()); } catch (Exception e) { e.printStackTrace(); } } // handleRightNodeClick } // class GraphVisualizerMouseListener /** * private class for handling mouseMoved events to highlight nodes if the the * mouse is moved on one, move it around or move selection around */ private class GraphVisualizerMouseMotionListener extends MouseMotionAdapter { /* last node moved over. Used for turning highlight on and off */ int m_nLastNode = -1; /* current mouse position clicked */ int m_nPosX, m_nPosY; /* * identify the node under the mouse * * @returns node index of node under mouse, or -1 if there is no such node */ int getGraphNode(MouseEvent me) { m_nPosX = m_nPosY = 0; Rectangle r = new Rectangle(0, 0, (int) (m_nPaddedNodeWidth * m_fScale), (int) (m_nNodeHeight * m_fScale)); m_nPosX += me.getX(); m_nPosY += me.getY(); for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) { r.x = (int) (m_BayesNet.getPositionX(iNode) * m_fScale); r.y = (int) (m_BayesNet.getPositionY(iNode) * m_fScale); if (r.contains(m_nPosX, m_nPosY)) { return iNode; } } return -1; } // getGraphNode /* * handle mouse dragging event (non-Javadoc) * * @see * java.awt.event.MouseMotionListener#mouseDragged(java.awt.event.MouseEvent * ) */ @Override public void mouseDragged(MouseEvent me) { if (m_nSelectedRect != null) { m_nSelectedRect.width = me.getPoint().x - m_nSelectedRect.x; m_nSelectedRect.height = me.getPoint().y - m_nSelectedRect.y; repaint(); return; } int iNode = getGraphNode(me); if (iNode >= 0) { if (m_Selection.getSelected().size() > 0) { if (m_Selection.getSelected().contains(iNode)) { m_BayesNet.setPosition(iNode, (int) ((m_nPosX / m_fScale - m_nPaddedNodeWidth / 2)), (int) ((m_nPosY / m_fScale - m_nNodeHeight / 2)), m_Selection.getSelected()); } else { m_Selection.clear(); m_BayesNet.setPosition(iNode, (int) ((m_nPosX / m_fScale - m_nPaddedNodeWidth / 2)), (int) ((m_nPosY / m_fScale - m_nNodeHeight / 2))); } repaint(); } else { m_BayesNet.setPosition(iNode, (int) ((m_nPosX / m_fScale - m_nPaddedNodeWidth / 2)), (int) ((m_nPosY / m_fScale - m_nNodeHeight / 2))); } m_jStatusBar.setText(m_BayesNet.lastActionMsg()); a_undo.setEnabled(true); a_redo.setEnabled(false); m_GraphPanel.highLight(iNode); } if (iNode < 0) { if (m_nLastNode >= 0) { m_GraphPanel.repaint(); m_nLastNode = -1; } else { m_nSelectedRect = new Rectangle(me.getPoint().x, me.getPoint().y, 1, 1); m_GraphPanel.repaint(); } } } // mouseDragged /* * handles mouse move event (non-Javadoc) * * @see * java.awt.event.MouseMotionListener#mouseMoved(java.awt.event.MouseEvent) */ @Override public void mouseMoved(MouseEvent me) { int iNode = getGraphNode(me); if (iNode >= 0) { if (iNode != m_nLastNode) { m_GraphPanel.highLight(iNode); if (m_nLastNode >= 0) { m_GraphPanel.highLight(m_nLastNode); } m_nLastNode = iNode; } } if (iNode < 0 && m_nLastNode >= 0) { m_GraphPanel.repaint(); m_nLastNode = -1; } } // mouseMoved } // class GraphVisualizerMouseMotionListener /* * apply graph layout algorithm to Bayesian network */ void layoutGraph() { if (m_BayesNet.getNrOfNodes() == 0) { return; } try { ArrayList m_nodes = new ArrayList(); ArrayList m_edges = new ArrayList(); BIFParser bp = new BIFParser(m_BayesNet.toXMLBIF03(), m_nodes, m_edges); bp.parse(); updateStatus(); m_layoutEngine = new HierarchicalBCEngine(m_nodes, m_edges, m_nPaddedNodeWidth, m_nNodeHeight); m_layoutEngine.addLayoutCompleteEventListener(this); m_layoutEngine.layoutGraph(); } catch (Exception e) { e.printStackTrace(); } } // layoutGraph /* * Update status of various items that need regular updating such as enabled * status of some menu items, marginal distributions if shown, repainting of * graph. */ void updateStatus() { a_undo.setEnabled(m_BayesNet.canUndo()); a_redo.setEnabled(m_BayesNet.canRedo()); a_datagenerator.setEnabled(m_BayesNet.getNrOfNodes() > 0); if (!m_bViewMargins && !m_bViewCliques) { repaint(); return; } try { m_marginCalculator = new MarginCalculator(); m_marginCalculator.calcMargins(m_BayesNet); SerializedObject so = new SerializedObject(m_marginCalculator); m_marginCalculatorWithEvidence = (MarginCalculator) so.getObject(); for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) { if (m_BayesNet.getEvidence(iNode) >= 0) { m_marginCalculatorWithEvidence.setEvidence(iNode, m_BayesNet.getEvidence(iNode)); } } for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) { m_BayesNet.setMargin(iNode, m_marginCalculatorWithEvidence.getMargin(iNode)); } } catch (Exception e) { e.printStackTrace(); } repaint(); } // updateStatus /* * add arc with node iChild as child. This pops up a selection list with * potential parents for the child. All decendants and current parents are * excluded from the list as is the child node itself. * * @param iChild index of the node for which to add an arc */ void addArcInto(int iChild) { String sChild = m_BayesNet.getNodeName(iChild); try { int nNodes = m_BayesNet.getNrOfNodes(); boolean[] isNotAllowedAsParent = new boolean[nNodes]; // prevent it being a parent of itself isNotAllowedAsParent[iChild] = true; // prevent a descendant being a parent, since it introduces cycles for (int i = 0; i < nNodes; i++) { for (int iNode = 0; iNode < nNodes; iNode++) { for (int iParent = 0; iParent < m_BayesNet.getNrOfParents(iNode); iParent++) { if (isNotAllowedAsParent[m_BayesNet.getParent(iNode, iParent)]) { isNotAllowedAsParent[iNode] = true; } } } } // prevent nodes that are already a parent for (int iParent = 0; iParent < m_BayesNet.getNrOfParents(iChild); iParent++) { isNotAllowedAsParent[m_BayesNet.getParent(iChild, iParent)] = true; } // count nr of remaining candidates int nCandidates = 0; for (int i = 0; i < nNodes; i++) { if (!isNotAllowedAsParent[i]) { nCandidates++; } } if (nCandidates == 0) { JOptionPane.showMessageDialog(null, "No potential parents available for this node (" + sChild + "). Choose another node as child node."); return; } String[] options = new String[nCandidates]; int k = 0; for (int i = 0; i < nNodes; i++) { if (!isNotAllowedAsParent[i]) { options[k++] = m_BayesNet.getNodeName(i); } } String sParent = (String) JOptionPane.showInputDialog(null, "Select parent node for " + sChild, "Nodes", 0, null, options, options[0]); if (sParent == null || sParent.equals("")) { return; } // update all data structures m_BayesNet.addArc(sParent, sChild); m_jStatusBar.setText(m_BayesNet.lastActionMsg()); updateStatus(); } catch (Exception e) { e.printStackTrace(); } } // addArcInto /* * deletes arc from node with name sParent into child with index iChild */ void deleteArc(int iChild, String sParent) { try { m_BayesNet.deleteArc(m_BayesNet.getNode(sParent), iChild); m_jStatusBar.setText(m_BayesNet.lastActionMsg()); } catch (Exception e) { e.printStackTrace(); } updateStatus(); } // deleteArc /* * deletes arc from node with index iParent into child with name sChild */ void deleteArc(String sChild, int iParent) { try { m_BayesNet.deleteArc(iParent, m_BayesNet.getNode(sChild)); m_jStatusBar.setText(m_BayesNet.lastActionMsg()); } catch (Exception e) { e.printStackTrace(); } updateStatus(); } // deleteArc /* * deletes arc. Pops up list of arcs listed in 'options' as * " -> ". */ void deleteArc(String[] options) { String sResult = (String) JOptionPane.showInputDialog(null, "Select arc to delete", "Arcs", 0, null, options, options[0]); if (sResult != null && !sResult.equals("")) { int nPos = sResult.indexOf(" -> "); String sParent = sResult.substring(0, nPos); String sChild = sResult.substring(nPos + 4); try { m_BayesNet.deleteArc(sParent, sChild); m_jStatusBar.setText(m_BayesNet.lastActionMsg()); } catch (Exception e) { e.printStackTrace(); } updateStatus(); } } // deleteArc /* * Rename node with index nTargetNode. Pops up window that allwos for entering * a new name. */ void renameNode(int nTargetNode) { String sName = JOptionPane.showInputDialog(null, m_BayesNet.getNodeName(nTargetNode), "New name for node", JOptionPane.OK_CANCEL_OPTION); if (sName == null || sName.equals("")) { return; } try { while (m_BayesNet.getNode2(sName) >= 0) { sName = JOptionPane.showInputDialog(null, "Cannot rename to " + sName + ".\nNode with that name already exists."); if (sName == null || sName.equals("")) { return; } } m_BayesNet.setNodeName(nTargetNode, sName); m_jStatusBar.setText(m_BayesNet.lastActionMsg()); } catch (Exception e) { e.printStackTrace(); } repaint(); } // renameNode /* * Rename value with name sValeu of a node with index nTargetNode. Pops up * window that allows entering a new name. */ void renameValue(int nTargetNode, String sValue) { String sNewValue = JOptionPane.showInputDialog(null, "New name for value " + sValue, "Node " + m_BayesNet.getNodeName(nTargetNode), JOptionPane.OK_CANCEL_OPTION); if (sNewValue == null || sNewValue.equals("")) { return; } m_BayesNet.renameNodeValue(nTargetNode, sValue, sNewValue); m_jStatusBar.setText(m_BayesNet.lastActionMsg()); a_undo.setEnabled(true); a_redo.setEnabled(false); repaint(); } // renameValue /* delete a single node with index iNode */ void deleteNode(int iNode) { try { m_BayesNet.deleteNode(iNode); m_jStatusBar.setText(m_BayesNet.lastActionMsg()); } catch (Exception e) { e.printStackTrace(); } updateStatus(); } // deleteNode /* * Add a value to currently selected node. Shows window that allows to enter * the name of the value. */ void addValue() { // GraphNode n = (GraphNode) m_nodes.elementAt(m_nCurrentNode); String sValue = "Value" + (m_BayesNet.getCardinality(m_nCurrentNode) + 1); String sNewValue = JOptionPane.showInputDialog(null, "New value " + sValue, "Node " + m_BayesNet.getNodeName(m_nCurrentNode), JOptionPane.OK_CANCEL_OPTION); if (sNewValue == null || sNewValue.equals("")) { return; } try { m_BayesNet.addNodeValue(m_nCurrentNode, sNewValue); m_jStatusBar.setText(m_BayesNet.lastActionMsg()); // n.outcomes = m_BayesNet.getValues(m_nCurrentNode); // for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) { // n = (GraphNode) m_nodes.elementAt(iNode); // n.probs = m_BayesNet.getDistribution(iNode); // } } catch (Exception e) { e.printStackTrace(); } updateStatus(); } // addValue /* * remove value with name sValue from the node with index nTargetNode */ void delValue(int nTargetNode, String sValue) { try { m_BayesNet.delNodeValue(nTargetNode, sValue); m_jStatusBar.setText(m_BayesNet.lastActionMsg()); } catch (Exception e) { e.printStackTrace(); } updateStatus(); } // delValue /* * Edits CPT of node with index nTargetNode. Pops up table with probability * table that the user can change or just view. */ void editCPT(int nTargetNode) { m_nCurrentNode = nTargetNode; final GraphVisualizerTableModel tm = new GraphVisualizerTableModel( nTargetNode); JTable jTblProbs = new JTable(tm); JScrollPane js = new JScrollPane(jTblProbs); int nParents = m_BayesNet.getNrOfParents(nTargetNode); if (nParents > 0) { GridBagConstraints gbc = new GridBagConstraints(); JPanel jPlRowHeader = new JPanel(new GridBagLayout()); // indices of the parent nodes in the Vector int[] idx = new int[nParents]; // max length of values of each parent int[] lengths = new int[nParents]; // Adding labels for rows gbc.anchor = GridBagConstraints.NORTHWEST; gbc.fill = GridBagConstraints.HORIZONTAL; gbc.insets = new Insets(0, 1, 0, 0); int addNum = 0, temp = 0; boolean dark = false; while (true) { gbc.gridwidth = 1; for (int k = 0; k < nParents; k++) { int iParent2 = m_BayesNet.getParent(nTargetNode, k); JLabel lb = new JLabel(m_BayesNet.getValueName(iParent2, idx[k])); lb.setFont(new Font("Dialog", Font.PLAIN, 12)); lb.setOpaque(true); lb.setBorder(BorderFactory.createEmptyBorder(1, 2, 1, 1)); lb.setHorizontalAlignment(JLabel.CENTER); if (dark) { lb.setBackground(lb.getBackground().darker()); lb.setForeground(Color.white); } else { lb.setForeground(Color.black); } temp = lb.getPreferredSize().width; lb.setPreferredSize(new Dimension(temp, jTblProbs.getRowHeight())); if (lengths[k] < temp) { lengths[k] = temp; } temp = 0; if (k == nParents - 1) { gbc.gridwidth = GridBagConstraints.REMAINDER; dark = (dark == true) ? false : true; } jPlRowHeader.add(lb, gbc); addNum++; } for (int k = nParents - 1; k >= 0; k--) { int iParent2 = m_BayesNet.getParent(m_nCurrentNode, k); if (idx[k] == m_BayesNet.getCardinality(iParent2) - 1 && k != 0) { idx[k] = 0; continue; } else { idx[k]++; break; } } int iParent2 = m_BayesNet.getParent(m_nCurrentNode, 0); if (idx[0] == m_BayesNet.getCardinality(iParent2)) { JLabel lb = (JLabel) jPlRowHeader.getComponent(addNum - 1); jPlRowHeader.remove(addNum - 1); lb.setPreferredSize(new Dimension(lb.getPreferredSize().width, jTblProbs.getRowHeight())); gbc.gridwidth = GridBagConstraints.REMAINDER; gbc.weighty = 1; jPlRowHeader.add(lb, gbc); gbc.weighty = 0; break; } } gbc.gridwidth = 1; // The following panel contains the names of the // parents // and is displayed above the row names to identify // which value belongs to which parent JPanel jPlRowNames = new JPanel(new GridBagLayout()); for (int j = 0; j < nParents; j++) { JLabel lb2; JLabel lb1 = new JLabel(m_BayesNet.getNodeName(m_BayesNet.getParent( nTargetNode, j))); lb1.setBorder(BorderFactory.createEmptyBorder(1, 2, 1, 1)); Dimension tempd = lb1.getPreferredSize(); if (tempd.width < lengths[j]) { lb1.setPreferredSize(new Dimension(lengths[j], tempd.height)); lb1.setHorizontalAlignment(JLabel.CENTER); lb1.setMinimumSize(new Dimension(lengths[j], tempd.height)); } else if (tempd.width > lengths[j]) { lb2 = (JLabel) jPlRowHeader.getComponent(j); lb2.setPreferredSize(new Dimension(tempd.width, lb2 .getPreferredSize().height)); } jPlRowNames.add(lb1, gbc); } js.setRowHeaderView(jPlRowHeader); js.setCorner(JScrollPane.UPPER_LEFT_CORNER, jPlRowNames); } final JDialog dlg = new JDialog((Frame) GUI.this.getTopLevelAncestor(), "Probability Distribution Table For " + m_BayesNet.getNodeName(nTargetNode), true); dlg.setSize(500, 400); dlg.setLocation(GUI.this.getLocation().x + GUI.this.getWidth() / 2 - 250, GUI.this.getLocation().y + GUI.this.getHeight() / 2 - 200); dlg.getContentPane().setLayout(new BorderLayout()); dlg.getContentPane().add(js, BorderLayout.CENTER); JButton jBtRandomize = new JButton("Randomize"); jBtRandomize.setMnemonic('R'); jBtRandomize.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent ae) { tm.randomize(); dlg.repaint(); } }); JButton jBtOk = new JButton("Ok"); jBtOk.setMnemonic('O'); jBtOk.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent ae) { tm.setData(); try { m_BayesNet.setDistribution(m_nCurrentNode, tm.m_fProbs); m_jStatusBar.setText(m_BayesNet.lastActionMsg()); updateStatus(); } catch (Exception e) { e.printStackTrace(); } dlg.setVisible(false); } }); JButton jBtCancel = new JButton("Cancel"); jBtCancel.setMnemonic('C'); jBtCancel.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent ae) { dlg.setVisible(false); } }); Container c = new Container(); c.setLayout(new GridBagLayout()); c.add(jBtRandomize); c.add(jBtOk); c.add(jBtCancel); dlg.getContentPane().add(c, BorderLayout.SOUTH); dlg.setVisible(true); } // editCPT /** * Main method. Builds up menus and reads from file if one is specified. */ public static void main(String[] args) { weka.core.logging.Logger.log(weka.core.logging.Logger.Level.INFO, "Logging started"); LookAndFeel.setLookAndFeel(); JFrame jf = new JFrame("Bayes Network Editor"); final GUI g = new GUI(); JMenuBar menuBar = g.getMenuBar(); if (args.length > 0) { try { g.readBIFFromFile(args[0]); } catch (IOException ex) { ex.printStackTrace(); } catch (BIFFormatException bf) { bf.printStackTrace(); System.exit(-1); } } jf.setJMenuBar(menuBar); jf.getContentPane().add(g); jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); jf.setSize(800, 600); jf.setVisible(true); g.m_Selection.updateGUI(); GenericObjectEditor.registerEditors(); } // main } // end of class




© 2015 - 2024 Weber Informatics LLC | Privacy Policy