0001 /*
0002 * Copyright (c) 1995-2010, The University of Sheffield. See the file
0003 * COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
0004 *
0005 * This file is part of GATE (see http://gate.ac.uk/), and is free
0006 * software, licenced under the GNU Library General Public License,
0007 * Version 2, June 1991 (in the distribution as file licence.html,
0008 * and also available at http://gate.ac.uk/gate/licence.html).
0009 *
0010 * AnnotationDiffGUI.java
0011 *
0012 * Valentin Tablan, 24-Jun-2004
0013 *
0014 * $Id: AnnotationDiffGUI.java 13451 2011-02-14 15:39:58Z thomas_heitz $
0015 */
0016
0017 package gate.gui;
0018
0019 import java.awt.*;
0020 import java.awt.event.*;
0021 import java.io.*;
0022 import java.text.NumberFormat;
0023 import java.util.*;
0024 import java.util.List;
0025 import java.util.Timer;
0026 import javax.swing.*;
0027 import javax.swing.text.BadLocationException;
0028 import javax.swing.event.*;
0029 import javax.swing.table.AbstractTableModel;
0030 import javax.swing.table.DefaultTableCellRenderer;
0031 import gate.*;
0032 import gate.gui.docview.TextualDocumentView;
0033 import gate.gui.docview.AnnotationSetsView;
0034 import gate.swing.XJTable;
0035 import gate.swing.XJFileChooser;
0036 import gate.util.*;
0037
0038 /**
0039 * Compare annotations in two annotation sets in one or two documents.
0040 *
0041 * Display a table with annotations compared side by side.
0042 * Annotations offsets and features can be edited by modifying cells.
0043 * Selected annotations can be copied to another annotation set.
0044 */
0045 public class AnnotationDiffGUI extends JFrame{
0046
0047 public AnnotationDiffGUI(String title){
0048 super(title);
0049 setIconImage(((ImageIcon)MainFrame.getIcon("annotation-diff")).getImage());
0050 MainFrame.getGuiRoots().add(this);
0051 setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
0052 initLocalData();
0053 initGUI();
0054 initListeners();
0055 populateGUI();
0056 }
0057
0058 /**
0059 * Set all the parameters and compute the differences.
0060 *
0061 * @param title title of the frame
0062 * @param keyDocumentName name of the key document
0063 * @param responseDocumentName name of the response document
0064 * @param keyAnnotationSetName key annotation set name, may be null
0065 * @param responseAnnotationSetName response annotation set name, may be null
0066 * @param annotationType annotation type, may be null
0067 * @param featureNames feature name, may be null
0068 */
0069
0070 public AnnotationDiffGUI(String title,
0071 final String keyDocumentName, final String responseDocumentName,
0072 final String keyAnnotationSetName, final String responseAnnotationSetName,
0073 final String annotationType, final Set<String> featureNames){
0074 super(title);
0075 setIconImage(((ImageIcon)MainFrame.getIcon("annotation-diff")).getImage());
0076 MainFrame.getGuiRoots().add(this);
0077 setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
0078 initLocalData();
0079 initGUI();
0080 initListeners();
0081 populateGUI();
0082
0083 // set programmatically the different settings
0084 SwingUtilities.invokeLater(new Runnable(){ public void run() {
0085 keyDocCombo.setSelectedItem(keyDocumentName);
0086 resDocCombo.setSelectedItem(responseDocumentName);
0087 if (keyAnnotationSetName != null) {
0088 keySetCombo.setSelectedItem(keyAnnotationSetName);
0089 }
0090 if (responseAnnotationSetName != null) {
0091 resSetCombo.setSelectedItem(responseAnnotationSetName);
0092 }
0093 if (annotationType != null) {
0094 annTypeCombo.setSelectedItem(annotationType);
0095 }
0096 significantFeatures.clear();
0097 if (featureNames == null || featureNames.isEmpty()) {
0098 noFeaturesBtn.setSelected(true);
0099 } else {
0100 significantFeatures.addAll(featureNames);
0101 someFeaturesBtn.setSelected(true);
0102 }
0103 // compute differences automatically
0104 if (keyAnnotationSetName != null
0105 && responseAnnotationSetName != null
0106 && annotationType != null) {
0107 SwingUtilities.invokeLater(new Runnable() {
0108 public void run() {
0109 // wait some time
0110 Date timeToRun = new Date(System.currentTimeMillis() + 1000);
0111 Timer timer = new Timer("Annotation diff init timer", true);
0112 timer.schedule(new TimerTask() {
0113 public void run() {
0114 diffAction.actionPerformed(
0115 new ActionEvent(this, -1, "corpus quality"));
0116 }
0117 }, timeToRun);
0118 }});
0119 }
0120 }});
0121 }
0122
0123 protected void initLocalData(){
0124 differ = new AnnotationDiffer();
0125 pairings = new ArrayList<AnnotationDiffer.Pairing>();
0126 keyCopyValueRows = new ArrayList<Boolean>();
0127 resCopyValueRows = new ArrayList<Boolean>();
0128 significantFeatures = new HashSet<String>();
0129 keyDoc = null;
0130 resDoc = null;
0131 Component root = SwingUtilities.getRoot(AnnotationDiffGUI.this);
0132 isStandalone = (root instanceof MainFrame);
0133 }
0134
0135 protected void initGUI(){
0136 getContentPane().setLayout(new GridBagLayout());
0137 GridBagConstraints constraints = new GridBagConstraints();
0138 constraints.fill = GridBagConstraints.HORIZONTAL;
0139 constraints.anchor = GridBagConstraints.WEST;
0140 constraints.insets = new Insets(2, 2, 2, 2);
0141 constraints.weightx = 1;
0142
0143 /*******************************************
0144 * First row - Settings and 'Compare' button *
0145 *******************************************/
0146
0147 constraints.gridy = 0;
0148 JLabel keyDocLabel = new JLabel("Key doc:");
0149 keyDocLabel.setToolTipText("Key document");
0150 getContentPane().add(keyDocLabel, constraints);
0151 keyDocCombo = new JComboBox();
0152 keyDocCombo.setPrototypeDisplayValue("long_document_name");
0153 // add the value of the combobox in a tooltip for long value
0154 keyDocCombo.setRenderer(new DefaultListCellRenderer() {
0155 @Override
0156 public Component getListCellRendererComponent(JList list, Object value,
0157 int index, boolean isSelected, boolean cellHasFocus) {
0158 super.getListCellRendererComponent(
0159 list, value, index, isSelected, cellHasFocus);
0160 if (value != null) {
0161 Rectangle textRectangle = new Rectangle(keyDocCombo.getSize().width,
0162 getPreferredSize().height);
0163 String shortText = SwingUtilities.layoutCompoundLabel(this,
0164 getFontMetrics(getFont()), value.toString(), null,
0165 getVerticalAlignment(), getHorizontalAlignment(),
0166 getHorizontalTextPosition(), getVerticalTextPosition(),
0167 textRectangle, new Rectangle(), textRectangle, getIconTextGap());
0168 if (shortText.equals(value)) { // no tooltip
0169 if (index == -1) {
0170 keyDocCombo.setToolTipText(null);
0171 } else {
0172 setToolTipText(null);
0173 }
0174 } else { // add tooltip because text is shortened
0175 if (index == -1) {
0176 keyDocCombo.setToolTipText(value.toString());
0177 } else {
0178 setToolTipText(value.toString());
0179 }
0180 }
0181 }
0182 return this;
0183 }
0184 });
0185 constraints.weightx = 3;
0186 getContentPane().add(keyDocCombo, constraints);
0187 constraints.weightx = 1;
0188 JLabel keySetLabel = new JLabel("Key set:");
0189 keySetLabel.setToolTipText("Key annotation set");
0190 getContentPane().add(keySetLabel, constraints);
0191 keySetCombo = new JComboBox();
0192 keySetCombo.setPrototypeDisplayValue("long_set_name");
0193 getContentPane().add(keySetCombo, constraints);
0194 JLabel typeLabel = new JLabel("Type:");
0195 typeLabel.setToolTipText("Annotation type");
0196 getContentPane().add(typeLabel, constraints);
0197 annTypeCombo = new JComboBox();
0198 annTypeCombo.setPrototypeDisplayValue("very_long_type");
0199 constraints.gridwidth = 3;
0200 getContentPane().add(annTypeCombo, constraints);
0201 constraints.gridwidth = 1;
0202 JLabel weightLabel = new JLabel("Weight");
0203 weightLabel.setToolTipText("F-measure weight");
0204 getContentPane().add(weightLabel, constraints);
0205 diffAction = new DiffAction();
0206 diffAction.setEnabled(false);
0207 doDiffBtn = new JButton(diffAction);
0208 doDiffBtn.setForeground((Color)
0209 UIManager.getDefaults().get("Button.disabledText"));
0210 doDiffBtn.setToolTipText("Choose two annotation sets "
0211 +"that have at least one annotation type in common.");
0212 constraints.gridheight = 2;
0213 getContentPane().add(doDiffBtn, constraints);
0214 constraints.gridheight = 1;
0215
0216 /*************************
0217 * Second row - Settings *
0218 *************************/
0219
0220 constraints.gridy++;
0221 constraints.gridx = 0;
0222 JLabel responseDocLabel = new JLabel("Resp. doc:");
0223 responseDocLabel.setToolTipText("Response document");
0224 getContentPane().add(responseDocLabel, constraints);
0225 constraints.gridx = GridBagConstraints.RELATIVE;
0226 resDocCombo = new JComboBox();
0227 resDocCombo.setPrototypeDisplayValue("long_document_name");
0228 resDocCombo.setRenderer(new DefaultListCellRenderer() {
0229 @Override
0230 public Component getListCellRendererComponent(JList list, Object value,
0231 int index, boolean isSelected, boolean cellHasFocus) {
0232 super.getListCellRendererComponent(
0233 list, value, index, isSelected, cellHasFocus);
0234 if (value != null) {
0235 Rectangle textRectangle = new Rectangle(resDocCombo.getSize().width,
0236 getPreferredSize().height);
0237 String shortText = SwingUtilities.layoutCompoundLabel(this,
0238 getFontMetrics(getFont()), value.toString(), null,
0239 getVerticalAlignment(), getHorizontalAlignment(),
0240 getHorizontalTextPosition(), getVerticalTextPosition(),
0241 textRectangle, new Rectangle(), textRectangle, getIconTextGap());
0242 if (shortText.equals(value)) { // no tooltip
0243 if (index == -1) {
0244 resDocCombo.setToolTipText(null);
0245 } else {
0246 setToolTipText(null);
0247 }
0248 } else { // add tooltip because text is shortened
0249 if (index == -1) {
0250 resDocCombo.setToolTipText(value.toString());
0251 } else {
0252 setToolTipText(value.toString());
0253 }
0254 }
0255 }
0256 return this;
0257 }
0258 });
0259 constraints.weightx = 3;
0260 getContentPane().add(resDocCombo, constraints);
0261 constraints.weightx = 1;
0262 JLabel responseSetLabel = new JLabel("Resp. set:");
0263 responseSetLabel.setToolTipText("Response annotation set");
0264 getContentPane().add(responseSetLabel, constraints);
0265 resSetCombo = new JComboBox();
0266 resSetCombo.setPrototypeDisplayValue("long_set_name");
0267 getContentPane().add(resSetCombo, constraints);
0268 getContentPane().add(new JLabel("Features:"), constraints);
0269 ButtonGroup btnGrp = new ButtonGroup();
0270 allFeaturesBtn = new JRadioButton("all");
0271 allFeaturesBtn.setOpaque(false);
0272 allFeaturesBtn.setMargin(new Insets(0, 0, 0, 1));
0273 allFeaturesBtn.setIconTextGap(1);
0274 btnGrp.add(allFeaturesBtn);
0275 constraints.insets = new Insets(0, 0, 0, 0);
0276 getContentPane().add(allFeaturesBtn, constraints);
0277 someFeaturesBtn = new JRadioButton("some");
0278 someFeaturesBtn.setOpaque(false);
0279 someFeaturesBtn.setMargin(new Insets(0, 0, 0, 1));
0280 someFeaturesBtn.setIconTextGap(1);
0281 btnGrp.add(someFeaturesBtn);
0282 getContentPane().add(someFeaturesBtn, constraints);
0283 noFeaturesBtn = new JRadioButton("none");
0284 noFeaturesBtn.setOpaque(false);
0285 noFeaturesBtn.setMargin(new Insets(0, 0, 0, 1));
0286 noFeaturesBtn.setIconTextGap(1);
0287 btnGrp.add(noFeaturesBtn);
0288 getContentPane().add(noFeaturesBtn, constraints);
0289 constraints.insets = new Insets(2, 2, 2, 2);
0290 noFeaturesBtn.setSelected(true);
0291 weightTxt = new JTextField("1.0");
0292 weightTxt.setToolTipText(
0293 "<html>Relative weight of precision and recall aka beta." +
0294 "<ul><li>1 weights equally precision and recall" +
0295 "<li>0.5 weights precision twice as much as recall" +
0296 "<li>2 weights recall twice as much as precision</ul></html>");
0297 constraints.fill = GridBagConstraints.HORIZONTAL;
0298 getContentPane().add(weightTxt, constraints);
0299 constraints.fill = GridBagConstraints.NONE;
0300
0301 /********************************
0302 * Third row - Comparison table *
0303 ********************************/
0304
0305 constraints.gridy++;
0306 constraints.gridx = 0;
0307 diffTableModel = new DiffTableModel();
0308 diffTable = new XJTable(diffTableModel);
0309 diffTable.setDefaultRenderer(String.class, new DiffTableCellRenderer());
0310 diffTable.setDefaultRenderer(Boolean.class, new DiffTableCellRenderer());
0311 diffTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
0312 diffTable.setComparator(DiffTableModel.COL_MATCH, new Comparator(){
0313 public int compare(Object o1, Object o2){
0314 String label1 = (String)o1;
0315 String label2 = (String)o2;
0316 int match1 = 0;
0317 while(!label1.equals(matchLabel[match1])) match1++;
0318 int match2 = 0;
0319 while(!label2.equals(matchLabel[match2])) match2++;
0320
0321 return match1 - match2;
0322 }
0323 });
0324 diffTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
0325 diffTable.setRowSelectionAllowed(true);
0326 diffTable.setColumnSelectionAllowed(true);
0327 diffTable.setEnableHidingColumns(true);
0328 diffTable.hideColumn(DiffTableModel.COL_KEY_COPY);
0329 diffTable.hideColumn(DiffTableModel.COL_RES_COPY);
0330
0331 Comparator startEndComparator = new Comparator() {
0332 public int compare(Object o1, Object o2) {
0333 String no1 = (String) o1;
0334 String no2 = (String) o2;
0335 if (no1.trim().equals("") && no2.trim().equals("")) {
0336 return 0;
0337 }
0338 else if (no1.trim().equals("")) {
0339 return -1;
0340 }
0341 else if (no2.trim().equals("")) {
0342 return 1;
0343 }
0344 int n1 = Integer.parseInt(no1);
0345 int n2 = Integer.parseInt(no2);
0346 if(n1 == n2)
0347 return 0;
0348 else if(n1 > n2)
0349 return 1;
0350 else
0351 return -1;
0352 }
0353 };
0354
0355 diffTable.setComparator(DiffTableModel.COL_KEY_START, startEndComparator);
0356 diffTable.setComparator(DiffTableModel.COL_KEY_END, startEndComparator);
0357 diffTable.setComparator(DiffTableModel.COL_RES_START, startEndComparator);
0358 diffTable.setComparator(DiffTableModel.COL_RES_END, startEndComparator);
0359
0360 diffTable.setSortable(true);
0361 diffTable.setSortedColumn(DiffTableModel.COL_MATCH);
0362 diffTable.setAscending(true);
0363 scroller = new JScrollPane(diffTable);
0364 constraints.gridwidth = GridBagConstraints.REMAINDER;
0365 constraints.weightx = 1;
0366 constraints.weighty = 1;
0367 constraints.fill = GridBagConstraints.BOTH;
0368 getContentPane().add(scroller, constraints);
0369 constraints.gridwidth = 1;
0370 constraints.weightx = 0;
0371 constraints.weighty = 0;
0372 constraints.fill = GridBagConstraints.NONE;
0373
0374 /*************************************************
0375 * Fourth row - Tabbed panes, status and buttons *
0376 *************************************************/
0377
0378 bottomTabbedPane = new JTabbedPane(
0379 JTabbedPane.BOTTOM, JTabbedPane.WRAP_TAB_LAYOUT);
0380
0381 // statistics pane will be added to the bottom tabbed pane
0382 statisticsPane = new JPanel(new GridBagLayout());
0383 GridBagConstraints constraints2 = new GridBagConstraints();
0384 constraints2.insets = new Insets(2, 2, 2, 2);
0385
0386 // first column
0387 constraints2.gridx = 0;
0388 constraints2.anchor = GridBagConstraints.WEST;
0389 JLabel lbl = new JLabel("Correct:");
0390 lbl.setToolTipText("Correct:");
0391 lbl.setBackground(diffTable.getBackground());
0392 statisticsPane.add(lbl, constraints2);
0393 lbl = new JLabel("Partially correct:");
0394 lbl.setToolTipText("Partially correct:");
0395 lbl.setBackground(PARTIALLY_CORRECT_BG);
0396 lbl.setOpaque(true);
0397 statisticsPane.add(lbl, constraints2);
0398 lbl = new JLabel("Missing:");
0399 lbl.setToolTipText("Missing:");
0400 lbl.setBackground(MISSING_BG);
0401 lbl.setOpaque(true);
0402 statisticsPane.add(lbl, constraints2);
0403 lbl = new JLabel("False positives:");
0404 lbl.setToolTipText("False positives:");
0405 lbl.setBackground(FALSE_POSITIVE_BG);
0406 lbl.setOpaque(true);
0407 statisticsPane.add(lbl, constraints2);
0408
0409 // second column
0410 constraints2.gridx++;
0411 correctLbl = new JLabel("0");
0412 statisticsPane.add(correctLbl, constraints2);
0413 partiallyCorrectLbl = new JLabel("0");
0414 statisticsPane.add(partiallyCorrectLbl, constraints2);
0415 missingLbl = new JLabel("0");
0416 statisticsPane.add(missingLbl, constraints2);
0417 falsePozLbl = new JLabel("0");
0418 statisticsPane.add(falsePozLbl, constraints2);
0419
0420 // third column
0421 constraints2.gridx++;
0422 constraints2.insets = new Insets(4, 30, 4, 4);
0423 statisticsPane.add(Box.createGlue());
0424 lbl = new JLabel("Strict:");
0425 statisticsPane.add(lbl, constraints2);
0426 lbl = new JLabel("Lenient:");
0427 statisticsPane.add(lbl, constraints2);
0428 lbl = new JLabel("Average:");
0429 statisticsPane.add(lbl, constraints2);
0430
0431 // fourth column
0432 constraints2.gridx++;
0433 constraints2.insets = new Insets(4, 4, 4, 4);
0434 lbl = new JLabel("Recall");
0435 statisticsPane.add(lbl, constraints2);
0436 recallStrictLbl = new JLabel("0.00");
0437 statisticsPane.add(recallStrictLbl, constraints2);
0438 recallLenientLbl = new JLabel("0.00");
0439 statisticsPane.add(recallLenientLbl, constraints2);
0440 recallAveLbl = new JLabel("0.00");
0441 statisticsPane.add(recallAveLbl, constraints2);
0442
0443 // fifth column
0444 constraints2.gridx++;
0445 lbl = new JLabel("Precision");
0446 statisticsPane.add(lbl, constraints2);
0447 precisionStrictLbl = new JLabel("0.00");
0448 statisticsPane.add(precisionStrictLbl, constraints2);
0449 precisionLenientLbl = new JLabel("0.00");
0450 statisticsPane.add(precisionLenientLbl, constraints2);
0451 precisionAveLbl = new JLabel("0.00");
0452 statisticsPane.add(precisionAveLbl, constraints2);
0453
0454 // sixth column
0455 constraints2.gridx++;
0456 lbl = new JLabel("F-measure");
0457 lbl.setToolTipText("<html>F-measure =<br>" +
0458 " ((weight² + 1) * precision * recall)<br>" +
0459 " / (weight² * precision + recall))</html>");
0460 statisticsPane.add(lbl, constraints2);
0461 fmeasureStrictLbl = new JLabel("0.00");
0462 statisticsPane.add(fmeasureStrictLbl, constraints2);
0463 fmeasureLenientLbl = new JLabel("0.00");
0464 statisticsPane.add(fmeasureLenientLbl, constraints2);
0465 fmeasureAveLbl = new JLabel("0.00");
0466 statisticsPane.add(fmeasureAveLbl, constraints2);
0467
0468 bottomTabbedPane.add("Statistics", statisticsPane);
0469
0470 // adjudication pane will be added to the bottom tabbed pane
0471 JPanel adjudicationPane = new JPanel(new GridBagLayout());
0472 constraints2 = new GridBagConstraints();
0473 constraints2.insets = new Insets(2, 2, 2, 2);
0474
0475 // first row
0476 constraints2.gridy = 0;
0477 adjudicationPane.add(new JLabel("Target set:"), constraints2);
0478 consensusASTextField = new JTextField("consensus", 15);
0479 consensusASTextField.setToolTipText(
0480 "Annotation set name where to copy the selected annotations");
0481 adjudicationPane.add(consensusASTextField, constraints2);
0482
0483 // second row
0484 constraints2.gridy++;
0485 constraints2.gridx = 0;
0486 copyToTargetSetAction = new CopyToTargetSetAction();
0487 copyToTargetSetAction.setEnabled(false);
0488 copyToConsensusBtn = new JButton(copyToTargetSetAction);
0489 constraints2.gridwidth = 2;
0490 adjudicationPane.add(copyToConsensusBtn, constraints2);
0491
0492 bottomTabbedPane.add("Adjudication", adjudicationPane);
0493
0494 JPanel bottomPanel = new JPanel(new GridBagLayout());
0495 constraints2 = new GridBagConstraints();
0496 constraints2.insets = new Insets(2, 2, 2, 2);
0497
0498 // add the bottom tabbed pane to the bottom panel
0499 constraints2.gridx = 0;
0500 constraints2.gridy = 0;
0501 constraints2.gridheight = 3;
0502 constraints2.anchor = GridBagConstraints.WEST;
0503 bottomPanel.add(bottomTabbedPane, constraints2);
0504 constraints2.gridheight = 1;
0505
0506 // status bar
0507 constraints2.gridx++;
0508 statusLabel = new JLabel();
0509 statusLabel.setBackground(diffTable.getBackground());
0510 constraints2.insets = new Insets(2, 6, 6, 2);
0511 bottomPanel.add(statusLabel, constraints2);
0512
0513 // toolbar
0514 JToolBar toolbar = new JToolBar();
0515 toolbar.setFloatable(false);
0516 if (!isStandalone) {
0517 showDocumentAction = new ShowDocumentAction();
0518 showDocumentAction.setEnabled(false);
0519 showDocumentBtn = new JButton(showDocumentAction);
0520 showDocumentBtn.setToolTipText("Use first the \"Compare\"" +
0521 " button then select an annotation in the table.");
0522 toolbar.add(showDocumentBtn);
0523 }
0524 htmlExportAction = new HTMLExportAction();
0525 htmlExportAction.setEnabled(false);
0526 htmlExportBtn = new JButton(htmlExportAction);
0527 htmlExportBtn.setToolTipText("Use first the \"Compare\" button.");
0528 toolbar.add(htmlExportBtn);
0529 if (!isStandalone) {
0530 toolbar.add(new HelpAction());
0531 }
0532 constraints2.gridy++;
0533 constraints2.fill = GridBagConstraints.NONE;
0534 constraints2.anchor = GridBagConstraints.NORTHWEST;
0535 bottomPanel.add(toolbar, constraints2);
0536
0537 // progress bar
0538 progressBar = new JProgressBar();
0539 progressBar.setMinimumSize(new Dimension(5, 5));
0540 progressBar.setBackground(diffTable.getBackground());
0541 progressBar.setOpaque(true);
0542 constraints2.gridy++;
0543 constraints2.anchor = GridBagConstraints.SOUTHWEST;
0544 bottomPanel.add(progressBar, constraints2);
0545
0546 // add the bottom panel to the fourth row
0547 constraints.gridy++;
0548 constraints.gridx = 0;
0549 constraints.gridwidth = GridBagConstraints.REMAINDER;
0550 constraints.gridheight = GridBagConstraints.REMAINDER;
0551 constraints.anchor = GridBagConstraints.WEST;
0552 constraints.insets = new Insets(0, 0, 0, 0);
0553 getContentPane().add(bottomPanel, constraints);
0554 constraints.insets = new Insets(2, 2, 2, 2);
0555
0556 // set the background colour
0557 Color background = diffTable.getBackground();
0558 getContentPane().setBackground(background);
0559 scroller.setBackground(background);
0560 scroller.getViewport().setBackground(background);
0561 statisticsPane.setBackground(background);
0562 adjudicationPane.setBackground(background);
0563 bottomPanel.setBackground(background);
0564 bottomTabbedPane.setBackground(background);
0565
0566 featureslistModel = new DefaultListModel();
0567 featuresList = new JList(featureslistModel);
0568 featuresList.setSelectionMode(
0569 ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
0570 keySetCombo.requestFocusInWindow();
0571 }
0572
0573 protected void initListeners(){
0574
0575 addWindowListener(new WindowAdapter(){
0576 public void windowClosing(WindowEvent e) {
0577 new CloseAction().actionPerformed(null);
0578 }
0579 });
0580
0581 addWindowFocusListener(new WindowAdapter(){
0582 public void windowGainedFocus(WindowEvent e) {
0583 populateGUI();
0584 }
0585 });
0586
0587 keyDocCombo.addActionListener(new ActionListener(){
0588 public void actionPerformed(ActionEvent evt){
0589 int keyDocSelectedIndex = keyDocCombo.getSelectedIndex();
0590 if (keyDocSelectedIndex == -1) { return; }
0591 Document newDoc = (Document) documents.get(keyDocSelectedIndex);
0592 if (keyDoc == newDoc) { return; }
0593 pairings.clear();
0594 diffTableModel.fireTableDataChanged();
0595 copyToTargetSetAction.setEnabled(false);
0596 keyDoc = newDoc;
0597 keySets = new ArrayList<AnnotationSet>();
0598 List<String> keySetNames = new ArrayList<String>();
0599 keySets.add(keyDoc.getAnnotations());
0600 keySetNames.add("[Default set]");
0601 if(keyDoc.getNamedAnnotationSets() != null) {
0602 for (Object o : keyDoc.getNamedAnnotationSets().keySet()) {
0603 String name = (String) o;
0604 keySetNames.add(name);
0605 keySets.add(keyDoc.getAnnotations(name));
0606 }
0607 }
0608 keySetCombo.setModel(new DefaultComboBoxModel(keySetNames.toArray()));
0609 if(!keySetNames.isEmpty()) {
0610 keySetCombo.setSelectedIndex(0);
0611 if(resSetCombo.getItemCount() > 0) {
0612 // find first annotation set with annotation type in common
0613 for(int res = 0; res < resSetCombo.getItemCount(); res++) {
0614 resSetCombo.setSelectedIndex(res);
0615 for(int key = 0; key < keySetCombo.getItemCount(); key++) {
0616 if (keyDoc.equals(resDoc)
0617 && resSetCombo.getItemAt(res).equals(
0618 keySetCombo.getItemAt(key))) {
0619 continue; // same document, skip it
0620 }
0621 keySetCombo.setSelectedIndex(key);
0622 if (annTypeCombo.getSelectedItem() != null) { break; }
0623 }
0624 if (annTypeCombo.getSelectedItem() != null) { break; }
0625 }
0626 if (annTypeCombo.getSelectedItem() == null) {
0627 statusLabel.setText("There is no annotation type in common.");
0628 statusLabel.setForeground(Color.RED);
0629 } else if (statusLabel.getText().equals(
0630 "There is no annotation type in common.")) {
0631 statusLabel.setText("The first annotation sets with" +
0632 " annotation type in common have been automatically selected.");
0633 statusLabel.setForeground(Color.BLACK);
0634 }
0635 }
0636 }
0637 }
0638 });
0639
0640 resDocCombo.addActionListener(new ActionListener(){
0641 public void actionPerformed(ActionEvent evt){
0642 int resDocSelectedIndex = resDocCombo.getSelectedIndex();
0643 if (resDocSelectedIndex == -1) { return; }
0644 Document newDoc = (Document) documents.get(resDocSelectedIndex);
0645 if (resDoc == newDoc) { return; }
0646 resDoc = newDoc;
0647 pairings.clear();
0648 diffTableModel.fireTableDataChanged();
0649 copyToTargetSetAction.setEnabled(false);
0650 resSets = new ArrayList<AnnotationSet>();
0651 List<String> resSetNames = new ArrayList<String>();
0652 resSets.add(resDoc.getAnnotations());
0653 resSetNames.add("[Default set]");
0654 if(resDoc.getNamedAnnotationSets() != null) {
0655 for (Object o : resDoc.getNamedAnnotationSets().keySet()) {
0656 String name = (String) o;
0657 resSetNames.add(name);
0658 resSets.add(resDoc.getAnnotations(name));
0659 }
0660 }
0661 resSetCombo.setModel(new DefaultComboBoxModel(resSetNames.toArray()));
0662 if(!resSetNames.isEmpty()) {
0663 resSetCombo.setSelectedIndex(0);
0664 if(keySetCombo.getItemCount() > 0) {
0665 // find annotation sets with annotations in common
0666 for(int res = 0; res < resSetCombo.getItemCount(); res++) {
0667 resSetCombo.setSelectedIndex(res);
0668 for(int key = 0; key < keySetCombo.getItemCount(); key++) {
0669 if (keyDoc.equals(resDoc)
0670 && resSetCombo.getItemAt(res).equals(
0671 keySetCombo.getItemAt(key))) {
0672 continue;
0673 }
0674 keySetCombo.setSelectedIndex(key);
0675 if (annTypeCombo.getSelectedItem() != null) { break; }
0676 }
0677 if (annTypeCombo.getSelectedItem() != null) { break; }
0678 }
0679 if (annTypeCombo.getSelectedItem() == null) {
0680 statusLabel.setText("There is no annotations in common.");
0681 statusLabel.setForeground(Color.RED);
0682 } else if (statusLabel.getText().equals(
0683 "There is no annotations in common.")) {
0684 statusLabel.setText("The first annotation sets with" +
0685 " annotations in common have been selected.");
0686 statusLabel.setForeground(Color.BLACK);
0687 }
0688 }
0689 }
0690 }
0691 });
0692
0693 /**
0694 * This populates the types combo when set selection changes
0695 */
0696 ActionListener setComboActionListener = new ActionListener(){
0697 public void actionPerformed(ActionEvent evt){
0698 keySet = keySets == null || keySets.isEmpty()?
0699 null : keySets.get(keySetCombo.getSelectedIndex());
0700 resSet = resSets == null || resSets.isEmpty()?
0701 null : resSets.get(resSetCombo.getSelectedIndex());
0702 Set<String> keyTypes = (keySet == null || keySet.isEmpty()) ?
0703 new HashSet<String>() : keySet.getAllTypes();
0704 Set<String> resTypes = (resSet == null || resSet.isEmpty()) ?
0705 new HashSet<String>() : resSet.getAllTypes();
0706 Set<String> types = new HashSet<String>(keyTypes);
0707 types.retainAll(resTypes);
0708 List<String> typesList = new ArrayList<String>(types);
0709 Collections.sort(typesList);
0710 annTypeCombo.setModel(new DefaultComboBoxModel(typesList.toArray()));
0711 if(typesList.size() > 0) {
0712 annTypeCombo.setSelectedIndex(0);
0713 diffAction.setEnabled(true);
0714 doDiffBtn.setForeground((Color)
0715 UIManager.getDefaults().get("Button.foreground"));
0716 doDiffBtn.setToolTipText(
0717 (String)diffAction.getValue(Action.SHORT_DESCRIPTION));
0718 } else {
0719 diffAction.setEnabled(false);
0720 doDiffBtn.setForeground((Color)
0721 UIManager.getDefaults().get("Button.disabledText"));
0722 doDiffBtn.setToolTipText("Choose two annotation sets "
0723 +"that have at least one annotation type in common.");
0724 }
0725 }
0726 };
0727 keySetCombo.addActionListener(setComboActionListener);
0728
0729 resSetCombo.addActionListener(setComboActionListener);
0730
0731 someFeaturesBtn.addActionListener(new ActionListener(){
0732 public void actionPerformed(ActionEvent evt){
0733 if(someFeaturesBtn.isSelected()){
0734 if(keySet == null || keySet.isEmpty() ||
0735 annTypeCombo.getSelectedItem() == null) return;
0736 Iterator<Annotation> annIter = keySet.
0737 get((String)annTypeCombo.getSelectedItem()).iterator();
0738 Set<String> featureSet = new HashSet<String>();
0739 while(annIter.hasNext()){
0740 Annotation ann = annIter.next();
0741 Map<Object, Object> someFeatures = ann.getFeatures();
0742 if (someFeatures == null) { continue; }
0743 for (Object feature : someFeatures.keySet()) {
0744 featureSet.add((String) feature);
0745 }
0746 }
0747 List<String> featureList = new ArrayList<String>(featureSet);
0748 Collections.sort(featureList);
0749 featureslistModel.clear();
0750 Iterator featIter = featureList.iterator();
0751 int index = 0;
0752 while(featIter.hasNext()){
0753 String aFeature = (String)featIter.next();
0754 featureslistModel.addElement(aFeature);
0755 if(significantFeatures.contains(aFeature))
0756 featuresList.addSelectionInterval(index, index);
0757 index ++;
0758 }
0759 int ret = JOptionPane.showConfirmDialog(AnnotationDiffGUI.this,
0760 new JScrollPane(featuresList),
0761 "Select features",
0762 JOptionPane.OK_CANCEL_OPTION,
0763 JOptionPane.QUESTION_MESSAGE);
0764 if(ret == JOptionPane.OK_OPTION){
0765 significantFeatures.clear();
0766 int[] selIdxs = featuresList.getSelectedIndices();
0767 for (int selIdx : selIdxs) {
0768 significantFeatures.add((String) featureslistModel.get(selIdx));
0769 }
0770 }
0771 }
0772 }
0773 });
0774
0775 // enable/disable buttons according to the table state
0776 diffTableModel.addTableModelListener(new TableModelListener() {
0777 public void tableChanged(javax.swing.event.TableModelEvent e) {
0778 if (diffTableModel.getRowCount() > 0) {
0779 htmlExportAction.setEnabled(true);
0780 htmlExportBtn.setToolTipText((String)
0781 htmlExportAction.getValue(Action.SHORT_DESCRIPTION));
0782 showDocumentAction.setEnabled(true);
0783 showDocumentBtn.setToolTipText((String)
0784 showDocumentAction.getValue(Action.SHORT_DESCRIPTION));
0785 } else {
0786 htmlExportAction.setEnabled(false);
0787 htmlExportBtn.setToolTipText("Use first the \"Compare\" button.");
0788 showDocumentAction.setEnabled(false);
0789 showDocumentBtn.setToolTipText("Use first the \"Compare\""
0790 + " button then select an annotation in the table.");
0791 }
0792 }
0793 });
0794
0795 // enable/disable buttons according to the table selection
0796 diffTable.getSelectionModel().addListSelectionListener(
0797 new ListSelectionListener() {
0798 public void valueChanged(ListSelectionEvent e) {
0799 int row = diffTable.rowViewToModel(diffTable.getSelectedRow());
0800 if (row == -1) { showDocumentAction.setEnabled(false); return; }
0801 AnnotationDiffer.Pairing pairing = pairings.get(row);
0802 Annotation key = pairing.getKey();
0803 Annotation response = pairing.getResponse();
0804 int column = diffTable.convertColumnIndexToModel(
0805 diffTable.getSelectedColumn());
0806 boolean enabled;
0807 switch(column){
0808 case DiffTableModel.COL_KEY_START:
0809 case DiffTableModel.COL_KEY_END:
0810 case DiffTableModel.COL_KEY_STRING:
0811 case DiffTableModel.COL_KEY_FEATURES:
0812 enabled = (key != null); break;
0813 case DiffTableModel.COL_RES_START:
0814 case DiffTableModel.COL_RES_END:
0815 case DiffTableModel.COL_RES_STRING:
0816 case DiffTableModel.COL_RES_FEATURES:
0817 enabled = (response != null); break;
0818 default: enabled = false;
0819 }
0820 showDocumentAction.setEnabled(enabled);
0821 }
0822 });
0823
0824 // enable/disable buttons according to the table selection
0825 diffTable.getColumnModel().addColumnModelListener(
0826 new TableColumnModelListener() {
0827 public void columnAdded(TableColumnModelEvent e) { /* do nothing */ }
0828 public void columnRemoved(TableColumnModelEvent e) { /* do nothing */ }
0829 public void columnMoved(TableColumnModelEvent e) { /* do nothing */ }
0830 public void columnMarginChanged(ChangeEvent e) { /* do nothing */ }
0831 public void columnSelectionChanged(ListSelectionEvent e) {
0832 int row = diffTable.rowViewToModel(diffTable.getSelectedRow());
0833 if (row == -1) { showDocumentAction.setEnabled(false); return; }
0834 AnnotationDiffer.Pairing pairing = pairings.get(row);
0835 Annotation key = pairing.getKey();
0836 Annotation response = pairing.getResponse();
0837 int column = diffTable.convertColumnIndexToModel(
0838 diffTable.getSelectedColumn());
0839 boolean enabled;
0840 switch(column){
0841 case DiffTableModel.COL_KEY_START:
0842 case DiffTableModel.COL_KEY_END:
0843 case DiffTableModel.COL_KEY_STRING:
0844 case DiffTableModel.COL_KEY_FEATURES:
0845 enabled = (key != null); break;
0846 case DiffTableModel.COL_RES_START:
0847 case DiffTableModel.COL_RES_END:
0848 case DiffTableModel.COL_RES_STRING:
0849 case DiffTableModel.COL_RES_FEATURES:
0850 enabled = (response != null); break;
0851 default: enabled = false;
0852 }
0853 showDocumentAction.setEnabled(enabled);
0854 }
0855 });
0856
0857 // inverse state of selected checkboxes when Space key is pressed
0858 diffTable.addKeyListener(new KeyAdapter() {
0859 public void keyPressed(KeyEvent e) {
0860 if (e.getKeyCode() != KeyEvent.VK_SPACE
0861 || !(diffTable.isColumnSelected(DiffTableModel.COL_KEY_COPY)
0862 || diffTable.isColumnSelected(DiffTableModel.COL_RES_COPY))) {
0863 return;
0864 }
0865 e.consume(); // disable normal behavior of Space key in a table
0866 int[] cols = {DiffTableModel.COL_KEY_COPY, DiffTableModel.COL_RES_COPY};
0867 for (int col : cols) {
0868 for (int row : diffTable.getSelectedRows()) {
0869 if (diffTable.isCellSelected(row, col)
0870 && diffTable.isCellEditable(row, col)) {
0871 diffTable.setValueAt(
0872 !(Boolean)diffTable.getValueAt(row, col), row, col);
0873 diffTableModel.fireTableCellUpdated(row, col);
0874 }
0875 }
0876 }
0877 }
0878 });
0879
0880 // context menu for the check boxes to easily change their state
0881 diffTable.addMouseListener(new MouseAdapter() {
0882 private JPopupMenu mousePopup;
0883 JMenuItem menuItem;
0884 public void mousePressed(MouseEvent e) {
0885 showContextMenu(e);
0886 }
0887 public void mouseReleased(MouseEvent e) {
0888 showContextMenu(e);
0889 }
0890 private void showContextMenu(MouseEvent e) {
0891 int col = diffTable.convertColumnIndexToModel(
0892 diffTable.columnAtPoint(e.getPoint()));
0893 if (!e.isPopupTrigger()
0894 || ( col != DiffTableModel.COL_KEY_COPY
0895 && col != DiffTableModel.COL_RES_COPY) ) {
0896 return;
0897 }
0898 mousePopup = new JPopupMenu();
0899 for (final String tick : new String[] {"Tick", "Untick"}) {
0900 menuItem = new JMenuItem(tick + " selected check boxes");
0901 menuItem.addActionListener(new ActionListener() {
0902 public void actionPerformed(ActionEvent ae) {
0903 int keyCol = diffTable.convertColumnIndexToView(
0904 DiffTableModel.COL_KEY_COPY);
0905 int responseCol = diffTable.convertColumnIndexToView(
0906 DiffTableModel.COL_RES_COPY);
0907 for (int row = 0; row < diffTable.getRowCount(); row++) {
0908 int rowModel = diffTable.rowViewToModel(row);
0909 AnnotationDiffer.Pairing pairing = pairings.get(rowModel);
0910 if (diffTable.isCellSelected(row, keyCol)
0911 && pairing.getKey() != null) {
0912 diffTable.setValueAt(tick.equals("Tick"), row, keyCol);
0913 }
0914 if (diffTable.isCellSelected(row, responseCol)
0915 && pairing.getResponse() != null) {
0916 diffTable.setValueAt(tick.equals("Tick"), row, responseCol);
0917 }
0918 }
0919 diffTableModel.fireTableDataChanged();
0920 }
0921 });
0922 mousePopup.add(menuItem);
0923 }
0924 mousePopup.addSeparator();
0925 String[] types = new String[] {"correct", "partially correct", "missing",
0926 "false positives", "mismatch"};
0927 String[] symbols = new String[] {"=", "~", "-?", "?-", "<>"};
0928 for (int i = 0; i < types.length; i++) {
0929 menuItem = new JMenuItem("Tick "+ types[i] + " annotations");
0930 final String symbol = symbols[i];
0931 menuItem.addActionListener(new ActionListener() {
0932 public void actionPerformed(ActionEvent ae) {
0933 int matchCol = diffTable.convertColumnIndexToView(
0934 DiffTableModel.COL_MATCH);
0935 for (int row = 0; row < diffTable.getRowCount(); row++) {
0936 int rowModel = diffTable.rowViewToModel(row);
0937 AnnotationDiffer.Pairing pairing = pairings.get(rowModel);
0938 if (diffTable.getValueAt(row, matchCol).equals(symbol)
0939 && pairing.getKey() != null) {
0940 keyCopyValueRows.set(diffTable.rowViewToModel(row), true);
0941 } else if (diffTable.getValueAt(row, matchCol).equals(symbol)
0942 && pairing.getResponse() != null) {
0943 resCopyValueRows.set(diffTable.rowViewToModel(row), true);
0944 }
0945 }
0946 diffTableModel.fireTableDataChanged();
0947 }
0948 });
0949 mousePopup.add(menuItem);
0950 }
0951 mousePopup.show(e.getComponent(), e.getX(), e.getY());
0952 }
0953 });
0954
0955 // revert to default name if the field is empty and lost focus
0956 consensusASTextField.addFocusListener(new FocusAdapter() {
0957 public void focusLost(FocusEvent e) {
0958 String target = consensusASTextField.getText().trim();
0959 if (target.length() == 0) {
0960 consensusASTextField.setText("consensus");
0961 }
0962 if (keyDoc != null
0963 && keyDoc.getAnnotationSetNames().contains(target)) {
0964 statusLabel.setText("Be careful, the annotation set " + target
0965 + " already exists.");
0966 statusLabel.setForeground(Color.RED);
0967 }
0968 }
0969 });
0970
0971 bottomTabbedPane.addChangeListener(new ChangeListener(){
0972 public void stateChanged(ChangeEvent e) {
0973 if (bottomTabbedPane.getSelectedIndex() == 0) {
0974 diffTable.hideColumn(DiffTableModel.COL_KEY_COPY);
0975 diffTable.hideColumn(DiffTableModel.COL_RES_COPY);
0976 } else {
0977 int middleIndex = Math.round(diffTable.getColumnCount() / 2);
0978 diffTable.showColumn(DiffTableModel.COL_KEY_COPY, middleIndex);
0979 diffTable.showColumn(DiffTableModel.COL_RES_COPY, middleIndex + 2);
0980 diffTable.getColumnModel().getColumn(DiffTableModel.COL_KEY_COPY)
0981 .setPreferredWidth(10);
0982 diffTable.getColumnModel().getColumn(DiffTableModel.COL_RES_COPY)
0983 .setPreferredWidth(10);
0984 diffTable.doLayout();
0985 }
0986 }
0987 });
0988
0989 // define keystrokes action bindings at the level of the main window
0990 InputMap inputMap = ((JComponent)this.getContentPane()).
0991 getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
0992 ActionMap actionMap = ((JComponent)this.getContentPane()).getActionMap();
0993 inputMap.put(KeyStroke.getKeyStroke("F1"), "Help");
0994 actionMap.put("Help", new AbstractAction() {
0995 public void actionPerformed(ActionEvent e) {
0996 new HelpAction().actionPerformed(null);
0997 }
0998 });
0999 }
1000
1001 public void pack(){
1002 super.pack();
1003 // add some vertical space for the table
1004 setSize(getWidth(), getHeight() + 300);
1005 }
1006
1007 protected void populateGUI(){
1008 try{
1009 documents = Gate.getCreoleRegister().getAllInstances("gate.Document");
1010 }catch(GateException ge){
1011 throw new GateRuntimeException(ge);
1012 }
1013 List<String> documentNames = new ArrayList<String>(documents.size());
1014 for(Resource document : documents) {
1015 documentNames.add(document.getName());
1016 }
1017 Object keyDocSelectedItem = keyDocCombo.getSelectedItem();
1018 Object resDocSelectedItem = resDocCombo.getSelectedItem();
1019 keyDocCombo.setModel(new DefaultComboBoxModel(documentNames.toArray()));
1020 resDocCombo.setModel(new DefaultComboBoxModel(documentNames.toArray()));
1021 if(!documents.isEmpty()){
1022 keyDocCombo.setSelectedItem(keyDocSelectedItem);
1023 if (keyDocCombo.getSelectedIndex() == -1) {
1024 keyDocCombo.setSelectedIndex(0);
1025 }
1026 resDocCombo.setSelectedItem(resDocSelectedItem);
1027 if (resDocCombo.getSelectedIndex() == -1) {
1028 resDocCombo.setSelectedIndex(0);
1029 }
1030 statusLabel.setText(documents.size() + " documents loaded");
1031 if (annTypeCombo.getSelectedItem() == null) {
1032 statusLabel.setText(statusLabel.getText() +
1033 ". Choose two annotation sets to compare.");
1034 }
1035 statusLabel.setForeground(Color.BLACK);
1036 } else {
1037 statusLabel.setText("You must load at least one document.");
1038 statusLabel.setForeground(Color.RED);
1039 }
1040 }
1041
1042 protected class DiffAction extends AbstractAction{
1043 public DiffAction(){
1044 super("Compare");
1045 putValue(SHORT_DESCRIPTION,
1046 "Compare annotations between key and response sets");
1047 putValue(MNEMONIC_KEY, KeyEvent.VK_ENTER);
1048 putValue(SMALL_ICON, MainFrame.getIcon("crystal-clear-action-run"));
1049 }
1050
1051 public void actionPerformed(ActionEvent evt){
1052 final int rowView = diffTable.getSelectedRow();
1053 final int colView = diffTable.getSelectedColumn();
1054 final int id = evt.getID();
1055 final String command = evt.getActionCommand();
1056
1057 // waiting animations
1058 progressBar.setIndeterminate(true);
1059 getContentPane().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
1060
1061 // compute the differences
1062 Runnable runnable = new Runnable() { public void run() {
1063 Set<Annotation> keys = new HashSet<Annotation>(
1064 keySet.get((String)annTypeCombo.getSelectedItem()));
1065 Set<Annotation> responses = new HashSet<Annotation>(
1066 resSet.get((String)annTypeCombo.getSelectedItem()));
1067 int countHidden = 0;
1068 if (bottomTabbedPane.getSelectedIndex() == 1) { // adjudication mode
1069 for (Annotation annotation : new ArrayList<Annotation>(keys)) {
1070 if (annotation.getFeatures().containsKey("anndiffstep")) {
1071 keys.remove(annotation); // previously copied
1072 countHidden++;
1073 }
1074 }
1075 for (Annotation annotation : new ArrayList<Annotation>(responses)) {
1076 if (annotation.getFeatures().containsKey("anndiffstep")) {
1077 responses.remove(annotation); // previously copied
1078 countHidden++;
1079 }
1080 }
1081 }
1082 if(someFeaturesBtn.isSelected())
1083 differ.setSignificantFeaturesSet(significantFeatures);
1084 else if(allFeaturesBtn.isSelected())
1085 differ.setSignificantFeaturesSet(null);
1086 else differ.setSignificantFeaturesSet(new HashSet());
1087 pairings.clear();
1088 pairings.addAll(differ.calculateDiff(keys, responses));
1089 keyCopyValueRows.clear();
1090 keyCopyValueRows.addAll(Collections.nCopies(pairings.size(), false));
1091 resCopyValueRows.clear();
1092 resCopyValueRows.addAll(Collections.nCopies(pairings.size(), false));
1093 if (command.equals("setvalue")) {
1094 // tick check boxes for annotations previously modified
1095 int row = 0;
1096 for (AnnotationDiffer.Pairing pairing : pairings) {
1097 if (pairing.getKey() != null
1098 && pairing.getKey().getFeatures().containsKey("anndiffmodified")) {
1099 keyCopyValueRows.set(row, true);
1100 }
1101 if (pairing.getResponse() != null
1102 && pairing.getResponse().getFeatures().containsKey("anndiffmodified")) {
1103 resCopyValueRows.set(row, true);
1104 }
1105 row++;
1106 }
1107 }
1108
1109 final int countHiddenF = countHidden;
1110 SwingUtilities.invokeLater(new Runnable(){ public void run(){
1111 // update the GUI
1112 diffTableModel.fireTableDataChanged();
1113 correctLbl.setText(Integer.toString(differ.getCorrectMatches()));
1114 partiallyCorrectLbl.setText(
1115 Integer.toString(differ.getPartiallyCorrectMatches()));
1116 missingLbl.setText(Integer.toString(differ.getMissing()));
1117 falsePozLbl.setText(Integer.toString(differ.getSpurious()));
1118 NumberFormat f = NumberFormat.getInstance();
1119 f.setMaximumFractionDigits(2);
1120 f.setMinimumFractionDigits(2);
1121 recallStrictLbl.setText(f.format(differ.getRecallStrict()));
1122 recallLenientLbl.setText(f.format(differ.getRecallLenient()));
1123 recallAveLbl.setText(f.format(differ.getRecallAverage()));
1124 precisionStrictLbl.setText(f.format(differ.getPrecisionStrict()));
1125 precisionLenientLbl.setText(f.format(differ.getPrecisionLenient()));
1126 precisionAveLbl.setText(f.format(differ.getPrecisionAverage()));
1127 double weight = Double.parseDouble(weightTxt.getText());
1128 fmeasureStrictLbl.setText(f.format(differ.getFMeasureStrict(weight)));
1129 fmeasureLenientLbl.setText(f.format(differ.getFMeasureLenient(weight)));
1130 fmeasureAveLbl.setText(f.format(differ.getFMeasureAverage(weight)));
1131 copyToTargetSetAction.setEnabled(keyDoc.equals(resDoc));
1132 if (keyDoc.equals(resDoc)) {
1133 copyToConsensusBtn.setToolTipText((String)
1134 copyToTargetSetAction.getValue(Action.SHORT_DESCRIPTION));
1135 } else {
1136 copyToConsensusBtn.setToolTipText(
1137 "Key and response document must be the same");
1138 }
1139 if (!command.equals("setvalue") && !command.equals("copy")) {
1140 statusLabel.setText(pairings.size() + " pairings have been found " +
1141 "(" + countHiddenF + " annotations are hidden)");
1142 statusLabel.setForeground(Color.BLACK);
1143 }
1144 diffTable.requestFocusInWindow();
1145 // stop waiting animations
1146 progressBar.setIndeterminate(false);
1147 getContentPane().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
1148 showDocumentAction.setEnabled(false);
1149
1150 if (!command.equals("setvalue") && !command.equals("copy")) { return; }
1151
1152 SwingUtilities.invokeLater(new Runnable(){ public void run(){
1153 if (command.equals("setvalue")) {
1154 // select the cell containing the previously selected annotation
1155 for (int row = 0; row < diffTable.getRowCount(); row++) {
1156 AnnotationDiffer.Pairing pairing =
1157 pairings.get(diffTable.rowViewToModel(row));
1158 if ((pairing.getKey() != null
1159 && pairing.getKey().getId() == id)
1160 || (pairing.getResponse() != null
1161 && pairing.getResponse().getId() == id)) {
1162 diffTable.changeSelection(row, colView, false, false);
1163 break;
1164 }
1165 }
1166 } else if (command.equals("copy")) {
1167 // select the previously selected cell
1168 diffTable.changeSelection(rowView, colView, false, false);
1169 }
1170 SwingUtilities.invokeLater(new Runnable(){ public void run(){
1171 diffTable.scrollRectToVisible(diffTable.getCellRect(
1172 diffTable.getSelectedRow(), diffTable.getSelectedColumn(), true));
1173 }});
1174 }});
1175 }});
1176 }};
1177 Thread thread = new Thread(runnable, "DiffAction");
1178 thread.setPriority(Thread.MIN_PRIORITY);
1179 thread.start();
1180 }
1181 }
1182
1183 /**
1184 * Copy selected annotations to the target annotation set.
1185 */
1186 protected class CopyToTargetSetAction extends AbstractAction {
1187 public CopyToTargetSetAction(){
1188 super("Copy selection to target set");
1189 putValue(SHORT_DESCRIPTION,
1190 "<html>Move selected annotations to the target annotation set" +
1191 "<br>and hide their paired annotations if not moved." +
1192 " <font color=\"#667799\"><small>Alt-Right" +
1193 "</small></font></html>");
1194 putValue(MNEMONIC_KEY, KeyEvent.VK_RIGHT);
1195 putValue(SMALL_ICON, MainFrame.getIcon("crystal-clear-action-loopnone"));
1196 }
1197 public void actionPerformed(ActionEvent evt){
1198 String step = (String) keyDoc.getFeatures().get("anndiffsteps");
1199 if (step == null) { step = "0"; }
1200 AnnotationSet target =
1201 keyDoc.getAnnotations(consensusASTextField.getText().trim());
1202 AnnotationSet keyAS =
1203 keyDoc.getAnnotations((String)keySetCombo.getSelectedItem());
1204 AnnotationSet responseAS =
1205 resDoc.getAnnotations((String)resSetCombo.getSelectedItem());
1206 int countCopied = 0, countMarked = 0;
1207 for (int row = 0; row < pairings.size(); row++) {
1208 if (keyCopyValueRows.get(row)) {
1209 Annotation key = pairings.get(row).getKey();
1210 key.getFeatures().put("anndiffstep", step);
1211 FeatureMap features = (FeatureMap)
1212 ((SimpleFeatureMapImpl)key.getFeatures()).clone();
1213 features.put("anndiffsource", keySetCombo.getSelectedItem());
1214 try { // copy the key annotation
1215 target.add(key.getStartNode().getOffset(),
1216 key.getEndNode().getOffset(), key.getType(), features);
1217 } catch (InvalidOffsetException e) { e.printStackTrace(); }
1218 if (key.getFeatures().containsKey("anndiffmodified")) {
1219 keyAS.remove(key); // remove the modified copy
1220 }
1221 countCopied++;
1222 if (pairings.get(row).getResponse() != null
1223 && !resCopyValueRows.get(row)) { // mark the response annotation
1224 pairings.get(row).getResponse().getFeatures().put("anndiffstep", step);
1225 countMarked++;
1226 }
1227 }
1228 if (resCopyValueRows.get(row)) {
1229 Annotation response = pairings.get(row).getResponse();
1230 response.getFeatures().put("anndiffstep", step);
1231 FeatureMap features = (FeatureMap)
1232 ((SimpleFeatureMapImpl)response.getFeatures()).clone();
1233 features.put("anndiffsource", resSetCombo.getSelectedItem());
1234 try { // copy the response annotation
1235 target.add(response.getStartNode().getOffset(),
1236 response.getEndNode().getOffset(), response.getType(), features);
1237 } catch (InvalidOffsetException e) { e.printStackTrace(); }
1238 if (response.getFeatures().containsKey("anndiffmodified")) {
1239 responseAS.remove(response); // remove the modified copy
1240 }
1241 countCopied++;
1242 if (pairings.get(row).getKey() != null
1243 && !keyCopyValueRows.get(row)) { // mark the key annotation
1244 pairings.get(row).getKey().getFeatures().put("anndiffstep", step);
1245 countMarked++;
1246 }
1247 }
1248 }
1249 if (countCopied > 0) {
1250 step = String.valueOf(Integer.valueOf(step) + 1);
1251 keyDoc.getFeatures().put("anndiffsteps", step);
1252 diffAction.actionPerformed(new ActionEvent(this, -1, "copy"));
1253 statusLabel.setText(countCopied +
1254 " annotations copied to " + consensusASTextField.getText().trim() +
1255 " and " + countMarked + " hidden");
1256 statusLabel.setForeground(Color.BLACK);
1257 } else {
1258 diffTable.requestFocusInWindow();
1259 statusLabel.setText(
1260 "Tick checkboxes in the columns K(ey) and R(esponse)");
1261 statusLabel.setForeground(Color.RED);
1262 }
1263 }
1264 }
1265
1266 protected class HTMLExportAction extends AbstractAction{
1267 public HTMLExportAction(){
1268 putValue(SHORT_DESCRIPTION, "Export the results to HTML");
1269 putValue(SMALL_ICON,
1270 MainFrame.getIcon("crystal-clear-app-download-manager"));
1271 }
1272 public void actionPerformed(ActionEvent evt){
1273 XJFileChooser fileChooser = (MainFrame.getFileChooser() != null) ?
1274 MainFrame.getFileChooser() : new XJFileChooser();
1275 fileChooser.setAcceptAllFileFilterUsed(true);
1276 fileChooser.setDialogTitle("Choose a file to export the results");
1277 fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
1278 ExtensionFileFilter filter = new ExtensionFileFilter("HTML files","html");
1279 fileChooser.addChoosableFileFilter(filter);
1280 String fileName = (resDoc.getSourceUrl() != null) ?
1281 new File (resDoc.getSourceUrl().getFile()).getName() : resDoc.getName();
1282 fileName += "_" + annTypeCombo.getSelectedItem().toString() + ".html";
1283 fileChooser.setFileName(fileName);
1284 fileChooser.setResource(AnnotationDiffGUI.class.getName());
1285 int res = fileChooser.showSaveDialog(AnnotationDiffGUI.this);
1286 if (res != JFileChooser.APPROVE_OPTION) { return; }
1287
1288 File saveFile = fileChooser.getSelectedFile();
1289 try{
1290 String nl = Strings.getNl();
1291 Writer fw = new BufferedWriter(new FileWriter(saveFile));
1292 //write the header
1293 fw.write(HEADER_1);
1294 fw.write(resDoc.getName() + " " +
1295 annTypeCombo.getSelectedItem().toString() +
1296 " annotations");
1297 fw.write(HEADER_2 + nl);
1298 fw.write("<H2>Annotation Diff - comparing " +
1299 annTypeCombo.getSelectedItem().toString() +
1300 " annotations" + "</H2>");
1301 fw.write("<TABLE cellpadding=\"5\" border=\"0\"");
1302 fw.write(nl);
1303 fw.write("<TR>" + nl);
1304 fw.write("\t<TH align=\"left\"> </TH>" + nl);
1305 fw.write("\t<TH align=\"left\">Document</TH>" + nl);
1306 fw.write("\t<TH align=\"left\">Annotation Set</TH>" + nl);
1307 fw.write("</TR>" + nl);
1308
1309 fw.write("<TR>" + nl);
1310 fw.write("\t<TH align=\"left\">Key</TH>" + nl);
1311 fw.write("\t<TD align=\"left\">" + keyDoc.getName() + "</TD>" + nl);
1312 fw.write("\t<TD align=\"left\">" + keySet.getName() + "</TD>" + nl);
1313 fw.write("</TR>" + nl);
1314 fw.write("<TR>" + nl);
1315 fw.write("\t<TH align=\"left\">Response</TH>" + nl);
1316 fw.write("\t<TD align=\"left\">" + resDoc.getName() + "</TD>" + nl);
1317 fw.write("\t<TD align=\"left\">" + resSet.getName() + "</TD>" + nl);
1318 fw.write("</TR>" + nl);
1319 fw.write("</TABLE>" + nl);
1320 fw.write("<BR><BR><BR>" + nl);
1321 //write the results
1322 java.text.NumberFormat format = java.text.NumberFormat.getInstance();
1323 format.setMaximumFractionDigits(4);
1324 fw.write("Recall: " + format.format(differ.getRecallStrict()) + "<br>" + nl);
1325 fw.write("Precision: " + format.format(differ.getPrecisionStrict()) + "<br>" + nl);
1326 fw.write("F-measure: " + format.format(differ.getFMeasureStrict(1)) + "<br>" + nl);
1327 fw.write("<br>");
1328 fw.write("Correct: " + differ.getCorrectMatches() + "<br>" + nl);
1329 fw.write("Partially correct: " +
1330 differ.getPartiallyCorrectMatches() + "<br>" + nl);
1331 fw.write("Missing: " + differ.getMissing() + "<br>" + nl);
1332 fw.write("False positives: " + differ.getSpurious() + "<br>" + nl);
1333 fw.write(HEADER_3 + nl + "<TR>" + nl);
1334 int maxColIdx = diffTable.getColumnCount() - 1;
1335 for(int col = 0; col <= maxColIdx; col++){
1336 fw.write("\t<TH align=\"left\">" + diffTable.getColumnName(col) +
1337 "</TH>" + nl);
1338 }
1339 fw.write("</TR>");
1340 int rowCnt = diffTableModel.getRowCount();
1341 for(int row = 0; row < rowCnt; row ++){
1342 fw.write("<TR>");
1343 for(int col = 0; col <= maxColIdx; col++){
1344 Color bgCol = diffTableModel.getBackgroundAt(
1345 diffTable.rowViewToModel(row),
1346 diffTable.convertColumnIndexToModel(col));
1347 fw.write("\t<TD bgcolor=\"#" +
1348 Integer.toHexString(bgCol.getRGB()).substring(2) +
1349 "\">" +
1350 diffTable.getValueAt(row, col) +
1351 "</TD>" + nl);
1352 }
1353 fw.write("</TR>");
1354 }
1355 fw.write(FOOTER);
1356 fw.flush();
1357 fw.close();
1358
1359 } catch(IOException ioe){
1360 JOptionPane.showMessageDialog(AnnotationDiffGUI.this, ioe.toString(),
1361 "GATE", JOptionPane.ERROR_MESSAGE);
1362 ioe.printStackTrace();
1363 }
1364 }
1365
1366 static final String HEADER_1 = "<html><head><title>";
1367 static final String HEADER_2 = "</title></head><body>";
1368 static final String HEADER_3 = "<table cellpadding=\"0\" border=\"1\">";
1369 static final String FOOTER = "</table></body></html>";
1370 }
1371
1372 protected class ShowDocumentAction extends AbstractAction{
1373 public ShowDocumentAction(){
1374 putValue(SHORT_DESCRIPTION,
1375 "Show the selected annotation in the document editor.");
1376 putValue(SMALL_ICON, MainFrame.getIcon("document"));
1377 putValue(MNEMONIC_KEY, KeyEvent.VK_UP);
1378 }
1379 public void actionPerformed(ActionEvent evt){
1380 int rowModel = diffTable.rowViewToModel(diffTable.getSelectedRow());
1381 boolean isKeySelected = (diffTable.convertColumnIndexToModel(
1382 diffTable.getSelectedColumn()) < DiffTableModel.COL_MATCH);
1383 final Document doc = isKeySelected ? keyDoc : resDoc;
1384 final Annotation annotation = isKeySelected ?
1385 pairings.get(rowModel).getKey() : pairings.get(rowModel).getResponse();
1386 final String asname = isKeySelected ? keySet.getName() : resSet.getName();
1387 // show the expression in the document
1388 SwingUtilities.invokeLater(new Runnable() {
1389 public void run() {
1390 MainFrame.getInstance().select(doc);
1391 // wait some time for the document to be displayed
1392 Date timeToRun = new Date(System.currentTimeMillis() + 1000);
1393 Timer timer = new Timer("Annotation diff show document timer", true);
1394 timer.schedule(new TimerTask() {
1395 public void run() {
1396 showExpressionInDocument(doc, annotation, asname);
1397 }
1398 }, timeToRun);
1399 }});
1400 }
1401
1402 private void showExpressionInDocument(Document doc, Annotation annotation,
1403 String asname) {
1404 try {
1405 // find the document view associated with the document
1406 TextualDocumentView t = null;
1407 for (Resource r : Gate.getCreoleRegister().getAllInstances(
1408 "gate.gui.docview.TextualDocumentView")) {
1409 if (((TextualDocumentView)r).getDocument().getName()
1410 .equals(doc.getName())) {
1411 t = (TextualDocumentView)r;
1412 break;
1413 }
1414 }
1415
1416 if (t != null && t.getOwner() != null) {
1417 // display the annotation sets view
1418 t.getOwner().setRightView(0);
1419 try {
1420 // scroll to the expression that matches the query result
1421 t.getTextView().scrollRectToVisible(
1422 t.getTextView().modelToView(
1423 annotation.getStartNode().getOffset().intValue()));
1424 } catch (BadLocationException e) {
1425 e.printStackTrace();
1426 return;
1427 }
1428 // select the expression that matches the query result
1429 t.getTextView().select(
1430 annotation.getStartNode().getOffset().intValue(),
1431 annotation.getEndNode().getOffset().intValue());
1432 }
1433
1434 // find the annotation sets view associated with the document
1435 for (Resource r : Gate.getCreoleRegister().getAllInstances(
1436 "gate.gui.docview.AnnotationSetsView")) {
1437 AnnotationSetsView asv = (AnnotationSetsView)r;
1438 if (asv.getDocument() != null
1439 && asv.isActive()
1440 && asv.getDocument().getName().equals(doc.getName())) {
1441 // look if there is the type displayed
1442 String type = annotation.getType();
1443 if (asname == null
1444 && doc.getAnnotations().getAllTypes().contains(type)) {
1445 asv.setTypeSelected(null, type, true);
1446 } else if (doc.getAnnotationSetNames().contains(asname)
1447 && doc.getAnnotations(asname).getAllTypes().contains(type)) {
1448 asv.setTypeSelected(asname, type, true);
1449 }
1450 }
1451 }
1452
1453 diffTable.requestFocusInWindow();
1454
1455 } catch (gate.util.GateException e) {
1456 e.printStackTrace();
1457 }
1458 }
1459 }
1460
1461 protected class HelpAction extends AbstractAction {
1462 public HelpAction() {
1463 super();
1464 putValue(SHORT_DESCRIPTION, "User guide for this tool");
1465 putValue(SMALL_ICON, MainFrame.getIcon("crystal-clear-action-info"));
1466 putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke("F1"));
1467 }
1468 public void actionPerformed(ActionEvent e) {
1469 MainFrame.getInstance().showHelpFrame(
1470 "sec:eval:annotationdiff", AnnotationDiffGUI.class.getName());
1471 }
1472 }
1473
1474 protected class CloseAction extends AbstractAction {
1475 public CloseAction(){
1476 super("Close");
1477 }
1478 public void actionPerformed(ActionEvent evt){
1479 MainFrame.getGuiRoots().remove(AnnotationDiffGUI.this);
1480 dispose();
1481 }
1482 }
1483
1484 protected class DiffTableCellRenderer extends DefaultTableCellRenderer{
1485 public Component getTableCellRendererComponent(JTable table, Object value,
1486 boolean isSelected, boolean hasFocus, int row, int column){
1487 int rowModel = diffTable.rowViewToModel(row);
1488 int colModel = diffTable.convertColumnIndexToModel(column);
1489 Component component;
1490 if (value instanceof Boolean) {
1491 component = new JCheckBox();
1492 } else {
1493 component = super.getTableCellRendererComponent(table, value,
1494 false, hasFocus, row, column);
1495 }
1496 if (pairings.size() == 0 || rowModel >= pairings.size()) {
1497 return component;
1498 }
1499 AnnotationDiffer.Pairing pairing = pairings.get(rowModel);
1500 // set fore and background colours
1501 component.setBackground(isSelected ? table.getSelectionBackground() :
1502 diffTableModel.getBackgroundAt(rowModel, column));
1503 component.setForeground(isSelected ? table.getSelectionForeground() :
1504 table.getForeground());
1505 if (!(component instanceof JComponent)) { return component; }
1506 // add tooltips for each cell, disable some checkboxes
1507 // shorten features column values
1508 String tip;
1509 try {
1510 switch (colModel){
1511 case DiffTableModel.COL_KEY_STRING:
1512 Annotation key = pairing.getKey();
1513 if (key == null) {
1514 tip = null;
1515 } else { // reformat the text
1516 tip = keyDoc.getContent().getContent(
1517 key.getStartNode().getOffset(),
1518 key.getEndNode().getOffset()).toString();
1519 if (tip.length() > 1000) {
1520 tip = tip.substring(0, 1000 / 2) + "<br>...<br>"
1521 + tip.substring(tip.length() - (1000 / 2));
1522 }
1523 tip = keyDoc.getContent().getContent(
1524 Math.max(0, key.getStartNode().getOffset()-40),
1525 Math.max(0, key.getStartNode().getOffset())).toString() +
1526 "<strong>" + tip + "</strong>" +
1527 keyDoc.getContent().getContent(
1528 Math.min(keyDoc.getContent().size(),
1529 key.getEndNode().getOffset()),
1530 Math.min(keyDoc.getContent().size(),
1531 key.getEndNode().getOffset()+40)).toString();
1532 tip = tip.replaceAll("\\s*\n\\s*", "<br>");
1533 tip = tip.replaceAll("\\s+", " ");
1534 tip = "<html><table width=\""+(tip.length() > 150? "500": "100%")
1535 + "\" border=\"0\" cellspacing=\"0\">"
1536 + "<tr><td>" + tip + "</td></tr><tr><td>";
1537 tip += "<small><i>âÜì = new line, âÜí = tab, · = space</i></small>";
1538 tip += "</td></tr></table></html>";
1539 }
1540 break;
1541 case DiffTableModel.COL_KEY_FEATURES:
1542 if (pairing.getKey() == null) {
1543 tip = null;
1544 } else {
1545 String features = pairing.getKey().getFeatures().toString();
1546 tip = features +
1547 "<br><small><i>To edit, double-click or press F2.</i></small>";
1548 tip = "<html><table width=\""+(tip.length() > 150? "500": "100%")
1549 + "\" border=\"0\" cellspacing=\"0\">"
1550 + "<tr><td>" + tip + "</td></tr></table></html>";
1551 if (features.length() > maxCellLength) {
1552 features = features.substring(0, maxCellLength / 2) + "..."
1553 + features.substring(features.length() - (maxCellLength / 2));
1554 }
1555 ((JLabel)component).setText(features);
1556 }
1557 break;
1558 case DiffTableModel.COL_KEY_COPY:
1559 tip = (pairing.getKey() == null) ?
1560 "<html>There is no key annotation."
1561 : "<html>Select this key annotation to copy.";
1562 tip += "<br><small><i>"
1563 + "Space key invert the selected check boxes state."
1564 + "<br>Right-click for context menu.</i></small></html>";
1565 component.setEnabled(pairing.getKey() != null);
1566 ((JCheckBox)component).setSelected(keyCopyValueRows.get(rowModel));
1567 break;
1568 case DiffTableModel.COL_MATCH: tip =
1569 value.equals("=") ? "correct" :
1570 value.equals("~") ? "partially correct" :
1571 value.equals("-?") ? "missing" :
1572 value.equals("?-") ? "false positives" :
1573 value.equals("<>") ? "mismatch" : "";
1574 break;
1575 case DiffTableModel.COL_RES_COPY:
1576 tip = (pairing.getResponse() == null) ?
1577 "<html>There is no response annotation."
1578 : "<html>Select this response annotation to copy.";
1579 tip += "<br><small><i>"
1580 + "Space key invert the selected check boxes state."
1581 + "<br>Right-click for context menu.</i></small></html>";
1582 component.setEnabled(pairing.getResponse() != null);
1583 ((JCheckBox)component).setSelected(resCopyValueRows.get(rowModel));
1584 break;
1585 case DiffTableModel.COL_RES_STRING:
1586 Annotation response = pairing.getResponse();
1587 if (response == null) {
1588 tip = null;
1589 } else { // reformat the text
1590 tip = resDoc.getContent().getContent(
1591 response.getStartNode().getOffset(),
1592 response.getEndNode().getOffset()).toString();
1593 if (tip.length() > 1000) {
1594 tip = tip.substring(0, 1000 / 2) + "<br>...<br>"
1595 + tip.substring(tip.length() - (1000 / 2));
1596 }
1597 tip = resDoc.getContent().getContent(
1598 Math.max(0, response.getStartNode().getOffset()-40),
1599 Math.max(0, response.getStartNode().getOffset())).toString() +
1600 "<strong>" + tip + "</strong>" +
1601 resDoc.getContent().getContent(
1602 Math.min(resDoc.getContent().size(),
1603 response.getEndNode().getOffset()),
1604 Math.min(resDoc.getContent().size(),
1605 response.getEndNode().getOffset()+40)).toString();
1606 tip = tip.replaceAll("\\s*\n\\s*", "<br>");
1607 tip = tip.replaceAll("\\s+", " ");
1608 tip = "<html><table width=\""+(tip.length() > 150? "500": "100%")
1609 + "\" border=\"0\" cellspacing=\"0\">"
1610 + "<tr><td>" + tip + "</td></tr><tr><td>";
1611 tip += "<small><i>âÜì = new line, âÜí = tab, · = space</i></small>";
1612 tip += "</td></tr></table></html>";
1613 }
1614 break;
1615 case DiffTableModel.COL_RES_FEATURES:
1616 if (pairing.getResponse() == null) {
1617 tip = null;
1618 } else {
1619 String features = pairing.getResponse().getFeatures().toString();
1620 tip = features +
1621 "<br><small><i>To edit, double-click or press F2.</i></small>";
1622 tip = "<html><table width=\""+(tip.length() > 150? "500": "100%")
1623 + "\" border=\"0\" cellspacing=\"0\">"
1624 + "<tr><td>" + tip + "</td></tr></table></html>";
1625 if (features.length() > maxCellLength) {
1626 features = features.substring(0, maxCellLength / 2) + "..."
1627 + features.substring(features.length() - (maxCellLength / 2));
1628 }
1629 ((JLabel)component).setText(features);
1630 }
1631 break;
1632 default:
1633 Annotation ann = (colModel < DiffTableModel.COL_MATCH) ?
1634 pairing.getKey() : pairing.getResponse();
1635 if (ann == null) {
1636 tip = null;
1637 } else {
1638 tip = "<html><small><i>To edit, double-click or press F2." +
1639 "</i></small></html>";
1640 }
1641 }
1642 } catch(InvalidOffsetException ioe){
1643 //this should never happen
1644 throw new GateRuntimeException(ioe);
1645 }
1646 ((JComponent)component).setToolTipText(tip);
1647 return component;
1648 }
1649 }
1650
1651 protected class DiffTableModel extends AbstractTableModel{
1652
1653 public int getRowCount(){
1654 return pairings.size();
1655 }
1656
1657 public Class getColumnClass(int columnIndex){
1658 switch (columnIndex){
1659 case COL_KEY_COPY: return Boolean.class;
1660 case COL_RES_COPY: return Boolean.class;
1661 default: return String.class;
1662 }
1663 }
1664
1665 public int getColumnCount(){
1666 return COL_COUNT;
1667 }
1668
1669 public String getColumnName(int column){
1670 switch(column){
1671 case COL_KEY_START: return "Start";
1672 case COL_KEY_END: return "End";
1673 case COL_KEY_STRING: return "Key";
1674 case COL_KEY_FEATURES: return "Features";
1675 case COL_KEY_COPY: return "K";
1676 case COL_MATCH: return "=?";
1677 case COL_RES_COPY: return "R";
1678 case COL_RES_START: return "Start";
1679 case COL_RES_END: return "End";
1680 case COL_RES_STRING: return "Response";
1681 case COL_RES_FEATURES: return "Features";
1682 default: return "?";
1683 }
1684 }
1685
1686 public Color getBackgroundAt(int row, int column){
1687 AnnotationDiffer.Pairing pairing = pairings.get(row);
1688 switch(pairing.getType()){
1689 case(AnnotationDiffer.CORRECT_TYPE): return diffTable.getBackground();
1690 case(AnnotationDiffer.PARTIALLY_CORRECT_TYPE): return PARTIALLY_CORRECT_BG;
1691 case(AnnotationDiffer.MISMATCH_TYPE):
1692 if(column < COL_MATCH) return MISSING_BG;
1693 else if(column > COL_MATCH) return FALSE_POSITIVE_BG;
1694 else return diffTable.getBackground();
1695 case(AnnotationDiffer.MISSING_TYPE): return MISSING_BG;
1696 case(AnnotationDiffer.SPURIOUS_TYPE): return FALSE_POSITIVE_BG;
1697 default: return diffTable.getBackground();
1698 }
1699 //
1700 // Color colKey = pairing.getType() ==
1701 // AnnotationDiffer.CORRECT_TYPE ?
1702 // diffTable.getBackground() :
1703 // (pairing.getType() == AnnotationDiffer.PARTIALLY_CORRECT_TYPE ?
1704 // PARTIALLY_CORRECT_BG : MISSING_BG);
1705 // if(pairing.getKey() == null) colKey = diffTable.getBackground();
1706 //
1707 // Color colRes = pairing.getType() == AnnotationDiffer.CORRECT_TYPE ?
1708 // diffTable.getBackground() :
1709 // (pairing.getType() == AnnotationDiffer.PARTIALLY_CORRECT_TYPE ?
1710 // PARTIALLY_CORRECT_BG :
1711 // FALSE_POSITIVE_BG);
1712 // if(pairing.getResponse() == null) colRes = diffTable.getBackground();
1713 // switch(column){
1714 // case COL_KEY_START: return colKey;
1715 // case COL_KEY_END: return colKey;
1716 // case COL_KEY_STRING: return colKey;
1717 // case COL_KEY_FEATURES: return colKey;
1718 // case COL_MATCH: return diffTable.getBackground();
1719 // case COL_RES_START: return colRes;
1720 // case COL_RES_END: return colRes;
1721 // case COL_RES_STRING: return colRes;
1722 // case COL_RES_FEATURES: return colRes;
1723 // default: return diffTable.getBackground();
1724 // }
1725 }
1726
1727 public Object getValueAt(int row, int column){
1728 AnnotationDiffer.Pairing pairing = pairings.get(row);
1729 Annotation key = pairing.getKey();
1730 Annotation res = pairing.getResponse();
1731
1732 switch(column){
1733 case COL_KEY_START: return key == null ? "" :
1734 key.getStartNode().getOffset().toString();
1735 case COL_KEY_END: return key == null ? "" :
1736 key.getEndNode().getOffset().toString();
1737 case COL_KEY_STRING:
1738 String keyStr = "";
1739 try{
1740 if(key != null && keyDoc != null){
1741 keyStr = keyDoc.getContent().getContent(
1742 key.getStartNode().getOffset(),
1743 key.getEndNode().getOffset()).toString();
1744 }
1745 }catch(InvalidOffsetException ioe){
1746 //this should never happen
1747 throw new GateRuntimeException(ioe);
1748 }
1749 // cut annotated text in the middle if too long
1750 if (keyStr.length() > maxCellLength) {
1751 keyStr = keyStr.substring(0, maxCellLength / 2) + "..."
1752 + keyStr.substring(keyStr.length() - (maxCellLength / 2));
1753 }
1754 // use special characters for newline, tab and space
1755 keyStr = keyStr.replaceAll("(?:\r?\n)|\r", "âÜì");
1756 keyStr = keyStr.replaceAll("\t", "âÜí");
1757 keyStr = keyStr.replaceAll(" ", "·");
1758 return keyStr;
1759 case COL_KEY_FEATURES: return key == null ? "" :
1760 key.getFeatures().toString();
1761 case COL_KEY_COPY: return keyCopyValueRows.get(row);
1762 case COL_MATCH: return matchLabel[pairing.getType()];
1763 case COL_RES_COPY: return resCopyValueRows.get(row);
1764 case COL_RES_START: return res == null ? "" :
1765 res.getStartNode().getOffset().toString();
1766 case COL_RES_END: return res == null ? "" :
1767 res.getEndNode().getOffset().toString();
1768 case COL_RES_STRING:
1769 String resStr = "";
1770 try{
1771 if(res != null && resDoc != null){
1772 resStr = resDoc.getContent().getContent(
1773 res.getStartNode().getOffset(),
1774 res.getEndNode().getOffset()).toString();
1775 }
1776 }catch(InvalidOffsetException ioe){
1777 //this should never happen
1778 throw new GateRuntimeException(ioe);
1779 }
1780 if (resStr.length() > maxCellLength) {
1781 resStr = resStr.substring(0, maxCellLength / 2) + "..."
1782 + resStr.substring(resStr.length() - (maxCellLength / 2));
1783 }
1784 // use special characters for newline, tab and space
1785 resStr = resStr.replaceAll("(?:\r?\n)|\r", "âÜì");
1786 resStr = resStr.replaceAll("\t", "âÜí");
1787 resStr = resStr.replaceAll(" ", "·");
1788 return resStr;
1789 case COL_RES_FEATURES: return res == null ? "" :
1790 res.getFeatures().toString();
1791 default: return "?";
1792 }
1793 }
1794
1795 public boolean isCellEditable(int rowIndex, int columnIndex){
1796 if (pairings.size() == 0) { return false; }
1797 AnnotationDiffer.Pairing pairing = pairings.get(rowIndex);
1798 switch(columnIndex) {
1799 case COL_KEY_COPY:
1800 case COL_KEY_START:
1801 case COL_KEY_END:
1802 case COL_KEY_FEATURES: return pairing.getKey() != null;
1803 case COL_RES_COPY:
1804 case COL_RES_START:
1805 case COL_RES_END:
1806 case COL_RES_FEATURES: return pairing.getResponse() != null;
1807 default: return false;
1808 }
1809 }
1810
1811 public void setValueAt(Object aValue, int rowIndex, int columnIndex){
1812 AnnotationDiffer.Pairing pairing = pairings.get(rowIndex);
1813 AnnotationSet keyAS =
1814 keyDoc.getAnnotations((String)keySetCombo.getSelectedItem());
1815 AnnotationSet responseAS =
1816 resDoc.getAnnotations((String)resSetCombo.getSelectedItem());
1817 Annotation key = pairing.getKey();
1818 Annotation res = pairing.getResponse();
1819 String step = (String) keyDoc.getFeatures().get("anndiffsteps");
1820 if (step == null) { step = "0"; }
1821 int id = -1;
1822 String keysValues;
1823 FeatureMap features;
1824 try {
1825 switch(columnIndex){
1826 case COL_KEY_START:
1827 if (Long.valueOf((String) aValue)
1828 .equals(key.getStartNode().getOffset())) { break; }
1829 id = keyAS.add(Long.valueOf((String)aValue),
1830 key.getEndNode().getOffset(), key.getType(),
1831 (FeatureMap)((SimpleFeatureMapImpl)key.getFeatures()).clone());
1832 keyAS.get(id).getFeatures().put("anndiffmodified", "true");
1833 if (key.getFeatures().containsKey("anndiffmodified")) {
1834 keyAS.remove(key);
1835 } else {
1836 key.getFeatures().put("anndiffstep", step);
1837 }
1838 statusLabel.setText("Start offset changed: " +
1839 key.getStartNode().getOffset() + " -> " + aValue + ".");
1840 statusLabel.setForeground(Color.BLACK);
1841 break;
1842 case COL_KEY_END:
1843 if (Long.valueOf((String) aValue)
1844 .equals(key.getEndNode().getOffset())) { break; }
1845 id = keyAS.add(key.getStartNode().getOffset(),
1846 Long.valueOf((String)aValue), key.getType(),
1847 (FeatureMap)((SimpleFeatureMapImpl)key.getFeatures()).clone());
1848 keyAS.get(id).getFeatures().put("anndiffmodified", "true");
1849 if (key.getFeatures().containsKey("anndiffmodified")) {
1850 keyAS.remove(key);
1851 } else {
1852 key.getFeatures().put("anndiffstep", step);
1853 }
1854 statusLabel.setText("End offset changed: " +
1855 key.getEndNode().getOffset() + " -> " + aValue + ".");
1856 statusLabel.setForeground(Color.BLACK);
1857 break;
1858 case COL_KEY_FEATURES:
1859 keysValues = (String) aValue;
1860 keysValues = keysValues.replaceAll("\\s+", " ").replaceAll("[}{]", "");
1861 features = Factory.newFeatureMap();
1862 if (keysValues.length() != 0) {
1863 for (String keyValue : keysValues.split(",")) {
1864 String[] keyOrValue = keyValue.split("=");
1865 if (keyOrValue.length != 2) { throw new NumberFormatException(); }
1866 features.put(keyOrValue[0].trim(), keyOrValue[1].trim());
1867 }
1868 }
1869 if (features.equals(key.getFeatures())) { break; }
1870 id = keyAS.add(key.getStartNode().getOffset(),
1871 key.getEndNode().getOffset(), key.getType(), features);
1872 keyAS.get(id).getFeatures().put("anndiffmodified", "true");
1873 if (key.getFeatures().containsKey("anndiffmodified")) {
1874 keyAS.remove(key);
1875 } else {
1876 key.getFeatures().put("anndiffstep", step);
1877 }
1878 statusLabel.setText("Features changed.");
1879 statusLabel.setForeground(Color.BLACK);
1880 break;
1881 case COL_KEY_COPY:
1882 keyCopyValueRows.set(rowIndex, (Boolean)aValue);
1883 break;
1884 case COL_RES_COPY:
1885 resCopyValueRows.set(rowIndex, (Boolean)aValue);
1886 break;
1887 case COL_RES_START:
1888 if (Long.valueOf((String) aValue)
1889 .equals(res.getStartNode().getOffset())) { break; }
1890 id = responseAS.add(Long.valueOf((String)aValue),
1891 res.getEndNode().getOffset(), res.getType(),
1892 (FeatureMap)((SimpleFeatureMapImpl)res.getFeatures()).clone());
1893 responseAS.get(id).getFeatures().put("anndiffmodified", "true");
1894 if (res.getFeatures().containsKey("anndiffmodified")) {
1895 responseAS.remove(res);
1896 } else {
1897 res.getFeatures().put("anndiffstep", step);
1898 }
1899 statusLabel.setText("Start offset changed: " +
1900 res.getStartNode().getOffset() + " -> " + aValue + ".");
1901 statusLabel.setForeground(Color.BLACK);
1902 break;
1903 case COL_RES_END:
1904 if (Long.valueOf((String) aValue)
1905 .equals(res.getEndNode().getOffset())) { break; }
1906 id = responseAS.add(res.getStartNode().getOffset(),
1907 Long.valueOf((String)aValue), res.getType(),
1908 (FeatureMap)((SimpleFeatureMapImpl)res.getFeatures()).clone());
1909 responseAS.get(id).getFeatures().put("anndiffmodified", "true");
1910 if (res.getFeatures().containsKey("anndiffmodified")) {
1911 responseAS.remove(res);
1912 } else {
1913 res.getFeatures().put("anndiffstep", step);
1914 }
1915 statusLabel.setText("End offset changed: " +
1916 res.getEndNode().getOffset() + " -> " + aValue + ".");
1917 statusLabel.setForeground(Color.BLACK);
1918 break;
1919 case COL_RES_FEATURES:
1920 keysValues = (String) aValue;
1921 keysValues = keysValues.replaceAll("\\s+", " ").replaceAll("[}{]", "");
1922 features = Factory.newFeatureMap();
1923 if (keysValues.length() != 0) {
1924 for (String keyValue : keysValues.split(",")) {
1925 String[] keyOrValue = keyValue.split("=");
1926 if (keyOrValue.length != 2) { throw new NumberFormatException(); }
1927 features.put(keyOrValue[0].trim(), keyOrValue[1].trim());
1928 }
1929 }
1930 if (features.equals(res.getFeatures())) { break; }
1931 id = responseAS.add(res.getStartNode().getOffset(),
1932 res.getEndNode().getOffset(), res.getType(), features);
1933 responseAS.get(id).getFeatures().put("anndiffmodified", "true");
1934 if (res.getFeatures().containsKey("anndiffmodified")) {
1935 responseAS.remove(res);
1936 } else {
1937 res.getFeatures().put("anndiffstep", step);
1938 }
1939 statusLabel.setText("Features changed.");
1940 statusLabel.setForeground(Color.BLACK);
1941 break;
1942 }
1943 }catch(InvalidOffsetException e){
1944 statusLabel.setText(
1945 "This offset is incorrect. No changes have been made.");
1946 statusLabel.setForeground(Color.RED);
1947 return;
1948 }catch(NumberFormatException e) {
1949 statusLabel.setText(
1950 "The format is incorrect. No changes have been made.");
1951 statusLabel.setForeground(Color.RED);
1952 return;
1953 }
1954 if (id != -1) {
1955 // compute again the differences
1956 diffAction.actionPerformed(new ActionEvent(this, id, "setvalue"));
1957 }
1958 }
1959
1960 protected static final int COL_COUNT = 11;
1961 protected static final int COL_KEY_START = 0;
1962 protected static final int COL_KEY_END = 1;
1963 protected static final int COL_KEY_STRING = 2;
1964 protected static final int COL_KEY_FEATURES = 3;
1965 protected static final int COL_KEY_COPY = 4;
1966 protected static final int COL_MATCH = 5;
1967 protected static final int COL_RES_COPY = 6;
1968 protected static final int COL_RES_START = 7;
1969 protected static final int COL_RES_END = 8;
1970 protected static final int COL_RES_STRING = 9;
1971 protected static final int COL_RES_FEATURES = 10;
1972 } // protected class DiffTableModel extends AbstractTableModel
1973
1974 // Local objects
1975 protected AnnotationDiffer differ;
1976 protected List<AnnotationDiffer.Pairing> pairings;
1977 protected List<Boolean> keyCopyValueRows;
1978 protected List<Boolean> resCopyValueRows;
1979 protected List<Resource> documents;
1980 protected Document keyDoc;
1981 protected Document resDoc;
1982 protected List<AnnotationSet> keySets;
1983 protected List<AnnotationSet> resSets;
1984 protected AnnotationSet keySet;
1985 protected AnnotationSet resSet;
1986 protected Set<String> significantFeatures;
1987
1988 // Actions
1989 protected DiffAction diffAction;
1990 protected CopyToTargetSetAction copyToTargetSetAction;
1991 protected HTMLExportAction htmlExportAction;
1992 protected ShowDocumentAction showDocumentAction;
1993
1994 // Top part of the UI from left to right
1995 protected JComboBox keyDocCombo;
1996 protected JComboBox resDocCombo;
1997 protected JComboBox keySetCombo;
1998 protected JComboBox resSetCombo;
1999 protected JComboBox annTypeCombo;
2000 protected DefaultListModel featureslistModel;
2001 protected JList featuresList;
2002 protected JRadioButton allFeaturesBtn;
2003 protected JRadioButton someFeaturesBtn;
2004 protected JRadioButton noFeaturesBtn;
2005 protected JTextField weightTxt;
2006 protected JButton doDiffBtn;
2007
2008 // Center part of the UI
2009 protected JScrollPane scroller;
2010 protected DiffTableModel diffTableModel;
2011 protected XJTable diffTable;
2012
2013 // Bottom part of the UI
2014 protected JTabbedPane bottomTabbedPane;
2015 protected JPanel statisticsPane;
2016 protected JLabel correctLbl;
2017 protected JLabel partiallyCorrectLbl;
2018 protected JLabel missingLbl;
2019 protected JLabel falsePozLbl;
2020 protected JLabel recallStrictLbl;
2021 protected JLabel precisionStrictLbl;
2022 protected JLabel fmeasureStrictLbl;
2023 protected JLabel recallLenientLbl;
2024 protected JLabel precisionLenientLbl;
2025 protected JLabel fmeasureLenientLbl;
2026 protected JLabel recallAveLbl;
2027 protected JLabel precisionAveLbl;
2028 protected JLabel fmeasureAveLbl;
2029 protected JTextField consensusASTextField;
2030 protected JButton copyToConsensusBtn;
2031 protected JLabel statusLabel;
2032 protected JButton htmlExportBtn;
2033 protected JButton showDocumentBtn;
2034 protected JProgressBar progressBar;
2035
2036 // Constants
2037 protected static final Color PARTIALLY_CORRECT_BG = new Color(173,215,255);
2038 protected static final Color MISSING_BG = new Color(255,173,181);
2039 protected static final Color FALSE_POSITIVE_BG = new Color(255,231,173);
2040 protected static final String[] matchLabel;
2041 static{
2042 matchLabel = new String[5];
2043 matchLabel[AnnotationDiffer.CORRECT_TYPE] = "=";
2044 matchLabel[AnnotationDiffer.PARTIALLY_CORRECT_TYPE] = "~";
2045 matchLabel[AnnotationDiffer.MISSING_TYPE] = "-?";
2046 matchLabel[AnnotationDiffer.SPURIOUS_TYPE] = "?-";
2047 matchLabel[AnnotationDiffer.MISMATCH_TYPE] = "<>";
2048 }
2049 /** Maximum number of characters for Key, Response and Features columns. */
2050 protected final int maxCellLength = 40;
2051 /** Is this GUI standalone or embedded in GATE? */
2052 protected boolean isStandalone;
2053 }
|