1   /*
2    *  AnnotationDiff.java
3    *
4    *  Copyright (c) 1998-2005, The University of Sheffield.
5    *
6    *  This file is part of GATE (see http://gate.ac.uk/), and is free
7    *  software, licenced under the GNU Library General Public License,
8    *  Version 2, June 1991 (in the distribution as file licence.html,
9    *  and also available at http://gate.ac.uk/gate/licence.html).
10   *
11   *  Cristian URSU, 27/Oct/2000
12   *
13   *  $Id: AnnotationDiff.java,v 1.53 2005/07/13 10:39:16 valyt Exp $
14   */
15  
16  package gate.annotation;
17  
18  import java.awt.*;
19  import java.text.NumberFormat;
20  import java.util.*;
21  
22  import javax.swing.*;
23  import javax.swing.table.*;
24  
25  import gate.*;
26  import gate.creole.*;
27  import gate.swing.XJTable;
28  import gate.util.*;
29  
30  /**
31    * This class compare two annotation sets on annotation type given by the
32    * AnnotationSchema object. It also deals with graphic representation of the
33    * result.
34    * @deprecated This functionality is provided by the 
35    * {@link gate.util.AnnotationDiffer} class as of GATE version 3.0.
36    */
37  public class AnnotationDiff extends AbstractVisualResource
38    implements  Scrollable{
39  
40    // number of pixels to be used as increment by scroller
41    protected int maxUnitIncrement = 10;
42  
43    /** Debug flag */
44    private static final boolean DEBUG = false;
45  
46    /** This document contains the key annotation set which is taken as reference
47     *  in comparison*/
48    private Document keyDocument = null;
49  
50    /** The name of the annotation set. If is null then the default annotation set
51      * will be considered.
52      */
53    private String keyAnnotationSetName = null;
54  
55    /** This document contains the response annotation set which is being
56      * compared against the key annotation set.
57      */
58    private Document responseDocument = null;
59  
60    /** The name of the annotation set. If is null then the default annotation set
61      * will be considered.
62      */
63    private String responseAnnotationSetName = null;
64  
65    /** The name of the annotation set considered in calculating FalsePozitive.
66      * If is null then the default annotation set will be considered.
67      */
68    private String responseAnnotationSetNameFalsePoz = null;
69  
70    /** The annotation schema object used to get the annotation name*/
71    private AnnotationSchema annotationSchema = null;
72  
73    /** A set of feature names bellonging to annotations from keyAnnotList
74      * used in isCompatible() and isPartiallyCompatible() methods
75      */
76    private Set keyFeatureNamesSet = null;
77  
78    /** The precision strict value (see NLP Information Extraction)*/
79    private double precisionStrict = 0.0;
80    /** The precision lenient value (see NLP Information Extraction)*/
81    private double precisionLenient = 0.0;
82    /** The precision average value (see NLP Information Extraction)*/
83    private double precisionAverage = 0.0;
84  
85    /** The Recall strict value (see NLP Information Extraction)*/
86    private double recallStrict = 0.0;
87    /** The Recall lenient value (see NLP Information Extraction)*/
88    private double recallLenient = 0.0;
89    /** The Recall average value (see NLP Information Extraction)*/
90    private double recallAverage = 0.0;
91  
92    /** The False positive strict (see NLP Information Extraction)*/
93    private double falsePositiveStrict = 0.0;
94    /** The False positive lenient (see NLP Information Extraction)*/
95    private double falsePositiveLenient = 0.0;
96    /** The False positive average (see NLP Information Extraction)*/
97    private double falsePositiveAverage = 0.0;
98  
99    /** The F-measure strict (see NLP Information Extraction)*/
100   private double fMeasureStrict = 0.0;
101   /** The F-measure lenient (see NLP Information Extraction)*/
102   private double fMeasureLenient = 0.0;
103   /** The F-measure average (see NLP Information Extraction)*/
104   private double fMeasureAverage = 0.0;
105   /** The weight used in F-measure (see NLP Information Extraction)*/
106   public  static double weight = 0.5;
107 
108   /**  This string represents the type of annotations used to play the roll of
109     *  total number of words needed to calculate the False Positive.
110     */
111   private String annotationTypeForFalsePositive = null;
112 
113   /** A number formater for displaying precision and recall*/
114   protected static NumberFormat formatter = NumberFormat.getInstance();
115 
116   /** The components that will stay into diffPanel*/
117   private XJTable diffTable = null;
118 
119   /** Used to represent the result of diff. See DiffSetElement class.*/
120   private Set diffSet = null;
121 
122   /** This field is used in doDiff() and detectKeyType() methods and holds all
123    *  partially correct keys */
124   private Set keyPartiallySet = null;
125   /** This field is used in doDiff() and detectResponseType() methods*/
126   private Set responsePartiallySet = null;
127 
128   /** This list is created from keyAnnotationSet at init() time*/
129   private java.util.List keyAnnotList = null;
130   /** This list is created from responseAnnotationSet at init() time*/
131   private java.util.List responseAnnotList = null;
132 
133   /** This field indicates wheter or not the annot diff should run int the text
134    *  mode*/
135   private boolean textMode = false;
136 
137   /**  Field designated to represent the max nr of annot types and coolors for
138     *  each type
139     **/
140   public static final int MAX_TYPES = 5;
141   /** A default type when all annotation are the same represented by White color*/
142   public static final int DEFAULT_TYPE = 0;
143   /** A correct type when all annotation are corect represented by Green color*/
144   public static final int CORRECT_TYPE = 1;
145   /** A partially correct type when all annotation are corect represented
146    *  by Blue color*/
147   public static final int PARTIALLY_CORRECT_TYPE = 2;
148   /** A spurious type when annotations in response were not present in key.
149    *  Represented by Red color*/
150   public static final int SPURIOUS_TYPE = 3;
151   /** A missing type when annotations in key were not present in response
152    *  Represented by Yellow color*/
153   public static final int MISSING_TYPE = 4;
154 
155   /** Red used for SPURIOUS_TYPE*/
156   private  final Color RED = new Color(255,173,181);
157   /** Green used for CORRECT_TYPE*/
158   private  final Color GREEN = new Color(173,255,214);
159   /** White used for DEFAULT_TYPE*/
160   private  final Color WHITE = new Color(255,255,255);
161   /** Blue used for PARTIALLY_CORRECT_TYPE*/
162   private  final Color BLUE = new Color(173,215,255);
163   /** Yellow used for MISSING_TYPE*/
164   private  final Color YELLOW = new Color(255,231,173);
165 
166   /** Used in DiffSetElement to represent an empty raw in the table*/
167   private final int NULL_TYPE = -1;
168   /** Used in some setForeground() methods*/
169   private  final Color BLACK = new Color(0,0,0);
170   /** The array holding the colours according to the annotation types*/
171   private Color colors[] = new Color[MAX_TYPES];
172 
173   /** A scroll for the AnnotDiff's table*/
174   private JScrollPane scrollPane = null;
175 
176   /** Used to store the no. of annotations from response,identified as belonging
177     * to one of the previous types.
178     */
179   private long typeCounter[] = new long[MAX_TYPES];
180 
181 
182   private gate.util.AnnotationDiffer annotDiffer;
183 
184   /** Constructs a AnnotationDif*/
185   public AnnotationDiff(){
186     annotDiffer = new AnnotationDiffer();
187   } //AnnotationDiff
188 
189   /** Sets the annotation type needed to calculate the falsePossitive measure
190     * @param anAnnotType is the annotation type needed to calculate a special
191     *  mesure called falsePossitive. Usualy the value is "token", but it can be
192     *  any other string with the same semantic.
193     */
194   public void setAnnotationTypeForFalsePositive(String anAnnotType){
195     annotationTypeForFalsePositive = anAnnotType;
196   } // setAnnotationTypeForFalsePositive
197 
198   /** Gets the annotation type needed to calculate the falsePossitive measure
199     * @return annotation type needed to calculate a special
200     * mesure called falsePossitive.
201     */
202   public String getAnnotationTypeForFalsePositive(){
203     return annotationTypeForFalsePositive;
204   } // getAnnotationTypeForFalsePositive
205 
206   /** Sets the keyDocument in AnnotDiff
207     * @param aKeyDocument The GATE document used as a key in annotation diff.
208     */
209   public void setKeyDocument(Document aKeyDocument) {
210     keyDocument = aKeyDocument;
211   } // setKeyDocument
212 
213   /** @return the keyDocument used in AnnotDiff process */
214   public Document getKeyDocument(){
215     return keyDocument;
216   } // getKeyDocument
217 
218   /** Sets the keyAnnotationSetName in AnnotDiff
219     * @param aKeyAnnotationSetName The name of the annotation set from the
220     * keyDocument.If aKeyAnnotationSetName is null then the default annotation
221     * set will be used.
222     */
223   public void setKeyAnnotationSetName(String aKeyAnnotationSetName){
224     keyAnnotationSetName = aKeyAnnotationSetName;
225   } // setKeyAnnotationSetName();
226 
227   /** Gets the keyAnnotationSetName.
228     * @return The name of the keyAnnotationSet used in AnnotationDiff. If
229     * returns null then the the default annotation set will be used.
230     */
231   public String getKeyAnnotationSetName(){
232     return keyAnnotationSetName;
233   } // getKeyAnnotationSetName()
234 
235   /** Sets the keyFeatureNamesSet in AnnotDiff.
236     * @param aKeyFeatureNamesSet a set containing the feature names from key
237     * that will be used in isPartiallyCompatible()
238     */
239   public void setKeyFeatureNamesSet(Set aKeyFeatureNamesSet){
240     keyFeatureNamesSet = aKeyFeatureNamesSet;
241   }//setKeyFeatureNamesSet();
242 
243   /** Gets the keyFeatureNamesSet in AnnotDiff.
244     * @return A set containing the feature names from key
245     * that will be used in isPartiallyCompatible()
246     */
247   public Set getKeyFeatureNamesSet(){
248     return keyFeatureNamesSet;
249   }//getKeyFeatureNamesSet();
250 
251   /** Sets the responseAnnotationSetName in AnnotDiff
252     * @param aResponseAnnotationSetName The name of the annotation set from the
253     * responseDocument.If aResponseAnnotationSetName is null then the default
254     * annotation set will be used.
255     */
256   public void setResponseAnnotationSetName(String aResponseAnnotationSetName){
257     responseAnnotationSetName = aResponseAnnotationSetName;
258   } // setResponseAnnotationSetName();
259 
260   /** gets the responseAnnotationSetName.
261     * @return The name of the responseAnnotationSet used in AnnotationDiff. If
262     * returns null then the the default annotation set will be used.
263     */
264   public String getResponseAnnotationSetName(){
265     return responseAnnotationSetName;
266   } // getResponseAnnotationSetName()
267 
268   /** Sets the responseAnnotationSetNameFalsePoz in AnnotDiff
269     * @param aResponseAnnotationSetNameFalsePoz The name of the annotation set
270     * from the responseDocument.If aResponseAnnotationSetName is null
271     * then the default annotation set will be used.
272     */
273   public void setResponseAnnotationSetNameFalsePoz(
274                                     String aResponseAnnotationSetNameFalsePoz){
275     responseAnnotationSetNameFalsePoz = aResponseAnnotationSetNameFalsePoz;
276   } // setResponseAnnotationSetNameFalsePoz();
277 
278   /** gets the responseAnnotationSetNameFalsePoz.
279     * @return The name of the responseAnnotationSetFalsePoz used in
280     * AnnotationDiff. If returns null then the the default annotation
281     * set will be used.
282     */
283   public String getResponseAnnotationSetNameFalsePoz(){
284     return responseAnnotationSetNameFalsePoz;
285   } // getResponseAnnotationSetNamefalsePoz()
286 
287   /**  Sets the annot diff to work in the text mode.This would not initiate the
288     *  GUI part of annot diff but it would calculate precision etc
289     */
290   public void setTextMode(Boolean aTextMode){
291     //it needs to be a Boolean and not boolean, because you cannot put
292     //in the parameters hashmap a boolean, it needs an object
293     textMode = aTextMode.booleanValue();
294   }// End setTextMode();
295 
296   /** Gets the annot diff textmode.True means that the text mode is activated.*/
297   public boolean isTextMode(){
298     return textMode;
299   }// End setTextMode();
300 
301   /** Returns a set with all annotations of a specific type*/
302   public Set getAnnotationsOfType(int annotType){
303     return annotDiffer.getAnnotationsOfType(annotType);
304   }//getAnnotationsOfType
305 
306 
307   ///////////////////////////////////////////////////
308   // PRECISION methods
309   ///////////////////////////////////////////////////
310 
311   /** @return the precisionStrict field*/
312   public double getPrecisionStrict(){
313     return annotDiffer.getPrecisionStrict();
314   } // getPrecisionStrict
315 
316   /** @return the precisionLenient field*/
317   public double getPrecisionLenient(){
318     return annotDiffer.getPrecisionLenient();
319   } // getPrecisionLenient
320 
321   /** @return the precisionAverage field*/
322   public double getPrecisionAverage(){
323     return annotDiffer.getPrecisionAverage();
324   } // getPrecisionAverage
325 
326   /** @return the fMeasureStrict field*/
327   public double getFMeasureStrict(){
328     return annotDiffer.getFMeasureStrict(1);
329   } // getFMeasureStrict
330 
331   /** @return the fMeasureLenient field*/
332   public double getFMeasureLenient(){
333     return annotDiffer.getFMeasureLenient(1);
334   } // getFMeasureLenient
335 
336   /** @return the fMeasureAverage field*/
337   public double getFMeasureAverage(){
338     return annotDiffer.getFMeasureAverage(1);
339   } // getFMeasureAverage
340 
341   ///////////////////////////////////////////////////
342   // RECALL methods
343   ///////////////////////////////////////////////////
344 
345   /** @return the recallStrict field*/
346   public double getRecallStrict(){
347     return annotDiffer.getRecallStrict();
348   } // getRecallStrict
349 
350   /** @return the recallLenient field*/
351   public double getRecallLenient(){
352     return annotDiffer.getRecallLenient();
353   } // getRecallLenient
354 
355   /** @return the recallAverage field*/
356   public double getRecallAverage(){
357     return annotDiffer.getRecallAverage();
358   } // getRecallAverage
359 
360   ///////////////////////////////////////////////////
361   // Missing, spurious, etc methods
362   ///////////////////////////////////////////////////
363 
364   public long getCorrectCount() {
365     return annotDiffer.getCorrectMatches();
366   }
367 
368   public long getPartiallyCorrectCount() {
369     return annotDiffer.getPartiallyCorrectMatches();
370   }
371 
372   public long getSpuriousCount() {
373     return annotDiffer.getSpurious();
374   }
375 
376   public long getMissingCount() {
377     return annotDiffer.getMissing();
378   }
379 
380   ///////////////////////////////////////////////////
381   // FALSE POSITIVE methods
382   ///////////////////////////////////////////////////
383 
384   /** @return the falsePositiveStrict field*/
385   public double getFalsePositiveStrict(){
386     return annotDiffer.getFalsePositivesStrict();
387   } // getFalsePositiveStrict
388 
389   /** @return the falsePositiveLenient field*/
390   public double getFalsePositiveLenient(){
391     return annotDiffer.getFalsePositivesLenient();
392   } // getFalsePositiveLenient
393 
394   /** @return the falsePositiveAverage field*/
395   public double getFalsePositiveAverage(){
396     return (double)(((double)getFalsePositiveLenient() + getFalsePositiveStrict()) / (double)(2.0));
397   } // getFalsePositive
398 
399   /**
400     * @param aResponseDocument the GATE response Document
401     * containing the annotation Set being compared against the annotation from
402     * the keyDocument.
403     */
404   public void setResponseDocument(Document aResponseDocument) {
405     responseDocument = aResponseDocument;
406   } //setResponseDocument
407 
408   /**
409     * @param anAnnotationSchema the annotation type being compared.
410     * This type is found in annotationSchema object as field
411     * {@link gate.creole.AnnotationSchema#getAnnotationName()}. If is <b>null<b>
412     * then AnnotDiff will throw an exception when it comes to do the diff.
413     */
414   public void setAnnotationSchema(AnnotationSchema anAnnotationSchema) {
415     annotationSchema = anAnnotationSchema;
416   } // setAnnotationType
417 
418   /** @return the annotation schema object used in annotation diff process */
419   public AnnotationSchema getAnnotationSchema(){
420     return annotationSchema;
421   } // AnnotationSchema
422 
423   public Dimension getPreferredScrollableViewportSize() {
424         return getPreferredSize();
425   }// public Dimension getPreferredScrollableViewportSize()
426 
427   public int getScrollableUnitIncrement(Rectangle visibleRect,
428                                               int orientation, int direction) {
429     return maxUnitIncrement;
430   }// public int getScrollableUnitIncrement
431 
432   public int getScrollableBlockIncrement(Rectangle visibleRect,
433                                               int orientation, int direction) {
434     if (orientation == SwingConstants.HORIZONTAL)
435         return visibleRect.width - maxUnitIncrement;
436     else
437         return visibleRect.height - maxUnitIncrement;
438   }// public int getScrollableBlockIncrement
439 
440   public boolean getScrollableTracksViewportWidth() {
441     return false;
442   }// public boolean getScrollableTracksViewportWidth()
443 
444   public boolean getScrollableTracksViewportHeight() {
445     return false;
446   }
447 
448   /**
449     * This method does the diff, Precision,Recall,FalsePositive
450     * calculation and so on.
451     */
452   public Resource init() throws ResourceInstantiationException {
453     colors[DEFAULT_TYPE] = WHITE;
454     colors[CORRECT_TYPE] = GREEN;
455     colors[SPURIOUS_TYPE] = RED;
456     colors[PARTIALLY_CORRECT_TYPE] = BLUE;
457     colors[MISSING_TYPE] = YELLOW;
458 
459     // Initialize the partially sets...
460     keyPartiallySet = new HashSet();
461     responsePartiallySet = new HashSet();
462 
463     // Do the diff, P&R calculation and so on
464     AnnotationSet keyAnnotSet = null;
465     AnnotationSet responseAnnotSet = null;
466 
467     if(annotationSchema == null)
468      throw new ResourceInstantiationException("No annotation schema defined !");
469 
470     if(keyDocument == null)
471       throw new ResourceInstantiationException("No key document defined !");
472 
473     if(responseDocument == null)
474       throw new ResourceInstantiationException("No response document defined !");
475 
476     if (keyAnnotationSetName == null)
477       // Get the default key AnnotationSet from the keyDocument
478       keyAnnotSet = keyDocument.getAnnotations().get(
479                               annotationSchema.getAnnotationName());
480     else
481       keyAnnotSet = keyDocument.getAnnotations(keyAnnotationSetName).
482                                     get(annotationSchema.getAnnotationName());
483 
484 
485     if (responseAnnotationSetName == null)
486       // Get the response AnnotationSet from the default set
487       responseAnnotSet = responseDocument.getAnnotations().get(
488                                           annotationSchema.getAnnotationName());
489     else
490       responseAnnotSet = responseDocument.getAnnotations(responseAnnotationSetName).get(annotationSchema.getAnnotationName());
491 
492 
493 
494     // Calculate the diff Set. This set will be used later with graphic
495     // visualisation.
496     annotDiffer.setSignificantFeaturesSet(getKeyFeatureNamesSet());
497     ArrayList choices = (ArrayList) annotDiffer.calculateDiff(keyAnnotSet, responseAnnotSet);
498     diffSet = new HashSet();
499     for(int i=0;i<choices.size();i++) {
500       AnnotationDiffer.PairingImpl choice = (AnnotationDiffer.PairingImpl) choices.get(i);
501       int type = choice.getType();
502       int leftType = 0;
503       int rightType = 0;
504       if(type == AnnotationDiffer.CORRECT) {
505         leftType = CORRECT_TYPE;
506         rightType = CORRECT_TYPE;
507       } else if(type == AnnotationDiffer.PARTIALLY_CORRECT) {
508         leftType = PARTIALLY_CORRECT_TYPE;
509         rightType = PARTIALLY_CORRECT_TYPE;
510       } else {
511         if(choice.getKey() == null) { leftType = SPURIOUS_TYPE; rightType = SPURIOUS_TYPE; }
512         else { leftType = MISSING_TYPE; rightType = MISSING_TYPE; }
513       }
514       diffSet.add(new DiffSetElement(choice.getKey(),choice.getResponse(), leftType, rightType));
515     }
516 
517     // If it runs under text mode just stop here.
518     if (textMode) return this;
519 
520     //Show it
521     // Configuring the formatter object. It will be used later to format
522     // precision and recall
523     formatter.setMaximumIntegerDigits(1);
524     formatter.setMinimumFractionDigits(4);
525     formatter.setMinimumFractionDigits(4);
526 
527     // Create an Annotation diff table model
528     AnnotationDiffTableModel diffModel = new AnnotationDiffTableModel(diffSet);
529     // Create a XJTable based on this model
530     diffTable = new XJTable(diffModel);
531     diffTable.setAlignmentX(Component.LEFT_ALIGNMENT);
532     // Set the cell renderer for this table.
533     AnnotationDiffCellRenderer cellRenderer = new AnnotationDiffCellRenderer();
534     diffTable.setDefaultRenderer(java.lang.String.class,cellRenderer);
535     diffTable.setDefaultRenderer(java.lang.Long.class,cellRenderer);
536     // Put the table into a JScroll
537 
538     // Arange all components on a this JPanel
539     SwingUtilities.invokeLater(new Runnable(){
540       public void run(){
541         arangeAllComponents();
542       }
543     });
544 
545     if (DEBUG)
546       printStructure(diffSet);
547 
548     return this;
549   } //init()
550 
551   /** This method creates the graphic components and aranges them on
552     * <b>this</b> JPanel
553     */
554   protected void arangeAllComponents(){
555     this.removeAll();
556     // Setting the box layout for diffpanel
557     BoxLayout boxLayout = new BoxLayout(this,BoxLayout.Y_AXIS);
558     this.setLayout(boxLayout);
559 
560     JTableHeader tableHeader = diffTable.getTableHeader();
561     tableHeader.setAlignmentX(Component.LEFT_ALIGNMENT);
562     this.add(tableHeader);
563     diffTable.setAlignmentX(Component.LEFT_ALIGNMENT);
564     // Add the tableScroll to the diffPanel
565     this.add(diffTable);
566 
567 
568     // ADD the LEGEND
569     //Lay out the JLabels from left to right.
570     //Box infoBox = new Box(BoxLayout.X_AXIS);
571     JPanel infoBox = new  JPanel();
572     infoBox.setLayout(new BoxLayout(infoBox,BoxLayout.X_AXIS));
573     infoBox.setAlignmentX(Component.LEFT_ALIGNMENT);
574     // Keep the components together
575     //box.add(Box.createHorizontalGlue());
576 
577     Box box = new Box(BoxLayout.Y_AXIS);
578     JLabel jLabel = new JLabel("LEGEND");
579     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
580     jLabel.setOpaque(true);
581     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
582     box.add(jLabel);
583 
584     jLabel = new JLabel("Missing (present in Key but not in Response):  " +
585                                                 annotDiffer.getMissing());
586     jLabel.setForeground(BLACK);
587     jLabel.setBackground(colors[MISSING_TYPE]);
588     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
589     jLabel.setOpaque(true);
590     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
591     box.add(jLabel);
592 
593     // Add a space
594     box.add(Box.createRigidArea(new Dimension(0,5)));
595 
596     jLabel = new JLabel("Correct (total match):  " + annotDiffer.getCorrectMatches());
597     jLabel.setForeground(BLACK);
598     jLabel.setBackground(colors[CORRECT_TYPE]);
599     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
600     jLabel.setOpaque(true);
601     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
602     box.add(jLabel);
603 
604     // Add a space
605     box.add(Box.createRigidArea(new Dimension(0,5)));
606 
607     jLabel =new JLabel("Partially correct (overlap in Key and Response):  "+
608                                         annotDiffer.getPartiallyCorrectMatches());
609     jLabel.setForeground(BLACK);
610     jLabel.setBackground(colors[PARTIALLY_CORRECT_TYPE]);
611     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
612     jLabel.setOpaque(true);
613     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
614     box.add(jLabel);
615 
616     // Add a space
617     box.add(Box.createRigidArea(new Dimension(0,5)));
618 
619     jLabel = new JLabel("Spurious (present in Response but not in Key):  " +
620                                         annotDiffer.getSpurious());
621     jLabel.setForeground(BLACK);
622     jLabel.setBackground(colors[SPURIOUS_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     infoBox.add(box);
629     // Add a space
630     infoBox.add(Box.createRigidArea(new Dimension(40,0)));
631 
632     // Precision measure
633     //Lay out the JLabels from left to right.
634     box = new Box(BoxLayout.Y_AXIS);
635 
636     jLabel = new JLabel("Precision strict: " +
637                                     formatter.format(getPrecisionStrict()));
638     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
639     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
640     box.add(jLabel);
641 
642     jLabel = new JLabel("Precision average: " +
643                                     formatter.format(getPrecisionAverage()));
644     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
645     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
646     box.add(jLabel);
647 
648     jLabel = new JLabel("Precision lenient: " +
649                                     formatter.format(getPrecisionLenient()));
650     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
651     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
652     box.add(jLabel);
653 
654     infoBox.add(box);
655     // Add a space
656     infoBox.add(Box.createRigidArea(new Dimension(40,0)));
657 
658     // RECALL measure
659     //Lay out the JLabels from left to right.
660     box = new Box(BoxLayout.Y_AXIS);
661 
662     jLabel = new JLabel("Recall strict: " + formatter.format(getRecallStrict()));
663     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
664     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
665     box.add(jLabel);
666 
667     jLabel = new JLabel("Recall average: " + formatter.format(getRecallAverage()));
668     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
669     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
670     box.add(jLabel);
671 
672     jLabel = new JLabel("Recall lenient: " + formatter.format(getRecallLenient()));
673     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
674     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
675     box.add(jLabel);
676 
677     infoBox.add(box);
678     // Add a space
679     infoBox.add(Box.createRigidArea(new Dimension(40,0)));
680 
681     // F-Measure
682     //Lay out the JLabels from left to right.
683     box = new Box(BoxLayout.Y_AXIS);
684 
685     jLabel = new JLabel("F-Measure strict: " +
686                                         formatter.format(getFMeasureStrict()));
687     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
688     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
689     box.add(jLabel);
690 
691     jLabel = new JLabel("F-Measure average: " +
692                                         formatter.format(getFMeasureAverage()));
693     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
694     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
695     box.add(jLabel);
696 
697     jLabel = new JLabel("F-Measure lenient: " +
698                                         formatter.format(getFMeasureLenient()));
699     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
700     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
701     box.add(jLabel);
702     infoBox.add(box);
703 
704     // Add a space
705     infoBox.add(Box.createRigidArea(new Dimension(40,0)));
706 
707     // FALSE POZITIVE measure
708     //Lay out the JLabels from left to right.
709     box = new Box(BoxLayout.Y_AXIS);
710 
711     jLabel = new JLabel("False positive strict: " +
712                                         formatter.format(getFalsePositiveStrict()));
713     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
714     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
715     box.add(jLabel);
716 
717     jLabel = new JLabel("False positive average: " +
718                                         formatter.format(getFalsePositiveAverage()));
719     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
720     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
721     box.add(jLabel);
722 
723     jLabel = new JLabel("False positive lenient: " +
724                                         formatter.format(getFalsePositiveLenient()));
725     jLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
726     jLabel.setFont(jLabel.getFont().deriveFont(Font.BOLD));
727     box.add(jLabel);
728     infoBox.add(box);
729 
730     // Add a space
731     infoBox.add(Box.createRigidArea(new Dimension(10,0)));
732 
733     this.add(infoBox);
734   } //arangeAllComponents
735 
736   /** Used internally for debugging */
737   protected void printStructure(Set aDiffSet){
738     Iterator iterator = aDiffSet.iterator();
739     String leftAnnot = null;
740     String rightAnnot = null;
741     while(iterator.hasNext()){
742       DiffSetElement diffElem = (DiffSetElement) iterator.next();
743       if (diffElem.getLeftAnnotation() == null)
744         leftAnnot = "NULL ";
745       else
746         leftAnnot = diffElem.getLeftAnnotation().toString();
747       if (diffElem.getRightAnnotation() == null)
748         rightAnnot = " NULL";
749       else
750         rightAnnot = diffElem.getRightAnnotation().toString();
751       Out.prln( leftAnnot + "|" + rightAnnot);
752     } // end while
753   } // printStructure
754 
755 
756   /* ********************************************************************
757    * INNER CLASS
758    * ********************************************************************/
759 
760   /**
761     * A custom table model used to render a table containing the two annotation
762     * sets. The columns will be:
763     * (KEY) Type, Start, End, Features, empty column,(Response) Type,Start, End, Features
764     */
765   protected class AnnotationDiffTableModel extends AbstractTableModel{
766 
767     /** Constructs an AnnotationDiffTableModel given a data Collection */
768     public AnnotationDiffTableModel(Collection data){
769       modelData = new ArrayList();
770       modelData.addAll(data);
771     } // AnnotationDiffTableModel
772 
773     /** Constructs an AnnotationDiffTableModel */
774     public AnnotationDiffTableModel(){
775       modelData = new ArrayList();
776     } // AnnotationDiffTableModel
777 
778     /** Return the size of data.*/
779     public int getRowCount(){
780       return modelData.size();
781     } //getRowCount
782 
783     /** Return the number of columns.*/
784     public int getColumnCount(){
785       return 9;
786     } //getColumnCount
787 
788     /** Returns the name of each column in the model*/
789     public String getColumnName(int column){
790       switch(column){
791         case 0: return "String - Key";
792         case 1: return "Start - Key";
793         case 2: return "End - Key";
794         case 3: return "Features - Key";
795         case 4: return "   ";
796         case 5: return "String - Response";
797         case 6: return "Start - Response";
798         case 7: return "End -Response";
799         case 8: return "Features - Response";
800         default:return "?";
801       }
802     } //getColumnName
803 
804     /** Return the class type for each column. */
805     public Class getColumnClass(int column){
806       switch(column){
807         case 0: return String.class;
808         case 1: return Long.class;
809         case 2: return Long.class;
810         case 3: return String.class;
811         case 4: return String.class;
812         case 5: return String.class;
813         case 6: return Long.class;
814         case 7: return Long.class;
815         case 8: return String.class;
816         default:return Object.class;
817       }
818     } //getColumnClass
819 
820     /**Returns a value from the table model */
821     public Object getValueAt(int row, int column){
822       DiffSetElement diffSetElement = (DiffSetElement) modelData.get(row);
823       if (diffSetElement == null) return null;
824       switch(column){
825         // Left Side (Key)
826         //Type - Key
827         case 0:{
828            if (diffSetElement.getLeftAnnotation() == null) return null;
829            Annotation annot = diffSetElement.getLeftAnnotation();
830            String theString = "";
831            try {
832              theString = keyDocument.getContent().getContent(
833                     annot.getStartNode().getOffset(),
834                     annot.getEndNode().getOffset()).toString();
835            } catch (gate.util.InvalidOffsetException ex) {
836              Err.prln(ex.getMessage());
837            }
838            return theString;
839         }
840         // Start - Key
841         case 1:{
842            if (diffSetElement.getLeftAnnotation() == null) return null;
843            return diffSetElement.getLeftAnnotation().getStartNode().getOffset();
844         }
845         // End - Key
846         case 2:{
847            if (diffSetElement.getLeftAnnotation() == null) return null;
848            return diffSetElement.getLeftAnnotation().getEndNode().getOffset();
849         }
850         // Features - Key
851         case 3:{
852            if (diffSetElement.getLeftAnnotation() == null) return null;
853            if (diffSetElement.getLeftAnnotation().getFeatures() == null)
854              return null;
855            return diffSetElement.getLeftAnnotation().getFeatures().toString();
856         }
857         // Empty column
858         case 4:{
859           return "   ";
860         }
861         // Right Side (Response)
862         //Type - Response
863         case 5:{
864            if (diffSetElement.getRightAnnotation() == null) return null;
865            Annotation annot = diffSetElement.getRightAnnotation();
866            String theString = "";
867            try {
868              theString = responseDocument.getContent().getContent(
869                     annot.getStartNode().getOffset(),
870                     annot.getEndNode().getOffset()).toString();
871            } catch (gate.util.InvalidOffsetException ex) {
872              Err.prln(ex.getMessage());
873            }
874            return theString;
875         }
876         // Start - Response
877         case 6:{
878            if (diffSetElement.getRightAnnotation() == null) return null;
879           return diffSetElement.getRightAnnotation().getStartNode().getOffset();
880         }
881         // End - Response
882         case 7:{
883            if (diffSetElement.getRightAnnotation() == null) return null;
884            return diffSetElement.getRightAnnotation().getEndNode().getOffset();
885         }
886         // Features - resonse
887         case 8:{
888            if (diffSetElement.getRightAnnotation() == null) return null;
889            return diffSetElement.getRightAnnotation().getFeatures().toString();
890         }
891         // The hidden column
892         case 9:{
893           return diffSetElement;
894         }
895         default:{return null;}
896       } // End switch
897     } //getValueAt
898 
899     public Object getRawObject(int row){
900       return modelData.get(row);
901     } //getRawObject
902 
903     /** Holds the data for TableDiff*/
904     private java.util.List modelData = null;
905 
906   } //Inner class AnnotationDiffTableModel
907 
908 
909   /* ********************************************************************
910    * INNER CLASS
911    * ********************************************************************/
912   /**
913     * This class defines a Cell renderer for the AnnotationDiff table
914     */
915   public class AnnotationDiffCellRenderer extends DefaultTableCellRenderer{
916 
917     /** Constructs a randerer with a table model*/
918     public AnnotationDiffCellRenderer() { }  //AnnotationDiffCellRenderer
919 
920     private Color background = WHITE;
921 
922     private Color foreground = BLACK;
923 
924     /** This method is called by JTable*/
925 
926     public Component getTableCellRendererComponent(
927       JTable table, Object value, boolean isSelected, boolean hasFocus,
928       int row, int column
929     ) {
930       JComponent defaultComp = null;
931       defaultComp = (JComponent) super.getTableCellRendererComponent(
932   table, value, isSelected, hasFocus, row, column
933       );
934 
935       // The column number four will be randered using a blank component
936       if (column == 4 || value == null)
937         return new JPanel();
938 
939       if (!(table.getModel().getValueAt(row,9) instanceof DiffSetElement))
940         return defaultComp;
941 
942       DiffSetElement diffSetElement =
943                         (DiffSetElement) table.getModel().getValueAt(row,9);
944 
945       if (diffSetElement == null)
946         return defaultComp;
947 
948       if (column < 4){
949         if (diffSetElement.getLeftAnnotation() != null) {
950           background = colors[diffSetElement.getLeftType()];
951         }
952         else return new JPanel();
953       }else{
954         if (diffSetElement.getRightAnnotation() != null)
955           background = colors[diffSetElement.getRightType()];
956         else return new JPanel();
957       }
958 
959       defaultComp.setBackground(background);
960       defaultComp.setForeground(BLACK);
961       defaultComp.setOpaque(true);
962       return defaultComp;
963     } //getTableCellRendererComponent
964 
965   } // class AnnotationDiffCellRenderer
966 
967 
968   /* ********************************************************************
969    * INNER CLASS
970    * ********************************************************************/
971 
972   /**
973     * This class is used for internal purposes. It represents a row from the
974     * table.
975     */
976   protected class DiffSetElement {
977     /** This field represent a key annotation*/
978     private Annotation leftAnnotation = null;
979     /** This field represent a response annotation*/
980     private Annotation rightAnnotation = null;
981     /** Default initialization of the key type*/
982     private int leftType = DEFAULT_TYPE;
983     /** Default initialization of the response type*/
984     private int rightType = DEFAULT_TYPE;
985 
986     /** Constructor for DiffSetlement*/
987     public DiffSetElement() {}
988 
989     /** Constructor for DiffSetlement*/
990     public DiffSetElement( Annotation aLeftAnnotation,
991                            Annotation aRightAnnotation,
992                            int aLeftType,
993                            int aRightType){
994       leftAnnotation = aLeftAnnotation;
995       rightAnnotation = aRightAnnotation;
996       leftType = aLeftType;
997       rightType = aRightType;
998     } // DiffSetElement
999 
1000    /** Sets the left annotation*/
1001    public void setLeftAnnotation(Annotation aLeftAnnotation){
1002      leftAnnotation = aLeftAnnotation;
1003    } // setLeftAnnot
1004
1005    /** Gets the left annotation*/
1006    public Annotation getLeftAnnotation(){
1007      return leftAnnotation;
1008    } // getLeftAnnotation
1009
1010    /** Sets the right annotation*/
1011    public void setRightAnnotation(Annotation aRightAnnotation){
1012      rightAnnotation = aRightAnnotation;
1013    } // setRightAnnot
1014
1015    /** Gets the right annotation*/
1016    public Annotation getRightAnnotation(){
1017      return rightAnnotation;
1018    } // getRightAnnotation
1019
1020    /** Sets the left type*/
1021    public void setLeftType(int aLeftType){
1022      leftType = aLeftType;
1023    } // setLeftType
1024
1025    /** Get the left type */
1026    public int getLeftType() {
1027      return leftType;
1028    } // getLeftType
1029
1030    /** Sets the right type*/
1031    public void setRightType(int aRightType) {
1032      rightType = aRightType;
1033    } // setRightType
1034
1035    /** Get the right type*/
1036    public int getRightType() {
1037      return rightType;
1038    } // getRightType
1039  } // classs DiffSetElement
1040} // class AnnotationDiff
1041