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