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