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