package xbneditor;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;
import java.awt.geom.*;
import javax.swing.*;
//import ncsa.d2k.modules.ls.*;
/**
* The main GUI, this class contains all of the GUI features. It
* calls all of the functions that will end up manipulation the
* nodes and their relations to each other. The entire GUI is
* divided into three sections: the toolbars, the editarea, and
* the graphical area.
*
* @author Laura Kruse
* @version v1.5
*/
public class XBN {
private JFrame frame = new JFrame("XBN Editor");
private JPanel panel = new JPanel();
private JPanel paneltop = new JPanel();
private JMenuBar menubar = new JMenuBar();
private JMenu file = new JMenu("File");
private JMenu tools = new JMenu("Tools");
private JMenuItem nnew = new JMenuItem("New");
private JMenuItem open = new JMenuItem("Open");
private JMenuItem save = new JMenuItem("Save");
private JMenuItem saveas = new JMenuItem("Save As");
private JMenuItem close = new JMenuItem("Exit");
private JMenuItem exit = new JMenuItem("Exit All");
private JMenu edit = new JMenu("Edit Node");
private JMenuItem addstate = new JMenuItem("Insert State");
private JMenuItem deletestate = new JMenuItem("Delete State");
private JMenuItem undo = new JMenuItem("Undo");
private JMenuItem redo = new JMenuItem("Redo");
private JMenu filetype = new JMenu("File Type");
// These are public for Haipengs code apparently.
public JCheckBoxMenuItem xml = new JCheckBoxMenuItem(".xml");
public JCheckBoxMenuItem bif = new JCheckBoxMenuItem(".bif");
public JCheckBoxMenuItem dsl = new JCheckBoxMenuItem(".dsl");
private JMenu loss = new JMenu("Measure Loss");
private JMenuItem grapherrors = new JMenuItem("Graph Errors");
private JMenuItem inference = new JMenuItem("Inference");
private JFileChooser fchoose = new JFileChooser();
private JToolBar toolbar = new JToolBar();
public static EditNode editnode;
public GraphicalPanel draw;
private int startx;
private int starty;
private GridBagLayout gridbag;
private GridBagConstraints gbc;
private Item cb = null;
private JDialog dialog;
private String state;
private Vector vundo;
private Vector vredo;
private boolean standalone;
public FileIO fileio = new FileIO();
public String filename = null;
public String format = "xml";
private int[][] goldstandard;
private int[][] bnstandard;
private final int width = 80;
private final int height = 50;
private final int boardwidth = 2000;
private final int boardheight = 1000;
/**
* Creates a new instance of the GUI and shows it to the
* user. This is also responsible for placing all of
* the widgets into the starting layout.
*/
public XBN() {
gridbag = new GridBagLayout();
gbc = new GridBagConstraints();
frame.setContentPane(new JScrollPane(panel));
panel.setLayout(gridbag);
// setting up the menu bar
frame.setJMenuBar(menubar);
menubar.add(file);
file.setMnemonic('F');
file.add(nnew);
nnew.setMnemonic('N');
nnew.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N,Event.CTRL_MASK));
nnew.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
nnew();
}
});
file.add(open);
open.setMnemonic('O');
open.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O,Event.CTRL_MASK));
open.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
open();
}
});
file.addSeparator();
file.add(save);
save.setMnemonic('S');
save.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S,Event.CTRL_MASK));
save.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
save();
}
});
file.add(saveas);
saveas.setMnemonic('A');
saveas.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A,Event.CTRL_MASK));
saveas.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
saveas();
}
});
file.addSeparator();
file.add(close);
close.setMnemonic('X');
close.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X, Event.CTRL_MASK));
close.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
close();
}
});
file.add(exit);
exit.setMnemonic('X');
exit.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X,Event.ALT_MASK));
exit.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
menubar.add(tools);
tools.setMnemonic('T');
tools.add(edit);
edit.setMnemonic('E');
edit.add(addstate);
addstate.setMnemonic('I');
addstate.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_I,Event.CTRL_MASK));
addstate.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if(cb!=null) redrawEditNodeArea(cb,1,false);
}
});
edit.add(deletestate);
deletestate.setMnemonic('D');
deletestate.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_D,Event.CTRL_MASK));
deletestate.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if(cb!=null) deleteDialog((ChanceBlock) cb.getItem());
}
});
tools.addSeparator();
tools.add(undo);
undo.setMnemonic('U');
undo.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_U, Event.CTRL_MASK));
undo.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
undo();
}
});
undo.setEnabled(false);
vundo = new Vector();
tools.add(redo);
redo.setMnemonic('R');
redo.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, Event.CTRL_MASK));
redo.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
redo();
}
});
redo.setEnabled(false);
vredo = new Vector();
tools.addSeparator();
tools.add(filetype);
filetype.setMnemonic('Y');
filetype.add(xml);
xml.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
format = "xml";
xml.setSelected(true);
bif.setSelected(false);
dsl.setSelected(false);
}
});
xml.setSelected(true);
filetype.add(bif);
bif.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
format = "bif";
xml.setSelected(false);
bif.setSelected(true);
dsl.setSelected(false);
}
});
filetype.add(dsl);
dsl.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
format = "dsl";
xml.setSelected(false);
bif.setSelected(false);
dsl.setSelected(true);
}
});
menubar.add(loss);
loss.setMnemonic('L');
loss.add(grapherrors);
grapherrors.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
getStandards();
}
});
loss.add(inference);
inference.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
inference();
}
});
// setting up the toolbar, this will eveuntally contain
// only picutre icons representing what to do
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.anchor = GridBagConstraints.NORTHWEST;
gridbag.setConstraints(toolbar,gbc);
panel.add(toolbar);
toolbar.setFloatable(false);
Action pointer = new AbstractAction(null,new ImageIcon("modules/ncsa/d2k/modules/k2/editor/images/node.jpg")) {
public void actionPerformed(ActionEvent e) {
draw.changeDrawType(0);
}
};
toolbar.add(pointer);
toolbar.addSeparator();
Action dline = new AbstractAction(null,new ImageIcon("modules/ncsa/d2k/modules/k2/editor/images/arrow1.jpg")) {
public void actionPerformed(ActionEvent e) {
draw.changeDrawType(1);
}
};
toolbar.add(dline);
toolbar.addSeparator();
Action del = new AbstractAction("X",null) {
public void actionPerformed(ActionEvent e) {
// delete the highlited node
draw.changeDrawType(2);
}
};
toolbar.add(del);
toolbar.addSeparator();
// setting stuff for the info area
editnode = new EditNode(new Item(), -1);
gbc.gridwidth = GridBagConstraints.REMAINDER;
gridbag.setConstraints(editnode,gbc);
// This is just a label that doesn't really need to be here
//label = new JLabel("Drag the Objects around within the area");
//gbc.gridwidth = GridBagConstraints.REMAINDER;
//gridbag.setConstraints(label,gbc);
// adding the drawing area for the BN
draw = new GraphicalPanel(this);
gbc.ipady = boardheight;
gbc.ipadx = boardwidth;
gbc.weightx = 1;
gbc.weighty = 1;
gbc.fill = GridBagConstraints.BOTH;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gridbag.setConstraints(draw,gbc);
panel.add(draw);
//panel.add(label);
// makes the little X in the corner work
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
close();
}
});
frame.pack();
frame.setSize(600, 400);
frame.show();
}
/**
* This method allows for a new Bayesian Network to be created
* over the existing Bayesian Network. If the previous network
* has not been saved the user will be asked if they want to save
* the network or not. Then the drawing area will be cleared
* and a new network can be created.
*/
private void nnew() {
if(draw.isSaved()) {
draw.setBayNet(new LinkedList());
} else {
save();
// possible add something here in case the user
// decides that they really don't want to make a
// new file
draw.setBayNet(new LinkedList());
}
}
/**
* This is a file menu option and will allow the user to
* load a previously generate BN file.
*/
private void open() {
File file;
fchoose.setDialogTitle("Load File");
fchoose.showOpenDialog(frame);
file = fchoose.getSelectedFile();
if(file != null) {
//System.out.println(file.getAbsolutePath());
filename = file.getAbsolutePath();
if(file.getName()!=null) {
LinkedList llist = fileio.load(filename);
format = fileio.getFileType();
if(format=="xml") {
xml.setSelected(true);
bif.setSelected(false);
dsl.setSelected(false);
} else if(format=="bif") {
xml.setSelected(true);
bif.setSelected(false);
dsl.setSelected(false);
} else {
xml.setSelected(false);
bif.setSelected(false);
dsl.setSelected(true);
}
draw.setBayNet(llist);
}
}
}
/**
* Allows the user to save the current BN they are working with.
*/
private synchronized void save() {
if(filename==null) {
saveas();
} else {
fileio.setFileType(format);
fileio.save(filename,draw.getBayNet());
draw.setSaved(true);
}
}
/**
* Allows the user to save the current BN with a specific name.
*/
private synchronized void saveas() {
File file;
fchoose.setDialogTitle("Save File As");
if(fchoose.showSaveDialog(frame) == JFileChooser.APPROVE_OPTION) {
try {
file = fchoose.getSelectedFile();
filename = file.getAbsolutePath();
fileio.setFileType(format);
fileio.save(filename,draw.getBayNet());
draw.setSaved(true);
} catch(NullPointerException npe) {
// the user didn't specify a file, tell them
}
}
}
/**
* Puts a copy of the Network onto the undo Vector so that it can
* be undone if the user so chooses.
*/
public void addundo() {
vundo.add(copy());
undo.setEnabled(true);
}
/**
* Puts a copy of the Network onto the redo Vector so that it can
* be redone if the user so chooses.
*/
private void addredo() {
vredo.add(copy());
redo.setEnabled(true);
}
/**
* This internally makes a copy of the entire Network. This is a
* very slow process. The Java version of clone can not be done in
* this case because it only does a high level copy of the structure
* which results in each node being put on the new list as a refrenec
* to the old copy. This is not the behavior that we want. Instead
* this traverses the entire structure and creates a new copy down
* to the last detail. The larger the networks become the more time
* this will take.
*/
private LinkedList copy() {
LinkedList old;
LinkedList copy;
Item current;
old = draw.getBayNet();
copy = new LinkedList();
for(int i=0;i<old.size();i++) {
current = (Item) old.get(i);
LinkedList states;
String blockName;
int xcoord;
int ycoord;
ChanceBlock cb;
Ellipse2D.Double e2d;
Item item;
states = ((ChanceBlock) current.getItem()).getAttributeNames();
blockName = current.getItem().getBlockName();
xcoord = (new Double(current.getItem().getx())).intValue();
ycoord = (new Double(current.getItem().gety())).intValue();
cb = new ChanceBlock(blockName, xcoord, ycoord, false);
for(int s=0;s<states.size();s++) {
cb.add((String) states.get(s));
}
e2d = new Ellipse2D.Double(xcoord - width / 2, ycoord - height / 2, width, height);
item = new Item(cb, e2d);
copy.add(item);
}
// Looking at the old list and are going to set the
// parents and children accordingly.
for(int i=0;i<old.size();i++) {
current = (Item) old.get(i);
Item child;
String parentName;
String childName;
int rows;
int columns;
int loc;
int size;
loc = 0;
size = copy.size();
childName = current.getItem().getBlockName();
child = (Item) copy.getFirst();
while(!child.getItem().getBlockName().equals(childName) && loc<size) {
child = (Item) copy.get(loc);
loc++;
}
rows = ((ChanceBlock) current.getItem()).getRows();
columns = ((ChanceBlock) current.getItem()).getColumns();
// Need to find the Node in the new list to make
// the changes too.
for(int p=0;p<current.numParents();p++) {
Item parent;
parentName = current.getParent(p).getItem().getBlockName();
loc = 0;
size = copy.size();
parent = (Item) copy.getFirst();
while(!parent.getItem().getBlockName().equals(parentName) && loc<size) {
parent = (Item) copy.get(loc);
loc++;
}
parent.setChild(child);
child.setParent(parent);
}
// Copy the probabilities over
for(int j=0;j<columns;j++) {
for(int k=0;k<rows;k++) {
((ChanceBlock) child.getItem()).setValue(((ChanceBlock) current.getItem()).getValue(k, j), k, j);
}
}
}
return copy;
}
/**
* This takes the most recent copy of the Network on the undo Vector
* and sets it as the current Network.
*/
private void undo() {
LinkedList object;
int size;
size = vundo.size();
object = (LinkedList) vundo.remove(size - 1);
addredo();
draw.setBayNet(object);
if(size == 1) {
// this means we undid the last item
undo.setEnabled(false);
}
}
/**
* This takes the newest copy of the Network off the redo Vector
* and sets it to be the current Network.
*/
private void redo() {
LinkedList object;
int size;
size = vredo.size();
object = (LinkedList) vredo.remove(size - 1);
addundo();
draw.setBayNet(object);
if(size==1) {
// there are no more item left in the vector
redo.setEnabled(false);
}
}
/**
* This closes the viewer. If the current network has not been
* saved then it will prompt the user.
*/
private synchronized void close() {
SaveDialog sdialog;
int ret;
if(draw.isSaved()) {
frame.dispose();
} else {
sdialog = new SaveDialog(frame, filename, true);
sdialog.show();
ret = sdialog.value();
switch(ret) {
case(1): {
save();
frame.dispose();
break;
}
case(2): {
frame.dispose();
break;
}
default: {
// do nothing
}
}
}
System.exit(0);
}
/**
* Calculates and redraws the treetable.
*/
private void redrawTreeTable() {
editnode.check();
redraw();
}
/**
* Creates and draws the edit area corresponding to a particular
* node.
*
* @param choice 0 = delete state 1 = add state
*/
public void redrawEditNodeArea(Item tmp, int choice, boolean check) {
// need to add methods for different things
cb = tmp;
if(check) editnode.check();
editnode = new EditNode(tmp, choice);
redraw();
}
/**
* Redraws the entire window and updates the components
* accordingly.
*/
private void redraw() {
panel.removeAll();
gridbag = new GridBagLayout();
gbc = new GridBagConstraints();
panel.setLayout(gridbag);
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.anchor = GridBagConstraints.NORTHWEST;
gridbag.setConstraints(toolbar,gbc);
panel.add(toolbar);
gbc.gridwidth = GridBagConstraints.REMAINDER;
gridbag.setConstraints(editnode,gbc);
panel.add(editnode);
//gbc.gridwidth = GridBagConstraints.REMAINDER;
//gridbag.setConstraints(label,gbc);
gbc.ipady = boardheight;
gbc.ipadx = boardwidth;
gbc.fill = GridBagConstraints.BOTH;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gridbag.setConstraints(draw,gbc);
panel.add(draw);
//panel.add(label);
frame.validate();
}
/**
* Make a dialog appear for this particular node asking which
* state needs to be deleted.
*
* @param block - the nature node that is having a state removed
*/
public void deleteDialog(ChanceBlock block) {
GridBagLayout layout = new GridBagLayout();
GridBagConstraints constraints = new GridBagConstraints();
final LinkedList states = block.getAttributeNames();
dialog = new JDialog(frame,"Delete States",true);
dialog.getContentPane().setLayout(layout);
JLabel label = new JLabel("Choose a state to delete.");
final JComboBox combobox = new JComboBox();
combobox.addItem("<none>");
for(int i=0;i<block.numAttributes();i++) {
combobox.addItem(states.get(i));
}
combobox.setEnabled(true);
JButton done = new JButton("Done");
done.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
state=combobox.getSelectedItem().toString();
dialog.dispose();
if(!state.equals("<done>")) {
//System.out.println(states.indexOf(state));
((ChanceBlock) cb.getItem()).delete(((ChanceBlock) cb.getItem()).getAttributeNames().indexOf(state));
redrawEditNodeArea(cb,0,false);
}
}
});
constraints.gridwidth = GridBagConstraints.RELATIVE;
constraints.anchor = GridBagConstraints.NORTH;
layout.setConstraints(label,constraints);
constraints.gridwidth = GridBagConstraints.REMAINDER;
layout.setConstraints(done,constraints);
constraints.insets=new Insets(0,50,30,0);
constraints.gridwidth = GridBagConstraints.REMAINDER;
layout.setConstraints(combobox,constraints);
dialog.getContentPane().add(label);
dialog.getContentPane().add(combobox);
dialog.getContentPane().add(done);
dialog.pack();
dialog.show();
}
/**
* This function should actually be pushing the data somewhere in
* d2k, but I don't know how to do it based on a user click.
*/
private void getStandards() {
GraphErrorDialog ged;
CountGraphErrors cge;
File file;
String name;
ged = new GraphErrorDialog(frame, true);
fchoose.setDialogTitle("Compare Gold Standard");
fchoose.showOpenDialog(frame);
file = fchoose.getSelectedFile();
name = file.getAbsolutePath();
if(file.getName() != null) {
cge = new CountGraphErrors(draw.getBayNet(), name);
ged.setAddition(cge.countErrorAddition());
ged.setDeletion(cge.countErrorDeletion());
ged.setReversal(cge.countErrorReversal());
ged.show();
}
}
/**
* This function should actually be pushing the data somewhere in
* d2k, but I don't know how to do it based on a user click.
*/
private void inference() {
/*
FileIO file;
LinkedList bn;
//CreateCliqueTree graph;
CreateCliqueTreeParallel graph;
CliqueProbabilityDriver cpd;
file = new FileIO();
bn = file.load(filename);
// print the starting time
Calendar cal1 = Calendar.getInstance();
Date startTime1 = cal1.getTime();
System.out.println("Start time of the first graph transform step is:"+ startTime1);
//graph = new CreateCliqueTree(bn);
graph = new CreateCliqueTreeParallel(bn);
cpd = new CliqueProbabilityDriver();
//Phase 1 : create the permanent clique tree from the input BN
//graph tranform
graph.moralize();
graph.triangulate();
graph.build();
Calendar cal2 = Calendar.getInstance();
Date endTime1 = cal2.getTime();
System.out.println("End time of the first graph transform step is:"+ endTime1);
//Phase 2 : compute the prob of cliques and query varialbes
// from the input evidnce file and the permanent clique
// tree from phase 1
// message passing
cpd.setRoot((Clique) graph.getCliqueTree());
cpd.doIt();
System.out.println(cpd.report());
Calendar cal3 = Calendar.getInstance();
Date endTime2 = cal3.getTime();
System.out.println("Start time of the first graph transform step is:"+ startTime1);
System.out.println("End time of the first graph transform step is:"+ endTime1);
System.out.println("End time of the second probs computation is:"+endTime2);
// do something to send this to Ben's stuff here
*/
}
}