View Javadoc

1   /*
2    *  UnBBayes
3    *  Copyright (C) 2002, 2009 Universidade de Brasilia - http://www.unb.br
4    *
5    *  This file is part of UnBBayes.
6    *
7    *  UnBBayes is free software: you can redistribute it and/or modify
8    *  it under the terms of the GNU General Public License as published by
9    *  the Free Software Foundation, either version 3 of the License, or
10   *  (at your option) any later version.
11   *
12   *  UnBBayes is distributed in the hope that it will be useful,
13   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   *  GNU General Public License for more details.
16   *
17   *  You should have received a copy of the GNU General Public License
18   *  along with UnBBayes.  If not, see <http://www.gnu.org/licenses/>.
19   *
20   */
21  package unbbayes.evaluation.gui;
22  
23  import java.awt.Dimension;
24  import java.awt.FlowLayout;
25  import java.awt.GridLayout;
26  import java.awt.event.ActionListener;
27  import java.awt.event.MouseEvent;
28  import java.util.ArrayList;
29  import java.util.HashMap;
30  import java.util.List;
31  import java.util.Map;
32  import java.util.Set;
33  
34  import javax.swing.AbstractListModel;
35  import javax.swing.BoxLayout;
36  import javax.swing.DefaultCellEditor;
37  import javax.swing.JButton;
38  import javax.swing.JComboBox;
39  import javax.swing.JFormattedTextField;
40  import javax.swing.JFrame;
41  import javax.swing.JLabel;
42  import javax.swing.JList;
43  import javax.swing.JPanel;
44  import javax.swing.JProgressBar;
45  import javax.swing.JScrollPane;
46  import javax.swing.JTable;
47  import javax.swing.ListModel;
48  import javax.swing.table.AbstractTableModel;
49  import javax.swing.table.JTableHeader;
50  import javax.swing.table.TableModel;
51  
52  import unbbayes.evaluation.EvidenceEvaluation;
53  import unbbayes.evaluation.exception.EvaluationException;
54  import unbbayes.gui.LongTaskProgressBar;
55  import unbbayes.gui.table.EachRowEditor;
56  import unbbayes.gui.table.NumberEditor;
57  import unbbayes.gui.table.NumberRenderer;
58  import unbbayes.gui.table.PercentRenderer;
59  import unbbayes.gui.table.RadioButtonCellEditor;
60  import unbbayes.gui.table.RadioButtonCellRenderer;
61  import unbbayes.gui.table.RowHeaderRenderer;
62  
63  import com.ibm.icu.text.NumberFormat;
64  
65  public class EvaluationPane extends JPanel {
66  
67  	private static final long serialVersionUID = 1L;
68  	
69  	public JPanel mainPane;
70  	
71  	private JTable inputTable;
72  	private JLabel sampleSizeLabel;
73  	private JFormattedTextField sampleSizeTextField;
74  	private JLabel errorLabel;
75  	private JFormattedTextField errorTextField;
76  	private JButton runButton;
77  	private JButton cancelButton;
78  	private JProgressBar progressBar;
79  	
80  	private JTable outputTable;
81  	private JScrollPane cmOutputTableScroll;
82  	private JLabel pccLabel;
83  	private JFormattedTextField pccValueTextField;
84  	private JPanel sampleSizePane;
85  	private JPanel progressBarPane;
86  
87  	public EvaluationPane() {
88  		super(new GridLayout(1,1));
89  		
90  		mainPane = new JPanel();
91  		mainPane.setLayout(new BoxLayout(mainPane, BoxLayout.Y_AXIS));
92  		
93  		setUpInputPane();
94  		
95  		setUpOutputPane();
96  		
97  		add(mainPane);
98  
99  	}
100 	
101 	private void setUpInputPane() {
102 		setUpInputTable();
103 		
104 		setUpSampleSizePane();
105 		
106 		setUpProgressBarPane();
107 	}
108 	
109 	private void setUpInputTable() {
110 		TableModel dm = new EvaluationInputTableModel();
111 		
112 		inputTable = new JTable(dm);
113 		inputTable.setPreferredScrollableViewportSize(new Dimension(500, 80));
114 
115 		// Create the scroll pane and add the table to it.
116 		JScrollPane scrollPane = new JScrollPane(inputTable);
117 
118 		inputTable.getColumn("Target").setCellRenderer(
119 				new RadioButtonCellRenderer());
120 		inputTable.getColumn("Target").setCellEditor(
121 				new RadioButtonCellEditor());
122 
123 		inputTable.setDefaultEditor(Float.class, new NumberEditor());
124 		inputTable.setDefaultRenderer(Float.class, new NumberRenderer());
125 		
126 		mainPane.add(scrollPane);
127 	}
128 	
129 	private void setUpSampleSizePane() {
130 		sampleSizePane = new JPanel(new FlowLayout(FlowLayout.LEFT));
131 		
132 		sampleSizeLabel = new JLabel("Sample Size:");
133 		sampleSizeTextField = new JFormattedTextField(NumberFormat.getIntegerInstance());
134 		sampleSizeTextField.setColumns(10);
135 		
136 		errorLabel = new JLabel("Error:");
137 		NumberFormat numberFormat = NumberFormat.getScientificInstance();
138 		numberFormat.setMaximumFractionDigits(3);
139 		errorTextField = new JFormattedTextField(numberFormat);
140 		errorTextField.setColumns(5);
141 		errorTextField.setEditable(false);
142 		
143 		sampleSizePane.add(sampleSizeLabel);
144 		sampleSizePane.add(sampleSizeTextField);
145 		sampleSizePane.add(errorLabel);
146 		sampleSizePane.add(errorTextField);
147 		
148 		mainPane.add(sampleSizePane);
149 	}
150 	
151 	private void setUpProgressBarPane() {
152 		progressBarPane = new JPanel(new FlowLayout(FlowLayout.LEFT));
153 		
154 		runButton = new JButton("Run");
155 		
156 		cancelButton = new JButton("Cancel");
157 		cancelButton.setEnabled(false);
158 		
159 		progressBar = new JProgressBar();
160 		progressBar.setValue(0);
161 		progressBar.setStringPainted(true);
162 		
163 		progressBarPane.add(runButton);
164 		progressBarPane.add(cancelButton);
165 		progressBarPane.add(progressBar);
166 		
167 		mainPane.add(progressBarPane);
168 	}
169 	
170 	public void updateProgressBarPane(LongTaskProgressBar ltProgressBar) {
171 		runButton.setEnabled(false);
172 		
173 		progressBarPane.remove(cancelButton);
174 		cancelButton = ltProgressBar.getCancelButton();
175 		progressBarPane.add(cancelButton);
176 		cancelButton.setEnabled(true);
177 		
178 		progressBarPane.remove(progressBar);
179 		progressBar = ltProgressBar.getProgressBar();
180 		progressBar.setBorder(null);
181 		progressBarPane.add(progressBar);
182 	}
183 	
184 	public void resetProgressBar() {
185 		runButton.setEnabled(true);
186 		cancelButton.setEnabled(false);
187 		progressBar.setValue(0);
188 	}
189 
190 	private void setUpOutputPane() {
191 		setUpPccPane();
192 		
193 		setUpOutputTable();
194 		
195 		setUpOutputCM(new Float[0][0], new String[0]);
196 	}
197 	
198 	private void setUpPccPane() {
199 		JPanel pccPane = new JPanel(new FlowLayout(FlowLayout.LEFT));
200 		
201 		pccLabel = new JLabel("Probability of Correct Classification:");
202 		NumberFormat numberFormat = NumberFormat.getPercentInstance();
203 		numberFormat.setMinimumIntegerDigits(1);
204 		numberFormat.setMinimumFractionDigits(2);
205 		numberFormat.setMaximumFractionDigits(2);
206 		pccValueTextField = new JFormattedTextField(numberFormat);
207 		pccValueTextField.setColumns(10);
208 		pccValueTextField.setEditable(false);
209 		
210 		pccPane.add(pccLabel);
211 		pccPane.add(pccValueTextField);
212 		
213 		mainPane.add(pccPane);
214 	}
215 	
216 	private void setUpOutputTable() {
217 		TableModel dm = new EvaluationOutputTableModel();
218 
219 		outputTable = new JTable(dm) {
220 			private static final long serialVersionUID = 1L;
221 
222 		    //Implement table header tool tips.
223 		    protected JTableHeader createDefaultTableHeader() {
224 		        return new JTableHeader(columnModel) {
225 					private static final long serialVersionUID = 1L;
226 
227 					public String getToolTipText(MouseEvent e) {
228 		                java.awt.Point p = e.getPoint();
229 		                int index = columnModel.getColumnIndexAtX(p.x);
230 		                int realIndex = 
231 		                        columnModel.getColumn(index).getModelIndex();
232 		                return ((EvaluationOutputTableModel)getModel()).getColumnToolTip(realIndex);
233 		            }
234 		        };
235 		    }
236 		};
237 		
238 		outputTable.setPreferredScrollableViewportSize(new Dimension(500, 70));
239 
240 		// Create the scroll pane and add the table to it.
241 		JScrollPane scrollPane = new JScrollPane(outputTable);
242 		
243 		outputTable.setDefaultRenderer(Float.class, new PercentRenderer());
244 		outputTable.getColumn("Cost").setCellRenderer(
245 				new NumberRenderer());
246 		outputTable.getColumn("Marginal Cost").setCellRenderer(
247 				new NumberRenderer(2, 6));
248 		
249 		mainPane.add(scrollPane);
250 	}
251 	
252 	public void setUpOutputCM(Float[][] rowData, final String[] header) {
253 		
254 		if (cmOutputTableScroll != null) {
255 			mainPane.remove(cmOutputTableScroll);
256 		}
257 		
258 		ListModel lm = new AbstractListModel() {
259 			private static final long serialVersionUID = 1L;
260 			
261 			String headers[] = header;
262 
263 			public int getSize() {
264 				return headers.length;
265 			}
266 
267 			public Object getElementAt(int index) {
268 				return headers[index];
269 			}
270 		};
271 
272 		CMOutputTableModel tm = new CMOutputTableModel(rowData, header);
273 		JTable table = new JTable(tm);
274 
275 		JList rowHeader = new JList(lm);
276 		rowHeader.setCellRenderer(new RowHeaderRenderer(table));
277 		
278 		cmOutputTableScroll = new JScrollPane(table);
279 		cmOutputTableScroll.setRowHeaderView(rowHeader);
280 		
281 		table.setPreferredScrollableViewportSize(new Dimension(500, 70));
282 		
283 		table.setDefaultRenderer(Float.class, new PercentRenderer());
284 		
285 		mainPane.add(cmOutputTableScroll);
286 	}
287 
288 	public void setRunButtonActionListener(ActionListener actionListener) {
289 		runButton.addActionListener(actionListener);
290 		
291 	}
292 	
293 	public Integer getSampleSizeValue() {
294 		return ((Long)sampleSizeTextField.getValue()).intValue();
295 	}
296 	
297 	public List<String> getTargetNodeNameList() {
298 		List<String> targetNodeNameList = new ArrayList<String>();
299 		
300 		TableModel dm = inputTable.getModel();
301 		
302 		for (int i = 0; i < dm.getRowCount(); i++) {
303 			if ((Boolean)dm.getValueAt(i, 1)) {
304 				targetNodeNameList.add((String)dm.getValueAt(i, 0));
305 			}
306 		}
307 		
308 		return targetNodeNameList;
309 	}
310 
311 	public List<String> getEvidenceNodeNameList() {
312 		List<String> evidenceNodeNameList = new ArrayList<String>();
313 		
314 		TableModel dm = inputTable.getModel();
315 		
316 		for (int i = 0; i < dm.getRowCount(); i++) {
317 			if ((Boolean)dm.getValueAt(i, 2)) {
318 				evidenceNodeNameList.add((String)dm.getValueAt(i, 0));
319 			}
320 		}
321 		
322 		return evidenceNodeNameList;
323 	}
324 	
325 	public float getCost(String nodeName) {
326 		
327 		TableModel dm = inputTable.getModel();
328 		
329 		for (int i = 0; i < dm.getRowCount(); i++) {
330 			// If it is an evidence node and it has the name given
331 			if ((Boolean)dm.getValueAt(i, 2) && dm.getValueAt(i, 0).equals(nodeName)) {
332 				// Return its cost
333 				return (Float)dm.getValueAt(i, 3);
334 			}
335 		}
336 		
337 		return 0.0f;
338 	}
339 	
340 	public Map<String, String> getNodeConditionMap() {
341 		Map<String, String> nodeConditionMap = new HashMap<String, String>();
342 		
343 		TableModel dm = inputTable.getModel();
344 		
345 		for (int i = 0; i < dm.getRowCount(); i++) {
346 			String finding = (String)dm.getValueAt(i, 4);
347 			if (!finding.equals("")) {
348 				nodeConditionMap.put((String)dm.getValueAt(i, 0), finding);
349 			}
350 		}
351 		
352 		return nodeConditionMap;
353 	}
354 	
355 	public void setPccValue(float pccValue) {
356 		pccValueTextField.setValue(pccValue);
357 	}
358 	
359 	public void setErrorValue(float errorValue) {
360 		errorTextField.setValue(errorValue);
361 	}
362 	
363 	private class CMOutputTableModel extends AbstractTableModel {
364 
365 		private static final long serialVersionUID = 1L;
366 
367 		private String[] columnNames;
368 		
369 		private Object[][] data;
370 		
371 		public CMOutputTableModel(Float[][] data, String[] columnNames) {
372 			super();
373 			this.data = data;
374 			this.columnNames = columnNames;
375 		}
376 
377 		public int getColumnCount() {
378 			return columnNames.length;
379 		}
380 
381 		public int getRowCount() {
382 			return data.length;
383 		}
384 
385 		public String getColumnName(int col) {
386 			return columnNames[col];
387 		}
388 		
389 		public Object getValueAt(int row, int col) {
390 			return data[row][col];
391 		}
392 
393 		/**
394 		 * JTable uses this method to determine the default renderer/ editor for
395 		 * each cell. If we didn't implement this method, then the last column
396 		 * would contain text ("true"/"false"), rather than a check box.
397 		 */
398 		@SuppressWarnings("unchecked")
399 		public Class getColumnClass(int c) {
400 			return Float.class;
401 		}
402 
403 		public boolean isCellEditable(int row, int col) {
404 			return false;
405 		}
406 
407 	}
408 	
409 	public void addOutputValues(List<EvidenceEvaluation> evidenceEvaluationList) throws EvaluationException {
410 		((EvaluationOutputTableModel)outputTable.getModel()).addValues(evidenceEvaluationList);
411 		outputTable.revalidate();
412 	}
413 
414 	private class EvaluationOutputTableModel extends AbstractTableModel {
415 
416 		private static final long serialVersionUID = 1L;
417 
418 		private String[] columnToolTips = { "Node", "Marginal PCC (%)",
419 				"Marginal Improvement (%)", "Individual PCC (%)", "Cost",
420 				"Individual Marginal Cost" };
421 		
422 		private String[] columnNames = { "Node", "MPCC (%)",
423 				"MI (%)", "IPCC (%)", "Cost",
424 				"Marginal Cost" };
425 		
426 		private Object[][] data  = new Object[0][6];
427 		
428 		public EvaluationOutputTableModel() {
429 			super();
430 			
431 		}
432 
433 		public void addValues(List<EvidenceEvaluation> evidenceEvaluationList) throws EvaluationException {
434 			data = new Object[evidenceEvaluationList.size()][getColumnCount()];
435 			EvidenceEvaluation evidenceEvaluation;
436 			for (int i = 0; i < evidenceEvaluationList.size(); i++) {
437 				evidenceEvaluation = evidenceEvaluationList.get(i);
438 				data[i][0] = evidenceEvaluation.getName();
439 				data[i][1] = evidenceEvaluation.getMarginalPCC();
440 				data[i][2] = evidenceEvaluation.getMarginalImprovement();
441 				data[i][3] = evidenceEvaluation.getIndividualPCC();
442 				data[i][4] = evidenceEvaluation.getCost();
443 				data[i][5] = evidenceEvaluation.getMarginalCost();
444 			}
445 		}
446 
447 		public int getColumnCount() {
448 			return columnNames.length;
449 		}
450 
451 		public int getRowCount() {
452 			return data.length;
453 		}
454 
455 		public String getColumnName(int col) {
456 			return columnNames[col];
457 		}
458 		
459 		public String getColumnToolTip(int col) {
460 			return columnToolTips[col];
461 		}
462 
463 		public Object getValueAt(int row, int col) {
464 			return data[row][col];
465 		}
466 
467 		/**
468 		 * JTable uses this method to determine the default renderer/ editor for
469 		 * each cell. If we didn't implement this method, then the last column
470 		 * would contain text ("true"/"false"), rather than a check box.
471 		 */
472 		@SuppressWarnings("unchecked")
473 		public Class getColumnClass(int c) {
474 			if (c == 0) {
475 				return String.class;
476 			} else {
477 				return Float.class;
478 			}
479 		}
480 
481 		public boolean isCellEditable(int row, int col) {
482 			return false;
483 		}
484 
485 	}
486 
487 	public void addInputValues(Map<String, List<String>> nodeConditionMap) {
488 		Set<String> nodeNameList = nodeConditionMap.keySet();
489 		((EvaluationInputTableModel)inputTable.getModel()).addValues(nodeNameList);
490 		
491 		int i = 0;
492 		JComboBox comboBox;
493 		EachRowEditor rowEditor = new EachRowEditor(inputTable);
494 		inputTable.getColumn("Condition").setCellEditor(rowEditor);
495 		for (String node : nodeNameList) {
496 			comboBox = getComboBox(nodeConditionMap.get(node));
497 			rowEditor.setEditorAt(i++, new DefaultCellEditor(comboBox));
498 		}
499 		inputTable.revalidate();
500 	}
501 	
502 	private JComboBox getComboBox(List<String> findingList) {
503 		JComboBox combo = new JComboBox();
504 		combo.addItem("");
505 		for (String finding : findingList) {
506 			combo.addItem(finding);
507 		}
508 		return combo;
509 	}
510 	
511 	private class EvaluationInputTableModel extends AbstractTableModel {
512 
513 		private static final long serialVersionUID = 1L;
514 
515 		private String[] columnNames = { "Node", "Target", "Evidence", "Cost", "Condition" };
516 
517 		private Object[][] data = new Object[0][5];
518 		
519 		public void addValues(Set<String> nodeNameSet) {
520 			data = new Object[nodeNameSet.size()][getColumnCount()];
521 			int i = 0;
522 			for (String node : nodeNameSet) {
523 				data[i][0] = node;
524 				data[i][1] = Boolean.FALSE;
525 				data[i][2] = Boolean.FALSE;
526 				data[i][3] = new Float(100.00);
527 				data[i++][4] = "";
528 			}
529 		}
530 		
531 		public int getColumnCount() {
532 			return columnNames.length;
533 		}
534 
535 		public int getRowCount() {
536 			return data.length;
537 		}
538 
539 		public String getColumnName(int col) {
540 			return columnNames[col];
541 		}
542 
543 		public Object getValueAt(int row, int col) {
544 			return data[row][col];
545 		}
546 
547 		@SuppressWarnings("unchecked")
548 		public Class getColumnClass(int c) {
549 			if (c == 0 || c == 4) {
550 				return String.class;
551 			} else if (c == 1 || c == 2) {
552 				return Boolean.class;
553 			} else {
554 				return Float.class;
555 			}
556 		}
557 
558 		public boolean isCellEditable(int row, int col) {
559 			// Just the node name is not editable
560 			return (col != 0);
561 		}
562 
563 		public void setValueAt(Object value, int row, int col) {
564 			data[row][col] = value;
565 			fireTableCellUpdated(row, col);
566 
567 			// If selected as target
568 			if (col == 1 && value.equals(Boolean.TRUE)) {
569 				// Make sure there is just one target selected
570 				resetNonSelectedValues(row, col);
571 				// Make sure it is not selected as evidence
572 				setValueAt(Boolean.FALSE, row, 2);
573 				fireTableCellUpdated(row, 2);
574 				// Make sure it does not have finding
575 				setValueAt("", row, 4);
576 				fireTableCellUpdated(row, 4);
577 				// If selected as evidence
578 			} else if (col == 2 && value.equals(Boolean.TRUE)) {
579 				// Make sure it is not selected as target
580 				setValueAt(Boolean.FALSE, row, 1);
581 				fireTableCellUpdated(row, 1);
582 				// Make sure it does not have finding
583 				setValueAt("", row, 4);
584 				fireTableCellUpdated(row, 4);
585 			} else if (col == 4 && !value.equals("")) {
586 				// Make sure it is not selected as target
587 				setValueAt(Boolean.FALSE, row, 1);
588 				fireTableCellUpdated(row, 1);
589 				// Make sure it is not selected as evidence
590 				setValueAt(Boolean.FALSE, row, 2);
591 				fireTableCellUpdated(row, 2);
592 			}
593 		}
594 
595 		/**
596 		 * This will give the behavior of a ButtonGroup
597 		 */
598 		private void resetNonSelectedValues(int newRow, int col) {
599 			for (int row = 0; row < data.length; row++) {
600 				if (getValueAt(row, col).equals(Boolean.TRUE) && row != newRow) {
601 					setValueAt(Boolean.FALSE, row, col);
602 					fireTableCellUpdated(row, col);
603 				}
604 			}
605 		}
606 
607 	}
608 
609 	/**
610 	 * Create the GUI and show it. For thread safety, this method should be
611 	 * invoked from the event-dispatching thread.
612 	 */
613 	private static void createAndShowGUI() {
614 		// Create and set up the window.
615 		JFrame frame = new JFrame("TableRenderDemo");
616 		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
617 
618 		// Create and set up the content pane.
619 		EvaluationPane newContentPane = new EvaluationPane();
620 		newContentPane.setOpaque(true); // content panes must be opaque
621 		frame.setContentPane(newContentPane);
622 
623 		// Display the window.
624 		frame.pack();
625 		frame.setVisible(true);
626 	}
627 
628 	public static void main(String[] args) {
629 
630 		// Schedule a job for the event-dispatching thread:
631 		// creating and showing this application's GUI.
632 		javax.swing.SwingUtilities.invokeLater(new Runnable() {
633 			public void run() {
634 				createAndShowGUI();
635 			}
636 		});
637 
638 		// JFrame frame = new JFrame("Evaluation Test");
639 		//
640 		// EvaluationPane pane = new EvaluationPane();
641 		//
642 		// List<String> nodeNameList = new ArrayList<String>();
643 		// nodeNameList.add("Node1");
644 		// nodeNameList.add("Node2");
645 		// nodeNameList.add("Node3");
646 		// nodeNameList.add("Node4");
647 		//
648 		// frame.add(pane);
649 		// frame.setSize(800, 450);
650 		// frame.setVisible(true);
651 		// frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
652 
653 	}
654 
655 }