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