1   /*
2    *  AnnotationDiff.java
3    *
4    *  Copyright (c) 1998-2001, The University of Sheffield.
5    *
6    *  This file is part of GATE (see http://gate.ac.uk/), and is free
7    *  software, licenced under the GNU Library General Public License,
8    *  Version 2, June 1991 (in the distribution as file licence.html,
9    *  and also available at http://gate.ac.uk/gate/licence.html).
10   *
11   *  Cristian URSU, 27/Oct/2000
12   *
13   *  $Id: AnnotationDiff.java,v 1.43 2002/10/21 14:45:01 kalina Exp $
14   */
15  
16  package gate.annotation;
17  
18  import java.util.*;
19  import java.awt.*;
20  import java.text.NumberFormat;
21  import java.awt.event.*;
22  import javax.swing.*;
23  import javax.swing.table.*;
24  
25  import gate.util.*;
26  import gate.annotation.*;
27  import gate.*;
28  import gate.gui.*;
29  import gate.swing.*;
30  import gate.creole.*;
31  import java.beans.*;
32  
33  /**
34    * This class compare two annotation sets on annotation type given by the
35    * AnnotationSchema object. It also deals with graphic representation of the
36    * result.
37    */
38  public class AnnotationDiff extends AbstractVisualResource
39    implements  Scrollable{
40  
41    // number of pixels to be used as increment by scroller
42    protected int maxUnitIncrement = 10;
43  
44    /** Debug flag */
45    private static final boolean DEBUG = false;
46  
47    /** This document contains the key annotation set which is taken as reference
48     *  in comparison*/
49    private Document keyDocument = null;
50  
51    /** The name of the annotation set. If is null then the default annotation set
52      * will be considered.
53      */
54    private String keyAnnotationSetName = null;
55  
56    /** This document contains the response annotation set which is being
57      * compared against the key annotation set.
58      */
59    private Document responseDocument = null;
60  
61    /** The name of the annotation set. If is null then the default annotation set
62      * will be considered.
63      */
64    private String responseAnnotationSetName = null;
65  
66    /** The name of the annotation set considered in calculating FalsePozitive.
67      * If is null then the default annotation set will be considered.
68      */
69    private String responseAnnotationSetNameFalsePoz = null;
70  
71    /** The annotation schema object used to get the annotation name*/
72    private AnnotationSchema annotationSchema = null;
73  
74    /** A set of feature names bellonging to annotations from keyAnnotList
75      * used in isCompatible() and isPartiallyCompatible() methods
76      */
77    private Set keyFeatureNamesSet = null;
78  
79    /** The precision strict value (see NLP Information Extraction)*/
80    private double precisionStrict = 0.0;
81    /** The precision lenient value (see NLP Information Extraction)*/
82    private double precisionLenient = 0.0;
83    /** The precision average value (see NLP Information Extraction)*/
84    private double precisionAverage = 0.0;
85  
86    /** The Recall strict value (see NLP Information Extraction)*/
87    private double recallStrict = 0.0;
88    /** The Recall lenient value (see NLP Information Extraction)*/
89    private double recallLenient = 0.0;
90    /** The Recall average value (see NLP Information Extraction)*/
91    private double recallAverage = 0.0;
92  
93    /** The False positive strict (see NLP Information Extraction)*/
94    private double falsePositiveStrict = 0.0;
95    /** The False positive lenient (see NLP Information Extraction)*/
96    private double falsePositiveLenient = 0.0;
97    /** The False positive average (see NLP Information Extraction)*/
98    private double falsePositiveAverage = 0.0;
99  
100   /** The F-measure strict (see NLP Information Extraction)*/
101   private double fMeasureStrict = 0.0;
102   /** The F-measure lenient (see NLP Information Extraction)*/
103   private double fMeasureLenient = 0.0;
104   /** The F-measure average (see NLP Information Extraction)*/
105   private double fMeasureAverage = 0.0;
106   /** The weight used in F-measure (see NLP Information Extraction)*/
107   public  static double weight = 0.5;
108 
109   /**  This string represents the type of annotations used to play the roll of
110     *  total number of words needed to calculate the False Positive.
111     */
112   private String annotationTypeForFalsePositive = null;
113 
114   /** A number formater for displaying precision and recall*/
115   protected static NumberFormat formatter = NumberFormat.getInstance();
116 
117   /** The components that will stay into diffPanel*/
118   private XJTable diffTable = null;
119 
120   /** Used to represent the result of diff. See DiffSetElement class.*/
121   private Set diffSet = null;
122 
123   /** This field is used in doDiff() and detectKeyType() methods and holds all
124    *  partially correct keys */
125   private Set keyPartiallySet = null;
126   /** This field is used in doDiff() and detectResponseType() methods*/
127   private Set responsePartiallySet = null;
128 
129   /** This list is created from keyAnnotationSet at init() time*/
130   private java.util.List keyAnnotList = null;
131   /** This list is created from responseAnnotationSet at init() time*/
132   private java.util.List responseAnnotList = null;
133 
134   /** This field indicates wheter or not the annot diff should run int the text
135    *  mode*/
136   private boolean textMode = false;
137 
138   /**  Field designated to represent the max nr of annot types and coolors for
139     *  each type
140     **/
141   public static final int MAX_TYPES = 5;
142   /** A default type when all annotation are the same represented by White color*/
143   public static final int DEFAULT_TYPE = 0;
144   /** A correct type when all annotation are corect represented by Green color*/
145   public static final int CORRECT_TYPE = 1;
146   /** A partially correct type when all annotation are corect represented
147    *  by Blue color*/
148   public static final int PARTIALLY_CORRECT_TYPE = 2;
149   /** A spurious type when annotations in response were not present in key.
150    *  Represented by Red color*/
151   public static final int SPURIOUS_TYPE = 3;
152   /** A missing type when annotations in key were not present in response
153    *  Represented by Yellow color*/
154   public static final int MISSING_TYPE = 4;
155 
156   /** Red used for SPURIOUS_TYPE*/
157   private  final Color RED = new Color(255,173,181);
158   /** Green used for CORRECT_TYPE*/
159   private  final Color GREEN = new Color(173,255,214);
160   /** White used for DEFAULT_TYPE*/
161   private  final Color WHITE = new Color(255,255,255);
162   /** Blue used for PARTIALLY_CORRECT_TYPE*/
163   private  final Color BLUE = new Color(173,215,255);
164   /** Yellow used for MISSING_TYPE*/
165   private  final Color YELLOW = new Color(255,231,173);
166 
167   /** Used in DiffSetElement to represent an empty raw in the table*/
168   private final int NULL_TYPE = -1;
169   /** Used in some setForeground() methods*/
170   private  final Color BLACK = new Color(0,0,0);
171   /** The array holding the colours according to the annotation types*/
172   private Color colors[] = new Color[MAX_TYPES];
173 
174   /** A scroll for the AnnotDiff's table*/
175   private JScrollPane scrollPane = null;
176 
177   /** Used to store the no. of annotations from response,identified as belonging
178     * to one of the previous types.
179     */
180   private long typeCounter[] = new long[MAX_TYPES];
181 
182   /** Constructs a AnnotationDif*/
183   public AnnotationDiff(){
184   } //AnnotationDiff
185 
186   /** Sets the annotation type needed to calculate the falsePossitive measure
187     * @param anAnnotType is the annotation type needed to calculate a special
188     *  mesure called falsePossitive. Usualy the value is "token", but it can be
189     *  any other string with the same semantic.
190     */
191   public void setAnnotationTypeForFalsePositive(String anAnnotType){
192     annotationTypeForFalsePositive = anAnnotType;
193   } // setAnnotationTypeForFalsePositive
194 
195   /** Gets the annotation type needed to calculate the falsePossitive measure
196     * @return annotation type needed to calculate a special
197     * mesure called falsePossitive.
198     */
199   public String getAnnotationTypeForFalsePositive(){
200     return annotationTypeForFalsePositive;
201   } // getAnnotationTypeForFalsePositive
202 
203   /** Sets the keyDocument in AnnotDiff
204     * @param aKeyDocument The GATE document used as a key in annotation diff.
205     */
206   public void setKeyDocument(Document aKeyDocument) {
207     keyDocument = aKeyDocument;
208   } // setKeyDocument
209 
210   /** @return the keyDocument used in AnnotDiff process */
211   public Document getKeyDocument(){
212     return keyDocument;
213   } // getKeyDocument
214 
215   /** Sets the keyAnnotationSetName in AnnotDiff
216     * @param aKeyAnnotationSetName The name of the annotation set from the
217     * keyDocument.If aKeyAnnotationSetName is null then the default annotation
218     * set will be used.
219     */
220   public void setKeyAnnotationSetName(String aKeyAnnotationSetName){
221     keyAnnotationSetName = aKeyAnnotationSetName;
222   } // setKeyAnnotationSetName();
223 
224   /** Gets the keyAnnotationSetName.
225     * @return The name of the keyAnnotationSet used in AnnotationDiff. If
226     * returns null then the the default annotation set will be used.
227     */
228   public String getKeyAnnotationSetName(){
229     return keyAnnotationSetName;
230   } // getKeyAnnotationSetName()
231 
232   /** Sets the keyFeatureNamesSet in AnnotDiff.
233     * @param aKeyFeatureNamesSet a set containing the feature names from key
234     * that will be used in isPartiallyCompatible()
235     */
236   public void setKeyFeatureNamesSet(Set aKeyFeatureNamesSet){
237     keyFeatureNamesSet = aKeyFeatureNamesSet;
238   }//setKeyFeatureNamesSet();
239 
240   /** Gets the keyFeatureNamesSet in AnnotDiff.
241     * @return A set containing the feature names from key
242     * that will be used in isPartiallyCompatible()
243     */
244   public Set getKeyFeatureNamesSet(){
245     return keyFeatureNamesSet;
246   }//getKeyFeatureNamesSet();
247 
248   /** Sets the responseAnnotationSetName in AnnotDiff
249     * @param aResponseAnnotationSetName The name of the annotation set from the
250     * responseDocument.If aResponseAnnotationSetName is null then the default
251     * annotation set will be used.
252     */
253   public void setResponseAnnotationSetName(String aResponseAnnotationSetName){
254     responseAnnotationSetName = aResponseAnnotationSetName;
255   } // setResponseAnnotationSetName();
256 
257   /** gets the responseAnnotationSetName.
258     * @return The name of the responseAnnotationSet used in AnnotationDiff. If
259     * returns null then the the default annotation set will be used.
260     */
261   public String getResponseAnnotationSetName(){
262     return responseAnnotationSetName;
263   } // getResponseAnnotationSetName()
264 
265   /** Sets the responseAnnotationSetNameFalsePoz in AnnotDiff
266     * @param aResponseAnnotationSetNameFalsePoz The name of the annotation set
267     * from the responseDocument.If aResponseAnnotationSetName is null
268     * then the default annotation set will be used.
269     */
270   public void setResponseAnnotationSetNameFalsePoz(
271                                     String aResponseAnnotationSetNameFalsePoz){
272     responseAnnotationSetNameFalsePoz = aResponseAnnotationSetNameFalsePoz;
273   } // setResponseAnnotationSetNameFalsePoz();
274 
275   /** gets the responseAnnotationSetNameFalsePoz.
276     * @return The name of the responseAnnotationSetFalsePoz used in
277     * AnnotationDiff. If returns null then the the default annotation
278     * set will be used.
279     */
280   public String getResponseAnnotationSetNameFalsePoz(){
281     return responseAnnotationSetNameFalsePoz;
282   } // getResponseAnnotationSetNamefalsePoz()
283 
284   /**  Sets the annot diff to work in the text mode.This would not initiate the
285     *  GUI part of annot diff but it would calculate precision etc
286     */
287   public void setTextMode(Boolean aTextMode){
288     //it needs to be a Boolean and not boolean, because you cannot put
289     //in the parameters hashmap a boolean, it needs an object
290     textMode = aTextMode.booleanValue();
291   }// End setTextMode();
292 
293   /** Gets the annot diff textmode.True means that the text mode is activated.*/
294   public boolean isTextMode(){
295     return textMode;
296   }// End setTextMode();
297 
298   /** Returns a set with all annotations of a specific type*/
299   public Set getAnnotationsOfType(int annotType){
300     HashSet results = new HashSet();
301     if (diffSet == null) return results;
302     Iterator diffIter = diffSet.iterator();
303     while(diffIter.hasNext()){
304       DiffSetElement diffElem = (DiffSetElement)diffIter.next();
305       switch(annotType){
306         case CORRECT_TYPE:{
307           if (diffElem.getRightType() == CORRECT_TYPE)
308             results.add(diffElem.getRightAnnotation());
309         }break;
310         case PARTIALLY_CORRECT_TYPE:{
311           if (diffElem.getRightType() == PARTIALLY_CORRECT_TYPE)
312             results.add(diffElem.getRightAnnotation());
313         }break;
314         case SPURIOUS_TYPE:{
315           if (diffElem.getRightType() == SPURIOUS_TYPE)
316             results.add(diffElem.getRightAnnotation());
317         }break;
318         case MISSING_TYPE:{
319           if (diffElem.getLeftType() == MISSING_TYPE)
320             results.add(diffElem.getLeftAnnotation());
321         }break;
322         case DEFAULT_TYPE:{
323           if (diffElem.getLeftType() == DEFAULT_TYPE)
324             results.add(diffElem.getLeftAnnotation());
325         }break;
326       }// End switch
327     }// End while
328     return results;
329   }//getAnnotationsOfType
330 
331   //Prameters utility methods
332   /**
333    * Gets the value of a parameter of this resource.
334    * @param paramaterName the name of the parameter
335    * @return the current value of the parameter
336    */
337   public Object getParameterValue(String paramaterName)
338                 throws ResourceInstantiationException{
339     return AbstractResource.getParameterValue(this, paramaterName);
340   }
341 
342   /**
343    * Sets the value for a specified parameter.
344    *
345    * @param paramaterName the name for the parameteer
346    * @param parameterValue the value the parameter will receive
347    */
348   public void setParameterValue(String paramaterName, Object parameterValue)
349               throws ResourceInstantiationException{
350     // get the beaninfo for the resource bean, excluding data about Object
351     BeanInfo resBeanInf = null;
352     try {
353       resBeanInf = Introspector.getBeanInfo(this.getClass(), Object.class);
354     } catch(Exception e) {
355       throw new ResourceInstantiationException(
356         "Couldn't get bean info for resource " + this.getClass().getName()
357         + Strings.getNl() + "Introspector exception was: " + e
358       );
359     }
360     AbstractResource.setParameterValue(this, resBeanInf, paramaterName, parameterValue);
361   }
362 
363   /**
364    * Sets the values for more parameters in one step.
365    *
366    * @param parameters a feature map that has paramete names as keys and
367    * parameter values as values.
368    */
369   public void setParameterValues(FeatureMap parameters)
370               throws ResourceInstantiationException{
371     AbstractResource.setParameterValues(this, parameters);
372   }
373 
374 
375 
376   ///////////////////////////////////////////////////
377   // PRECISION methods
378   ///////////////////////////////////////////////////
379 
380   /** @return the precisionStrict field*/
381   public double getPrecisionStrict(){
382     return precisionStrict;
383   } // getPrecisionStrict
384 
385   /** @return the precisionLenient field*/
386   public double getPrecisionLenient(){
387     return precisionLenient;
388   } // getPrecisionLenient
389 
390   /** @return the precisionAverage field*/
391   public double getPrecisionAverage(){
392     return precisionAverage;
393   } // getPrecisionAverage
394 
395   /** @return the fMeasureStrict field*/
396   public double getFMeasureStrict(){
397     return fMeasureStrict;
398   } // getFMeasureStrict
399 
400   /** @return the fMeasureLenient field*/
401   public double getFMeasureLenient(){
402     return fMeasureLenient;
403   } // getFMeasureLenient
404 
405   /** @return the fMeasureAverage field*/
406   public double getFMeasureAverage(){
407     return fMeasureAverage;
408   } // getFMeasureAverage
409 
410   ///////////////////////////////////////////////////
411   // RECALL methods
412   ///////////////////////////////////////////////////
413 
414   /** @return the recallStrict field*/
415   public double getRecallStrict(){
416     return recallStrict;
417   } // getRecallStrict
418 
419   /** @return the recallLenient field*/
420   public double getRecallLenient(){
421     return recallLenient;
422   } // getRecallLenient
423 
424   /** @return the recallAverage field*/
425   public double getRecallAverage(){
426     return recallAverage;
427   } // getRecallAverage
428 
429   ///////////////////////////////////////////////////
430   // Missing, spurious, etc methods
431   ///////////////////////////////////////////////////
432 
433   public long getCorrectCount() {
434     return typeCounter[CORRECT_TYPE];
435   }
436 
437   public long getPartiallyCorrectCount() {
438     return typeCounter[PARTIALLY_CORRECT_TYPE];
439   }
440 
441   public long getSpuriousCount() {
442     return typeCounter[SPURIOUS_TYPE];
443   }
444 
445   public long getMissingCount() {
446     return typeCounter[MISSING_TYPE];
447   }
448 
449   ///////////////////////////////////////////////////
450   // FALSE POSITIVE methods
451   ///////////////////////////////////////////////////
452 
453   /** @return the falsePositiveStrict field*/
454   public double getFalsePositiveStrict(){
455     return falsePositiveStrict;
456   } // getFalsePositiveStrict
457 
458   /** @return the falsePositiveLenient field*/
459   public double getFalsePositiveLenient(){
460     return falsePositiveLenient;
461   } // getFalsePositiveLenient
462 
463   /** @return the falsePositiveAverage field*/
464   public double getFalsePositiveAverage(){
465     return falsePositiveAverage;
466   } // getFalsePositive
467 
468   /**
469     * @param aResponseDocument the GATE response Document
470     * containing the annotation Set being compared against the annotation from
471     * the keyDocument.
472     */
473   public void setResponseDocument(Document aResponseDocument) {
474     responseDocument = aResponseDocument;
475   } //setResponseDocument
476 
477   /**
478     * @param anAnnotationSchema the annotation type being compared.
479     * This type is found in annotationSchema object as field
480     * {@link gate.creole.AnnotationSchema#getAnnotationName()}. If is <b>null<b>
481     * then AnnotDiff will throw an exception when it comes to do the diff.
482     */
483   public void setAnnotationSchema(AnnotationSchema anAnnotationSchema) {
484     annotationSchema = anAnnotationSchema;
485   } // setAnnotationType
486 
487   /** @return the annotation schema object used in annotation diff process */
488   public AnnotationSchema getAnnotationSchema(){
489     return annotationSchema;
490   } // AnnotationSchema
491 
492   public Dimension getPreferredScrollableViewportSize() {
493         return getPreferredSize();
494   }// public Dimension getPreferredScrollableViewportSize()
495 
496   public int getScrollableUnitIncrement(Rectangle visibleRect,
497                                               int orientation, int direction) {
498     return maxUnitIncrement;
499   }// public int getScrollableUnitIncrement
500 
501   public int getScrollableBlockIncrement(Rectangle visibleRect,
502                                               int orientation, int direction) {
503     if (orientation == SwingConstants.HORIZONTAL)
504         return visibleRect.width - maxUnitIncrement;
505     else
506         return visibleRect.height - maxUnitIncrement;
507   }// public int getScrollableBlockIncrement
508 
509   public boolean getScrollableTracksViewportWidth() {
510     return false;
511   }// public boolean getScrollableTracksViewportWidth()
512 
513   public boolean getScrollableTracksViewportHeight() {
514     return false;
515   }
516 
517   /**
518     * This method does the diff, Precision,Recall,FalsePositive
519     * calculation and so on.
520     */
521   public Resource init() throws ResourceInstantiationException {
522     colors[DEFAULT_TYPE] = WHITE;
523     colors[CORRECT_TYPE] = GREEN;
524     colors[SPURIOUS_TYPE] = RED;
525     colors[PARTIALLY_CORRECT_TYPE] = BLUE;
526     colors[MISSING_TYPE] = YELLOW;
527 
528     // Initialize the partially sets...
529     keyPartiallySet = new HashSet();
530     responsePartiallySet = new HashSet();
531 
532     // Do the diff, P&R calculation and so on
533     AnnotationSet keyAnnotSet = null;
534     AnnotationSet responseAnnotSet = null;
535 
536     if(annotationSchema == null)
537      throw new ResourceInstantiationException("No annotation schema defined !");
538 
539     if(keyDocument == null)
540       throw new ResourceInstantiationException("No key document defined !");
541 
542     if(responseDocument == null)
543       throw new ResourceInstantiationException("No response document defined !");
544 
545     if (keyAnnotationSetName == null)
546       // Get the default key AnnotationSet from the keyDocument
547       keyAnnotSet = keyDocument.getAnnotations().get(
548                               annotationSchema.getAnnotationName());
549     else
550       keyAnnotSet = keyDocument.getAnnotations(keyAnnotationSetName).
551                                     get(annotationSchema.getAnnotationName());
552 
553     if (keyAnnotSet == null)
554       // The diff will run with an empty set.All annotations from response
555       // would be spurious.
556       keyAnnotList = new LinkedList();
557     else
558       // The alghoritm will modify this annotation set. It is better to make a
559       // separate copy of them.
560       keyAnnotList = new LinkedList(keyAnnotSet);
561 
562     if (responseAnnotationSetName == null)
563       // Get the response AnnotationSet from the default set
564       responseAnnotSet = responseDocument.getAnnotations().get(
565                                           annotationSchema.getAnnotationName());
566     else
567       responseAnnotSet = responseDocument.getAnnotations(responseAnnotationSetName).
568                                     get(annotationSchema.getAnnotationName());
569 
570     if (responseAnnotSet == null)
571       // The diff will run with an empty set.All annotations from key
572       // would be missing.
573       responseAnnotList = new LinkedList();
574     else
575       // The alghoritm will modify this annotation set. It is better to make a
576       // separate copy of them.
577       responseAnnotList = new LinkedList(responseAnnotSet);
578 
579     // Sort them ascending on Start offset (the comparator does that)
580     AnnotationSetComparator asComparator = new AnnotationSetComparator();
581     Collections.sort(keyAnnotList, asComparator);
582     Collections.sort(responseAnnotList, asComparator);
583 
584     for (int type=0; type < MAX_TYPES; type++)
585       typeCounter[type] = 0;
586 
587     // Calculate the diff Set. This set will be used later with graphic
588     // visualisation.
589     doDiff(keyAnnotList, responseAnnotList);
590 
591     // If it runs under text mode just stop here.
592     if (textMode) return this;
593 
594     //Show it
595     // Configuring the formatter object. It will be used later to format
596     // precision and recall
597     formatter.setMaximumIntegerDigits(1);
598     formatter.setMinimumFractionDigits(4);
599     formatter.setMinimumFractionDigits(4);
600 
601     // Create an Annotation diff table model
602     AnnotationDiffTableModel diffModel = new AnnotationDiffTableModel(diffSet);
603     // Create a XJTable based on this model
604     diffTable = new XJTable(diffModel);
605     diffTable.setAlignmentX(Component.LEFT_ALIGNMENT);
606     // Set the cell renderer for this table.
607     AnnotationDiffCellRenderer cellRenderer = new AnnotationDiffCellRenderer();
608     diffTable.setDefaultRenderer(java.lang.String.class,cellRenderer);
609     diffTable.setDefaultRenderer(java.lang.Long.class,cellRenderer);
610     // Put the table into a JScroll
611 
612     // Arange all components on a this JPanel
613     SwingUtilities.invokeLater(new Runnable(){
614       public void run(){
615         arangeAllComponents();
616       }
617     });
618 
619     if (DEBUG)
620       printStructure(diffSet);
621 
622     return this;
623   } //init()
624 
625   /** This method creates the graphic components and aranges them on
626     * <b>this</b> JPanel
627     */
628   protected void arangeAllComponents(){
629     this.removeAll();
630     // Setting the box layout for diffpanel
631     BoxLayout boxLayout = new BoxLayout(this,BoxLayout.Y_AXIS);
632     this.setLayout(boxLayout);
633 
634     JTableHeader tableHeader = diffTable.getTableHeader();
635     tableHeader.setAlignmentX(Component.LEFT_ALIGNMENT);
636     this.add(tableHeader);
637     diffTable.setAlignmentX(Component.LEFT_ALIGNMENT);
638     // Add the tableScroll to the diffPanel
639     this.add(diffTable);
640 
641 
642     // ADD the LEGEND
643     //Lay out the JLabels from left to right.
644     //Box infoBox = new Box(BoxLayout.X_AXIS);
645     JPanel infoBox = new  JPanel();
646     infoBox.setLayout(new BoxLayout(infoBox,BoxLayout.X_AXIS));
647     infoBox.setAlignmentX(Component.LEFT_ALIGNMENT);
648     // Keep the components together
649     //box.add(Box.createHorizontalGlue());
650 
651     Box box = new Box(BoxLayout.Y_AXIS);
652     JLabel jLabel = new JLabel("LEGEND");
653     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
654     jLabel.setOpaque(true);
655     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
656     box.add(jLabel);
657 
658     jLabel = new JLabel("Missing (present in Key but not in Response):  " +
659                                                 typeCounter[MISSING_TYPE]);
660     jLabel.setForeground(BLACK);
661     jLabel.setBackground(colors[MISSING_TYPE]);
662     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
663     jLabel.setOpaque(true);
664     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
665     box.add(jLabel);
666 
667     // Add a space
668     box.add(Box.createRigidArea(new Dimension(0,5)));
669 
670     jLabel = new JLabel("Correct (total match):  " + typeCounter[CORRECT_TYPE]);
671     jLabel.setForeground(BLACK);
672     jLabel.setBackground(colors[CORRECT_TYPE]);
673     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
674     jLabel.setOpaque(true);
675     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
676     box.add(jLabel);
677 
678     // Add a space
679     box.add(Box.createRigidArea(new Dimension(0,5)));
680 
681     jLabel =new JLabel("Partially correct (overlap in Key and Response):  "+
682                                         typeCounter[PARTIALLY_CORRECT_TYPE]);
683     jLabel.setForeground(BLACK);
684     jLabel.setBackground(colors[PARTIALLY_CORRECT_TYPE]);
685     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
686     jLabel.setOpaque(true);
687     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
688     box.add(jLabel);
689 
690     // Add a space
691     box.add(Box.createRigidArea(new Dimension(0,5)));
692 
693     jLabel = new JLabel("Spurious (present in Response but not in Key):  " +
694                                         typeCounter[SPURIOUS_TYPE]);
695     jLabel.setForeground(BLACK);
696     jLabel.setBackground(colors[SPURIOUS_TYPE]);
697     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
698     jLabel.setOpaque(true);
699     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
700     box.add(jLabel);
701 
702     infoBox.add(box);
703     // Add a space
704     infoBox.add(Box.createRigidArea(new Dimension(40,0)));
705 
706     // Precision measure
707     //Lay out the JLabels from left to right.
708     box = new Box(BoxLayout.Y_AXIS);
709 
710     jLabel = new JLabel("Precision strict: " +
711                                     formatter.format(precisionStrict));
712     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
713     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
714     box.add(jLabel);
715 
716     jLabel = new JLabel("Precision average: " +
717                                     formatter.format(precisionAverage));
718     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
719     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
720     box.add(jLabel);
721 
722     jLabel = new JLabel("Precision lenient: " +
723                                     formatter.format(precisionLenient));
724     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
725     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
726     box.add(jLabel);
727 
728     infoBox.add(box);
729     // Add a space
730     infoBox.add(Box.createRigidArea(new Dimension(40,0)));
731 
732     // RECALL measure
733     //Lay out the JLabels from left to right.
734     box = new Box(BoxLayout.Y_AXIS);
735 
736     jLabel = new JLabel("Recall strict: " + formatter.format(recallStrict));
737     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
738     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
739     box.add(jLabel);
740 
741     jLabel = new JLabel("Recall average: " + formatter.format(recallAverage));
742     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
743     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
744     box.add(jLabel);
745 
746     jLabel = new JLabel("Recall lenient: " + formatter.format(recallLenient));
747     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
748     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
749     box.add(jLabel);
750 
751     infoBox.add(box);
752     // Add a space
753     infoBox.add(Box.createRigidArea(new Dimension(40,0)));
754 
755     // F-Measure
756     //Lay out the JLabels from left to right.
757     box = new Box(BoxLayout.Y_AXIS);
758 
759     jLabel = new JLabel("F-Measure strict: " +
760                                         formatter.format(fMeasureStrict));
761     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
762     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
763     box.add(jLabel);
764 
765     jLabel = new JLabel("F-Measure average: " +
766                                         formatter.format(fMeasureAverage));
767     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
768     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
769     box.add(jLabel);
770 
771     jLabel = new JLabel("F-Measure lenient: " +
772                                         formatter.format(fMeasureLenient));
773     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
774     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
775     box.add(jLabel);
776     infoBox.add(box);
777 
778     // Add a space
779     infoBox.add(Box.createRigidArea(new Dimension(40,0)));
780 
781     // FALSE POZITIVE measure
782     //Lay out the JLabels from left to right.
783     box = new Box(BoxLayout.Y_AXIS);
784 
785     jLabel = new JLabel("False positive strict: " +
786                                         formatter.format(falsePositiveStrict));
787     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
788     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
789     box.add(jLabel);
790 
791     jLabel = new JLabel("False positive average: " +
792                                         formatter.format(falsePositiveAverage));
793     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
794     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
795     box.add(jLabel);
796 
797     jLabel = new JLabel("False positive lenient: " +
798                                         formatter.format(falsePositiveLenient));
799     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
800     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
801     box.add(jLabel);
802     infoBox.add(box);
803 
804     // Add a space
805     infoBox.add(Box.createRigidArea(new Dimension(10,0)));
806 
807     this.add(infoBox);
808   } //arangeAllComponents
809 
810   /** Used internally for debugging */
811   protected void printStructure(Set aDiffSet){
812     Iterator iterator = aDiffSet.iterator();
813     String leftAnnot = null;
814     String rightAnnot = null;
815     while(iterator.hasNext()){
816       DiffSetElement diffElem = (DiffSetElement) iterator.next();
817       if (diffElem.getLeftAnnotation() == null)
818         leftAnnot = "NULL ";
819       else
820         leftAnnot = diffElem.getLeftAnnotation().toString();
821       if (diffElem.getRightAnnotation() == null)
822         rightAnnot = " NULL";
823       else
824         rightAnnot = diffElem.getRightAnnotation().toString();
825       Out.prln( leftAnnot + "|" + rightAnnot);
826     } // end while
827   } // printStructure
828 
829   /** This method is the brain of the AnnotationSet diff and creates a set with
830     * diffSetElement objects.
831     * @param aKeyAnnotList a list containing the annotations from key. If this
832     * param is <b>null</b> then the method will simply return and will not do a
833     * thing.
834     * @param aResponseAnnotList a list containing the annotation from response.
835     * If this param is <b>null</b> the method will return.
836     */
837   protected void doDiff(java.util.List aKeyAnnotList,
838                         java.util.List aResponseAnnotList){
839 
840     // If one of the annotation sets is null then is no point in doing the diff.
841     if (aKeyAnnotList == null || aResponseAnnotList == null)
842       return;
843 
844     int responseSize = aResponseAnnotList.size();
845 
846     diffSet = new HashSet();
847     // Iterate throught all elements from keyList and find those in the response
848     // list which satisfies isCompatible() and isPartiallyCompatible() relations
849     Iterator keyIterator = aKeyAnnotList.iterator();
850     while(keyIterator.hasNext()){
851       Annotation keyAnnot = (Annotation) keyIterator.next();
852       Iterator responseIterator = aResponseAnnotList.iterator();
853 
854       DiffSetElement diffElement = null;
855       while(responseIterator.hasNext()){
856         Annotation responseAnnot = (Annotation) responseIterator.next();
857 
858         if(keyAnnot.isPartiallyCompatible(responseAnnot,keyFeatureNamesSet)){
859           keyPartiallySet.add(keyAnnot);
860           responsePartiallySet.add(responseAnnot);
861           if (keyAnnot.coextensive(responseAnnot)){
862             // Found two compatible annotations
863             // Create a new DiffSetElement and add it to the diffSet
864             diffElement = new DiffSetElement( keyAnnot,
865                                               responseAnnot,
866                                               DEFAULT_TYPE,
867                                               CORRECT_TYPE);
868 
869             // Add this element to the DiffSet
870             addToDiffset(diffElement);
871           } // End if (keyAnnot.coextensive(responseAnnot))
872         }else if (keyAnnot.coextensive(responseAnnot)){
873           // Found two aligned annotations. We have to find out if the response
874           // is partialy compatible with another key annotation.
875           // Create a new DiffSetElement and add it to the diffSet
876           diffElement = new DiffSetElement( keyAnnot,
877                                             responseAnnot,
878                                             detectKeyType(keyAnnot),
879                                             detectResponseType(responseAnnot));
880           // Add this element to the DiffSet
881           addToDiffset(diffElement);
882         } // End if (keyAnnot.coextensive(responseAnnot)){
883 
884         if (diffElement != null){
885           // Eliminate the response annotation from the list.
886           responseIterator.remove();
887           break;
888         } // End if
889       } // end while responseIterator
890 
891       // If diffElement != null it means that break was used
892       if (diffElement == null){
893         if (keyPartiallySet.contains(keyAnnot))
894           diffElement = new DiffSetElement( keyAnnot,
895                                             null,
896                                             DEFAULT_TYPE,
897                                             NULL_TYPE);
898         else{
899           // If keyAnnot is not in keyPartiallySet then it has to be checked
900           // agains all annotations in DiffSet to see if there is
901           // a previous annotation from response set which is partially
902           // compatible with the keyAnnot
903           Iterator respParIter = diffSet.iterator();
904           while (respParIter.hasNext()){
905             DiffSetElement diffElem = (DiffSetElement) respParIter.next();
906             Annotation respAnnot = diffElem.getRightAnnotation();
907             if (respAnnot != null && keyAnnot.isPartiallyCompatible(respAnnot,
908                                                           keyFeatureNamesSet)){
909                 diffElement = new DiffSetElement( keyAnnot,
910                                                   null,
911                                                   DEFAULT_TYPE,
912                                                   NULL_TYPE);
913                 break;
914             } // End if
915           } // End while
916           // If is still nul then it means that the key annotation is missing
917           if (diffElement == null)
918             diffElement = new DiffSetElement( keyAnnot,
919                                               null,
920                                               MISSING_TYPE,
921                                               NULL_TYPE);
922         } // End if
923         addToDiffset(diffElement);
924       } // End if
925 
926       keyIterator.remove();
927     } // end while keyIterator
928 
929     DiffSetElement diffElem = null;
930     Iterator responseIter = aResponseAnnotList.iterator();
931     while (responseIter.hasNext()){
932       Annotation respAnnot = (Annotation) responseIter.next();
933       if (responsePartiallySet.contains(respAnnot))
934         diffElem = new DiffSetElement( null,
935                                        respAnnot,
936                                        NULL_TYPE,
937                                        PARTIALLY_CORRECT_TYPE);
938       else
939         diffElem = new DiffSetElement( null,
940                                        respAnnot,
941                                        NULL_TYPE,
942                                        SPURIOUS_TYPE);
943       addToDiffset(diffElem);
944       responseIter.remove();
945     } // End while
946 
947     // CALCULATE ALL (NLP) MEASURES like:
948     // Precistion, Recall, FalsePositive and F-Measure
949     long possible =  typeCounter[CORRECT_TYPE] +  // this comes from Key or Resp
950                     typeCounter[PARTIALLY_CORRECT_TYPE] + // this comes from Resp
951                     typeCounter[MISSING_TYPE]; // this comes from Key
952 
953     long actual =  typeCounter[CORRECT_TYPE] +  // this comes from Key or Resp
954                   typeCounter[PARTIALLY_CORRECT_TYPE] + // this comes from Resp
955                   typeCounter[SPURIOUS_TYPE]; // this comes from Resp
956 
957     if (actual != responseSize)
958       Err.prln("AnnotDiff warning: The response size(" + responseSize +
959       ") is not the same as the computed value of" +
960     " actual(Correct[resp or key]+Partial[resp]+Spurious[resp]=" + actual +")");
961 
962     if (actual != 0){
963       precisionStrict =  ((double)typeCounter[CORRECT_TYPE])/((double)actual);
964       precisionLenient = ((double)(typeCounter[CORRECT_TYPE] +
965                          typeCounter[PARTIALLY_CORRECT_TYPE]))/((double)actual);
966       precisionAverage = ((double)(precisionStrict + precisionLenient)) /
967                                                                   ((double) 2);
968     } // End if
969     if (possible != 0){
970       recallStrict = ((double)typeCounter[CORRECT_TYPE])/((double)possible);
971       recallLenient = ((double)(typeCounter[CORRECT_TYPE] +
972                        typeCounter[PARTIALLY_CORRECT_TYPE]))/((double)possible);
973       recallAverage = ((double)(recallStrict + recallLenient)) / ((double)2);
974     } // End if
975 
976 
977     int no = 0;
978     // If an annotation type for false poz was selected calculate the number of
979     // Annotations
980     if (annotationTypeForFalsePositive != null)
981      // Was it the default set ?
982      if (responseAnnotationSetNameFalsePoz == null){
983       AnnotationSet aSet = responseDocument.getAnnotations().get(
984                                       annotationTypeForFalsePositive);
985           no = aSet == null ? 0 : aSet.size();
986      }else{
987       AnnotationSet aSet = responseDocument.getAnnotations(responseAnnotationSetNameFalsePoz).get(
988                                         annotationTypeForFalsePositive);
989       no = aSet == null? 0 : aSet.size();
990      }
991     if (no != 0){
992       // No error here: the formula is the opposite to recall or precission
993      falsePositiveStrict = ((double)(typeCounter[SPURIOUS_TYPE] +
994                              typeCounter[PARTIALLY_CORRECT_TYPE])) /((double)no);
995      falsePositiveLenient = ((double)typeCounter[SPURIOUS_TYPE]) /((double) no);
996      falsePositiveAverage = ((double)(falsePositiveStrict +
997                                            falsePositiveLenient))/((double)2) ;
998     } // End if
999 
1000    // Calculate F-Measure Strict
1001    double denominator = weight * (precisionStrict + recallStrict);
1002    if (denominator != 0)
1003      fMeasureStrict = (precisionStrict * recallStrict) / denominator ;
1004    else fMeasureStrict = 0.0;
1005    // Calculate F-Measure Lenient
1006    denominator = weight * (precisionLenient + recallLenient);
1007    if (denominator != 0)
1008      fMeasureLenient = (precisionLenient * recallLenient) / denominator ;
1009    else fMeasureLenient = 0.0;
1010    // Calculate F-Measure Average
1011    fMeasureAverage = (fMeasureStrict + fMeasureLenient) / (double)2;
1012
1013  } // doDiff
1014
1015  /** Decide what type is the keyAnnotation (DEFAULT_TYPE, MISSING or NULL_TYPE)
1016   *  This method must be applied only on annotation from key set.
1017   *  @param anAnnot is an annotation from the key set.
1018   *  @return three possible value(DEFAULT_TYPE, MISSING or NULL_TYPE)
1019   */
1020  private int detectKeyType(Annotation anAnnot){
1021    if (anAnnot == null) return NULL_TYPE;
1022
1023    if (keyPartiallySet.contains(anAnnot)) return DEFAULT_TYPE;
1024    Iterator iter = responsePartiallySet.iterator();
1025    while(iter.hasNext()){
1026      Annotation a = (Annotation) iter.next();
1027      if (anAnnot.isPartiallyCompatible(a,keyFeatureNamesSet))
1028        return DEFAULT_TYPE;
1029    } // End while
1030
1031    iter = responseAnnotList.iterator();
1032    while(iter.hasNext()){
1033      Annotation a = (Annotation) iter.next();
1034      if (anAnnot.isPartiallyCompatible(a,keyFeatureNamesSet)){
1035         responsePartiallySet.add(a);
1036         keyPartiallySet.add(anAnnot);
1037         return DEFAULT_TYPE;
1038      } // End if
1039    } // End while
1040    return MISSING_TYPE;
1041  } //detectKeyType
1042
1043  /**  Decide what type is the responseAnnotation
1044    *  (PARTIALLY_CORRECT_TYPE, SPURIOUS or NULL_TYPE)
1045    *  This method must be applied only on annotation from response set.
1046    *  @param anAnnot is an annotation from the key set.
1047    *  @return three possible value(PARTIALLY_CORRECT_TYPE, SPURIOUS or NULL_TYPE)
1048    */
1049  private int detectResponseType(Annotation anAnnot){
1050    if (anAnnot == null) return NULL_TYPE;
1051
1052    if (responsePartiallySet.contains(anAnnot)) return PARTIALLY_CORRECT_TYPE;
1053    Iterator iter = keyPartiallySet.iterator();
1054    while(iter.hasNext()){
1055      Annotation a = (Annotation) iter.next();
1056      if (a.isPartiallyCompatible(anAnnot,keyFeatureNamesSet))
1057        return PARTIALLY_CORRECT_TYPE;
1058    } // End while
1059
1060    iter = keyAnnotList.iterator();
1061    while(iter.hasNext()){
1062      Annotation a = (Annotation) iter.next();
1063      if (a.isPartiallyCompatible(anAnnot,keyFeatureNamesSet)){
1064         responsePartiallySet.add(anAnnot);
1065         keyPartiallySet.add(a);
1066         return PARTIALLY_CORRECT_TYPE;
1067      } // End if
1068    } // End while
1069    return SPURIOUS_TYPE;
1070  } //detectResponseType
1071
1072  /** This method add an DiffsetElement to the DiffSet and also counts the
1073    * number of compatible, partialCompatible, Incorect and Missing annotations.
1074    */
1075  private void addToDiffset(DiffSetElement aDiffSetElement){
1076    if (aDiffSetElement == null) return;
1077
1078    diffSet.add(aDiffSetElement);
1079    // For the Right side (response) the type can be one of the following:
1080    // PC, I, C
1081    if (NULL_TYPE != aDiffSetElement.getRightType())
1082      typeCounter[aDiffSetElement.getRightType()]++;
1083    // For the left side (key) the type can be : D or M
1084    if (NULL_TYPE != aDiffSetElement.getLeftType() &&
1085        CORRECT_TYPE != aDiffSetElement.getLeftType())
1086      typeCounter[aDiffSetElement.getLeftType()]++;
1087  } // addToDiffset
1088
1089  /* ********************************************************************
1090   * INNER CLASS
1091   * ********************************************************************/
1092
1093  /**
1094    * A custom table model used to render a table containing the two annotation
1095    * sets. The columns will be:
1096    * (KEY) Type, Start, End, Features, empty column,(Response) Type,Start, End, Features
1097    */
1098  protected class AnnotationDiffTableModel extends AbstractTableModel{
1099
1100    /** Constructs an AnnotationDiffTableModel given a data Collection */
1101    public AnnotationDiffTableModel(Collection data){
1102      modelData = new ArrayList();
1103      modelData.addAll(data);
1104    } // AnnotationDiffTableModel
1105
1106    /** Constructs an AnnotationDiffTableModel */
1107    public AnnotationDiffTableModel(){
1108      modelData = new ArrayList();
1109    } // AnnotationDiffTableModel
1110
1111    /** Return the size of data.*/
1112    public int getRowCount(){
1113      return modelData.size();
1114    } //getRowCount
1115
1116    /** Return the number of columns.*/
1117    public int getColumnCount(){
1118      return 9;
1119    } //getColumnCount
1120
1121    /** Returns the name of each column in the model*/
1122    public String getColumnName(int column){
1123      switch(column){
1124        case 0: return "String - Key";
1125        case 1: return "Start - Key";
1126        case 2: return "End - Key";
1127        case 3: return "Features - Key";
1128        case 4: return "   ";
1129        case 5: return "String - Response";
1130        case 6: return "Start - Response";
1131        case 7: return "End -Response";
1132        case 8: return "Features - Response";
1133        default:return "?";
1134      }
1135    } //getColumnName
1136
1137    /** Return the class type for each column. */
1138    public Class getColumnClass(int column){
1139      switch(column){
1140        case 0: return String.class;
1141        case 1: return Long.class;
1142        case 2: return Long.class;
1143        case 3: return String.class;
1144        case 4: return String.class;
1145        case 5: return String.class;
1146        case 6: return Long.class;
1147        case 7: return Long.class;
1148        case 8: return String.class;
1149        default:return Object.class;
1150      }
1151    } //getColumnClass
1152
1153    /**Returns a value from the table model */
1154    public Object getValueAt(int row, int column){
1155      DiffSetElement diffSetElement = (DiffSetElement) modelData.get(row);
1156      if (diffSetElement == null) return null;
1157      switch(column){
1158        // Left Side (Key)
1159        //Type - Key
1160        case 0:{
1161           if (diffSetElement.getLeftAnnotation() == null) return null;
1162//           return diffSetElement.getLeftAnnotation().getType();
1163           Annotation annot = diffSetElement.getLeftAnnotation();
1164           String theString = "";
1165           try {
1166             theString = keyDocument.getContent().getContent(
1167                    annot.getStartNode().getOffset(),
1168                    annot.getEndNode().getOffset()).toString();
1169           } catch (gate.util.InvalidOffsetException ex) {
1170             Err.prln(ex.getMessage());
1171           }
1172           return theString;
1173        }
1174        // Start - Key
1175        case 1:{
1176           if (diffSetElement.getLeftAnnotation() == null) return null;
1177           return diffSetElement.getLeftAnnotation().getStartNode().getOffset();
1178        }
1179        // End - Key
1180        case 2:{
1181           if (diffSetElement.getLeftAnnotation() == null) return null;
1182           return diffSetElement.getLeftAnnotation().getEndNode().getOffset();
1183        }
1184        // Features - Key
1185        case 3:{
1186           if (diffSetElement.getLeftAnnotation() == null) return null;
1187           if (diffSetElement.getLeftAnnotation().getFeatures() == null)
1188             return null;
1189           return diffSetElement.getLeftAnnotation().getFeatures().toString();
1190        }
1191        // Empty column
1192        case 4:{
1193          return "   ";
1194        }
1195        // Right Side (Response)
1196        //Type - Response
1197        case 5:{
1198           if (diffSetElement.getRightAnnotation() == null) return null;
1199//           return diffSetElement.getRightAnnotation().getType();
1200           Annotation annot = diffSetElement.getRightAnnotation();
1201           String theString = "";
1202           try {
1203             theString = responseDocument.getContent().getContent(
1204                    annot.getStartNode().getOffset(),
1205                    annot.getEndNode().getOffset()).toString();
1206           } catch (gate.util.InvalidOffsetException ex) {
1207             Err.prln(ex.getMessage());
1208           }
1209           return theString;
1210        }
1211        // Start - Response
1212        case 6:{
1213           if (diffSetElement.getRightAnnotation() == null) return null;
1214          return diffSetElement.getRightAnnotation().getStartNode().getOffset();
1215        }
1216        // End - Response
1217        case 7:{
1218           if (diffSetElement.getRightAnnotation() == null) return null;
1219           return diffSetElement.getRightAnnotation().getEndNode().getOffset();
1220        }
1221        // Features - resonse
1222        case 8:{
1223           if (diffSetElement.getRightAnnotation() == null) return null;
1224           return diffSetElement.getRightAnnotation().getFeatures().toString();
1225        }
1226        // The hidden column
1227        case 9:{
1228          return diffSetElement;
1229        }
1230        default:{return null;}
1231      } // End switch
1232    } //getValueAt
1233
1234    public Object getRawObject(int row){
1235      return modelData.get(row);
1236    } //getRawObject
1237
1238    /** Holds the data for TableDiff*/
1239    private java.util.List modelData = null;
1240
1241  } //Inner class AnnotationDiffTableModel
1242
1243  /* ********************************************************************
1244   * INNER CLASS
1245   * ********************************************************************/
1246  /**
1247    * This class defines a Cell renderer for the AnnotationDiff table
1248    */
1249  public class AnnotationDiffCellRenderer extends DefaultTableCellRenderer{
1250
1251    /** Constructs a randerer with a table model*/
1252    public AnnotationDiffCellRenderer() { }  //AnnotationDiffCellRenderer
1253
1254    private Color background = WHITE;
1255
1256    private Color foreground = BLACK;
1257
1258    /** This method is called by JTable*/
1259
1260    public Component getTableCellRendererComponent(
1261      JTable table, Object value, boolean isSelected, boolean hasFocus,
1262      int row, int column
1263    ) {
1264      JComponent defaultComp = null;
1265      defaultComp = (JComponent) super.getTableCellRendererComponent(
1266  table, value, isSelected, hasFocus, row, column
1267      );
1268
1269      // The column number four will be randered using a blank component
1270      if (column == 4 || value == null)
1271        return new JPanel();
1272
1273      if (!(table.getModel().getValueAt(row,9) instanceof DiffSetElement))
1274        return defaultComp;
1275
1276      DiffSetElement diffSetElement =
1277                        (DiffSetElement) table.getModel().getValueAt(row,9);
1278
1279      if (diffSetElement == null)
1280        return defaultComp;
1281
1282      if (column < 4){
1283        if (NULL_TYPE != diffSetElement.getLeftType())
1284          background = colors[diffSetElement.getLeftType()];
1285        else return new JPanel();
1286      }else{
1287        if (NULL_TYPE != diffSetElement.getRightType())
1288          background = colors[diffSetElement.getRightType()];
1289        else return new JPanel();
1290      }
1291
1292      defaultComp.setBackground(background);
1293      defaultComp.setForeground(BLACK);
1294
1295      defaultComp.setOpaque(true);
1296      return defaultComp;
1297    } //getTableCellRendererComponent
1298
1299  } // class AnnotationDiffCellRenderer
1300
1301  /* ********************************************************************
1302   * INNER CLASS
1303   * ********************************************************************/
1304   class AnnotationSetComparator implements java.util.Comparator {
1305
1306      public AnnotationSetComparator(){}
1307
1308      public int compare(Object o1, Object o2) {
1309        if ( !(o1 instanceof gate.Annotation) ||
1310             !(o2 instanceof gate.Annotation)) return 0;
1311
1312        gate.Annotation a1 = (gate.Annotation) o1;
1313        gate.Annotation a2 = (gate.Annotation) o2;
1314
1315        Long l1 = a1.getStartNode().getOffset();
1316        Long l2 = a2.getStartNode().getOffset();
1317        if (l1 != null)
1318          return l1.compareTo(l2);
1319        else
1320          return -1;
1321      } //compare
1322    } // class AnnotationSetComparator
1323
1324  /* ********************************************************************
1325   * INNER CLASS
1326   * ********************************************************************/
1327
1328  /**
1329    * This class is used for internal purposes. It represents a row from the
1330    * table.
1331    */
1332  protected class DiffSetElement {
1333    /** This field represent a key annotation*/
1334    private Annotation leftAnnotation = null;
1335    /** This field represent a response annotation*/
1336    private Annotation rightAnnotation = null;
1337    /** Default initialization of the key type*/
1338    private int leftType = DEFAULT_TYPE;
1339    /** Default initialization of the response type*/
1340    private int rightType = DEFAULT_TYPE;
1341
1342    /** Constructor for DiffSetlement*/
1343    public DiffSetElement() {}
1344
1345    /** Constructor for DiffSetlement*/
1346    public DiffSetElement( Annotation aLeftAnnotation,
1347                           Annotation aRightAnnotation,
1348                           int aLeftType,
1349                           int aRightType){
1350      leftAnnotation = aLeftAnnotation;
1351      rightAnnotation = aRightAnnotation;
1352      leftType = aLeftType;
1353      rightType = aRightType;
1354    } // DiffSetElement
1355
1356    /** Sets the left annotation*/
1357    public void setLeftAnnotation(Annotation aLeftAnnotation){
1358      leftAnnotation = aLeftAnnotation;
1359    } // setLeftAnnot
1360
1361    /** Gets the left annotation*/
1362    public Annotation getLeftAnnotation(){
1363      return leftAnnotation;
1364    } // getLeftAnnotation
1365
1366    /** Sets the right annotation*/
1367    public void setRightAnnotation(Annotation aRightAnnotation){
1368      rightAnnotation = aRightAnnotation;
1369    } // setRightAnnot
1370
1371    /** Gets the right annotation*/
1372    public Annotation getRightAnnotation(){
1373      return rightAnnotation;
1374    } // getRightAnnotation
1375
1376    /** Sets the left type*/
1377    public void setLeftType(int aLeftType){
1378      leftType = aLeftType;
1379    } // setLeftType
1380
1381    /** Get the left type */
1382    public int getLeftType() {
1383      return leftType;
1384    } // getLeftType
1385
1386    /** Sets the right type*/
1387    public void setRightType(int aRightType) {
1388      rightType = aRightType;
1389    } // setRightType
1390
1391    /** Get the right type*/
1392    public int getRightType() {
1393      return rightType;
1394    } // getRightType
1395  } // classs DiffSetElement
1396} // class AnnotationDiff
1397