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