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